diff --git a/Cargo.lock b/Cargo.lock index ab72ff23..b2a00ec1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -902,7 +902,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "avn-lower-rpc" -version = "6.2.0" +version = "6.3.0" dependencies = [ "avn-service", "futures", @@ -924,7 +924,7 @@ dependencies = [ [[package]] name = "avn-node-parachain" -version = "6.2.0" +version = "6.3.0" dependencies = [ "avn-lower-rpc", "avn-parachain-runtime", @@ -1008,7 +1008,7 @@ dependencies = [ [[package]] name = "avn-parachain-runtime" -version = "6.2.0" +version = "6.3.0" dependencies = [ "avn-runtime-common", "cumulus-pallet-aura-ext", @@ -1090,7 +1090,7 @@ dependencies = [ [[package]] name = "avn-parachain-test-runtime" -version = "6.2.0" +version = "6.3.0" dependencies = [ "avn-runtime-common", "cumulus-pallet-aura-ext", @@ -1172,7 +1172,7 @@ dependencies = [ [[package]] name = "avn-runtime-common" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-support", "hex-literal", @@ -1186,7 +1186,7 @@ dependencies = [ [[package]] name = "avn-service" -version = "6.2.0" +version = "6.3.0" dependencies = [ "anyhow", "ethereum-types 0.11.0", @@ -7158,7 +7158,7 @@ dependencies = [ [[package]] name = "pallet-avn" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -7186,7 +7186,7 @@ dependencies = [ [[package]] name = "pallet-avn-anchor" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -7195,6 +7195,11 @@ dependencies = [ "pallet-avn", "pallet-avn-proxy", "pallet-balances", + "pallet-eth-bridge", + "pallet-scheduler", + "pallet-session", + "pallet-timestamp", + "pallet-token-manager", "parity-scale-codec 3.6.4", "parking_lot 0.12.1", "scale-info", @@ -7209,7 +7214,7 @@ dependencies = [ [[package]] name = "pallet-avn-offence-handler" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -7231,7 +7236,7 @@ dependencies = [ [[package]] name = "pallet-avn-proxy" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -7260,7 +7265,7 @@ dependencies = [ [[package]] name = "pallet-avn-transaction-payment" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -7551,7 +7556,7 @@ dependencies = [ [[package]] name = "pallet-eth-bridge" -version = "6.2.0" +version = "6.3.0" dependencies = [ "ethabi 13.0.0", "frame-benchmarking", @@ -7580,7 +7585,7 @@ dependencies = [ [[package]] name = "pallet-eth-bridge-runtime-api" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-support", "pallet-avn", @@ -7594,7 +7599,7 @@ dependencies = [ [[package]] name = "pallet-ethereum-events" -version = "6.2.0" +version = "6.3.0" dependencies = [ "env_logger 0.10.0", "frame-benchmarking", @@ -7792,7 +7797,7 @@ dependencies = [ [[package]] name = "pallet-nft-manager" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -7921,7 +7926,7 @@ dependencies = [ [[package]] name = "pallet-parachain-staking" -version = "6.2.0" +version = "6.3.0" dependencies = [ "assert_matches", "frame-benchmarking", @@ -8200,7 +8205,7 @@ dependencies = [ [[package]] name = "pallet-summary" -version = "6.2.0" +version = "6.3.0" dependencies = [ "assert_matches", "frame-benchmarking", @@ -8269,7 +8274,7 @@ dependencies = [ [[package]] name = "pallet-token-manager" -version = "6.2.0" +version = "6.3.0" dependencies = [ "enumflags2", "frame-benchmarking", @@ -8378,7 +8383,7 @@ dependencies = [ [[package]] name = "pallet-validators-manager" -version = "6.2.0" +version = "6.3.0" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -13202,7 +13207,7 @@ dependencies = [ [[package]] name = "sp-avn-common" -version = "6.2.0" +version = "6.3.0" dependencies = [ "byte-slice-cast", "derive_more", diff --git a/Cargo.toml b/Cargo.toml index 0fa49767..7f8e6836 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ lto = "fat" codegen-units = 1 [workspace.package] -version = "6.2.0" +version = "6.3.0" authors = ["Aventus systems team"] homepage = "https://www.aventus.io/" repository = "https://github.com/Aventus-Network-Services/avn-node-parachain/" diff --git a/pallets/avn-anchor/Cargo.toml b/pallets/avn-anchor/Cargo.toml index 305279a8..0dfc6c64 100644 --- a/pallets/avn-anchor/Cargo.toml +++ b/pallets/avn-anchor/Cargo.toml @@ -34,9 +34,16 @@ sp-application-crypto = { default-features = false, git = "https://github.com/pa parking_lot = { version = "0.12.0" } sp-io = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" } sp-keystore = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" } - +pallet-scheduler = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" } +pallet-session = { features = [ + "historical", +], git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0", default-features = false } pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0",features=["insecure_zero_ed"] } -pallet-avn-proxy = { default-features = false, path = "../avn-proxy" } +pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" } + +pallet-avn-proxy = { path = "../avn-proxy" } +pallet-token-manager = { path = "../token-manager" } +pallet-eth-bridge = { path = "../eth-bridge" } [features] default = ["std"] diff --git a/pallets/avn-anchor/src/benchmarking.rs b/pallets/avn-anchor/src/benchmarking.rs index 8fdc715f..ea4c3d58 100644 --- a/pallets/avn-anchor/src/benchmarking.rs +++ b/pallets/avn-anchor/src/benchmarking.rs @@ -7,12 +7,12 @@ use crate::{ }; use codec::{Decode, Encode}; use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; -use frame_support::BoundedVec; +use frame_support::{traits::Currency, BoundedVec}; use frame_system::RawOrigin; use sp_application_crypto::KeyTypeId; use sp_avn_common::Proof; use sp_core::H256; -use sp_runtime::RuntimeAppPublic; +use sp_runtime::{RuntimeAppPublic, Saturating}; pub const BENCH_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test"); @@ -38,10 +38,56 @@ fn create_proof( Proof { signer, relayer, signature: signature.into() } } +fn setup_chain( + handler: &T::AccountId, +) -> Result<(ChainId, BoundedVec>), &'static str> { + let name: BoundedVec> = BoundedVec::try_from(vec![0u8; 32]).unwrap(); + Pallet::::register_chain_handler(RawOrigin::Signed(handler.clone()).into(), name.clone())?; + let chain_id = ChainHandlers::::get(handler).ok_or("Chain not registered")?; + Ok((chain_id, name)) +} + +fn setup_balance(account: &T::AccountId) { + let min_balance = T::Currency::minimum_balance(); + // Convert default checkpoint fee to the correct balance type + let default_fee: BalanceOf = T::DefaultCheckpointFee::get(); + + // Calculate a large initial balance + // Use saturating operations to prevent overflow + let large_multiplier: BalanceOf = 1000u32.into(); + let fee_component = default_fee.saturating_mul(large_multiplier); + let existential_component = min_balance.saturating_mul(large_multiplier); + + // Add the components together for total initial balance + let initial_balance = fee_component.saturating_add(existential_component); + + // Set the balance + T::Currency::make_free_balance_be(account, initial_balance); + + // Ensure the account has enough free balance + assert!( + T::Currency::free_balance(account) >= initial_balance, + "Failed to set up sufficient balance" + ); +} + +fn ensure_fee_payment_possible( + chain_id: ChainId, + account: &T::AccountId, +) -> Result<(), &'static str> { + let fee = Pallet::::checkpoint_fee(chain_id); + let balance = T::Currency::free_balance(account); + if balance < fee { + return Err("Insufficient balance for fee payment") + } + Ok(()) +} + benchmarks! { register_chain_handler { - let caller: T::AccountId = account("caller", 0, SEED); + let caller: T::AccountId = create_account_id::(0); let name: BoundedVec> = BoundedVec::try_from(vec![0u8; 32]).unwrap(); + setup_balance::(&caller); }: _(RawOrigin::Signed(caller.clone()), name.clone()) verify { assert!(ChainHandlers::::contains_key(&caller)); @@ -50,11 +96,11 @@ benchmarks! { } update_chain_handler { - let old_handler: T::AccountId = account("old_handler", 0, SEED); - let new_handler: T::AccountId = account("new_handler", 1, SEED); - let name: BoundedVec> = BoundedVec::try_from(vec![0u8; 32]).unwrap(); - - Pallet::::register_chain_handler(RawOrigin::Signed(old_handler.clone()).into(), name.clone())?; + let old_handler: T::AccountId = create_account_id::(0); + let new_handler: T::AccountId = create_account_id::(1); + setup_balance::(&old_handler); + setup_balance::(&new_handler); + let (chain_id, name) = setup_chain::(&old_handler)?; }: _(RawOrigin::Signed(old_handler.clone()), new_handler.clone()) verify { assert!(!ChainHandlers::::contains_key(&old_handler)); @@ -64,18 +110,28 @@ benchmarks! { } submit_checkpoint_with_identity { - let handler: T::AccountId = account("handler", 0, SEED); - let name: BoundedVec> = BoundedVec::try_from(vec![0u8; 32]).unwrap(); - let checkpoint = H256::from([0u8; 32]); + let handler: T::AccountId = create_account_id::(0); - Pallet::::register_chain_handler(RawOrigin::Signed(handler.clone()).into(), name.clone())?; + // Setup balances + setup_balance::(&handler); - let chain_id = ChainHandlers::::get(&handler).unwrap(); + // Setup chain and verify initial state + let (chain_id, _) = setup_chain::(&handler)?; + ensure_fee_payment_possible::(chain_id, &handler)?; + + let checkpoint = H256::from([0u8; 32]); let initial_checkpoint_id = NextCheckpointId::::get(chain_id); + + // Verify initial balance is sufficient + let fee = Pallet::::checkpoint_fee(chain_id); + let initial_balance = T::Currency::free_balance(&handler); + assert!(initial_balance >= fee, "Insufficient initial balance"); }: _(RawOrigin::Signed(handler.clone()), checkpoint) verify { assert_eq!(Checkpoints::::get(chain_id, initial_checkpoint_id), checkpoint); assert_eq!(NextCheckpointId::::get(chain_id), initial_checkpoint_id + 1); + // Verify fee was paid + assert!(T::Currency::free_balance(&handler) < initial_balance, "Fee was not deducted"); } signed_register_chain_handler { @@ -84,7 +140,8 @@ benchmarks! { .expect("Valid account id"); let relayer: T::AccountId = create_account_id::(1); let name: BoundedVec> = BoundedVec::try_from(vec![0u8; 32]).unwrap(); - let nonce = 0; + setup_balance::(&handler); + setup_balance::(&relayer); let payload = encode_signed_register_chain_handler_params::(&relayer, &handler, &name); let signature = signer_pair.sign(&payload).ok_or("Error signing proof")?; @@ -102,10 +159,10 @@ benchmarks! { .expect("Valid account id"); let new_handler: T::AccountId = create_account_id::(1); let relayer: T::AccountId = create_account_id::(2); - let name: BoundedVec> = BoundedVec::try_from(vec![0u8; 32]).unwrap(); - - Pallet::::register_chain_handler(RawOrigin::Signed(old_handler.clone()).into(), name.clone())?; - let chain_id = ChainHandlers::::get(&old_handler).unwrap(); + setup_balance::(&old_handler); + setup_balance::(&new_handler); + setup_balance::(&relayer); + let (chain_id, _) = setup_chain::(&old_handler)?; let nonce = Nonces::::get(chain_id); let payload = encode_signed_update_chain_handler_params::( @@ -121,29 +178,43 @@ benchmarks! { verify { assert!(!ChainHandlers::::contains_key(&old_handler)); assert!(ChainHandlers::::contains_key(&new_handler)); - let chain_data = ChainData::::get(ChainHandlers::::get(&new_handler).unwrap()).unwrap(); - assert_eq!(chain_data.name, name); } signed_submit_checkpoint_with_identity { let signer_pair = SignerId::generate_pair(None); let handler: T::AccountId = T::AccountId::decode(&mut Encode::encode(&signer_pair).as_slice()).expect("valid account id"); let relayer: T::AccountId = create_account_id::(1); - let name: BoundedVec> = BoundedVec::try_from(vec![0u8; 32]).unwrap(); - let checkpoint = H256::from([0u8; 32]); - Pallet::::register_chain_handler(RawOrigin::Signed(handler.clone()).into(), name.clone())?; - let chain_id = ChainHandlers::::get(&handler).unwrap(); + setup_balance::(&handler); + setup_balance::(&relayer); + + let (chain_id, _) = setup_chain::(&handler)?; + ensure_fee_payment_possible::(chain_id, &handler)?; + + let checkpoint = H256::from([0u8; 32]); let nonce = Nonces::::get(chain_id); let initial_checkpoint_id = NextCheckpointId::::get(chain_id); let payload = encode_signed_submit_checkpoint_params::(&relayer, &handler, &checkpoint, chain_id, nonce); let signature = signer_pair.sign(&payload).ok_or("Error signing proof")?; let proof = create_proof::(signature.into(), handler.clone(), relayer); + + let initial_balance = T::Currency::free_balance(&handler); + let fee = Pallet::::checkpoint_fee(chain_id); + assert!(initial_balance >= fee, "Insufficient initial balance"); }: _(RawOrigin::Signed(handler.clone()), proof, handler.clone(), checkpoint) verify { assert_eq!(Checkpoints::::get(chain_id, initial_checkpoint_id), checkpoint); assert_eq!(NextCheckpointId::::get(chain_id), initial_checkpoint_id + 1); + assert!(T::Currency::free_balance(&handler) < initial_balance, "Fee was not deducted"); + } + + set_checkpoint_fee { + let chain_id = 0; + let new_fee = BalanceOf::::from(100u32); + }: _(RawOrigin::Root, chain_id, new_fee) + verify { + assert_eq!(CheckpointFee::::get(chain_id), new_fee); } } diff --git a/pallets/avn-anchor/src/default_weights.rs b/pallets/avn-anchor/src/default_weights.rs index 9e59b364..bfc55671 100644 --- a/pallets/avn-anchor/src/default_weights.rs +++ b/pallets/avn-anchor/src/default_weights.rs @@ -2,20 +2,20 @@ //! Autogenerated weights for pallet_avn_anchor //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-09-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `ivancholakov-Precision-3571`, CPU: `12th Gen Intel(R) Core(TM) i9-12900H` +//! HOSTNAME: `ip-172-31-28-16`, CPU: `AMD EPYC 7R32` //! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/release/avn-parachain-collator +// ./avn-parachain-collator // benchmark // pallet // --chain // dev // --wasm-execution=compiled // --template -// .maintain/frame-weight-template.hbs +// ./frame-weight-template.hbs // --pallet // pallet_avn_anchor // --extrinsic @@ -25,7 +25,7 @@ // --repeat // 20 // --output -// test-benchmarks.rs +// avn_anchor_weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -43,6 +43,7 @@ pub trait WeightInfo { fn signed_register_chain_handler() -> Weight; fn signed_update_chain_handler() -> Weight; fn signed_submit_checkpoint_with_identity() -> Weight; + fn set_checkpoint_fee() -> Weight; } /// Weights for pallet_avn_anchor using the Substrate node and recommended hardware. @@ -60,8 +61,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 13_179_000 picoseconds. - Weight::from_parts(13_753_000, 3517) + // Minimum execution time: 21_950_000 picoseconds. + Weight::from_parts(22_700_000, 3517) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -73,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `327` // Estimated: `6044` - // Minimum execution time: 16_209_000 picoseconds. - Weight::from_parts(17_174_000, 6044) + // Minimum execution time: 29_240_000 picoseconds. + Weight::from_parts(29_691_000, 6044) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -84,16 +85,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: `AvnAnchor::NextCheckpointId` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::Nonces` (r:1 w:1) /// Proof: `AvnAnchor::Nonces` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `AvnAnchor::CheckpointFee` (r:1 w:0) + /// Proof: `AvnAnchor::CheckpointFee` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `TokenManager::AVTTokenContract` (r:1 w:0) + /// Proof: `TokenManager::AVTTokenContract` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::Checkpoints` (r:0 w:1) /// Proof: `AvnAnchor::Checkpoints` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) fn submit_checkpoint_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `297` - // Estimated: `3517` - // Minimum execution time: 16_084_000 picoseconds. - Weight::from_parts(17_521_000, 3517) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + // Measured: `648` + // Estimated: `6196` + // Minimum execution time: 92_751_000 picoseconds. + Weight::from_parts(93_781_000, 6196) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `AvnAnchor::ChainHandlers` (r:1 w:1) /// Proof: `AvnAnchor::ChainHandlers` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) @@ -107,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 77_965_000 picoseconds. - Weight::from_parts(85_368_000, 3517) + // Minimum execution time: 163_752_000 picoseconds. + Weight::from_parts(166_722_000, 3517) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -122,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `343` // Estimated: `6044` - // Minimum execution time: 87_020_000 picoseconds. - Weight::from_parts(103_962_000, 6044) + // Minimum execution time: 174_893_000 picoseconds. + Weight::from_parts(176_013_000, 6044) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -133,16 +140,32 @@ impl WeightInfo for SubstrateWeight { /// Proof: `AvnAnchor::Nonces` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::NextCheckpointId` (r:1 w:1) /// Proof: `AvnAnchor::NextCheckpointId` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `AvnAnchor::CheckpointFee` (r:1 w:0) + /// Proof: `AvnAnchor::CheckpointFee` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `TokenManager::AVTTokenContract` (r:1 w:0) + /// Proof: `TokenManager::AVTTokenContract` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::Checkpoints` (r:0 w:1) /// Proof: `AvnAnchor::Checkpoints` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) fn signed_submit_checkpoint_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `297` - // Estimated: `3517` - // Minimum execution time: 82_980_000 picoseconds. - Weight::from_parts(90_203_000, 3517) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + // Measured: `614` + // Estimated: `6196` + // Minimum execution time: 236_483_000 picoseconds. + Weight::from_parts(238_713_000, 6196) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `AvnAnchor::CheckpointFee` (r:0 w:1) + /// Proof: `AvnAnchor::CheckpointFee` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_checkpoint_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_960_000 picoseconds. + Weight::from_parts(10_250_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -160,8 +183,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 13_179_000 picoseconds. - Weight::from_parts(13_753_000, 3517) + // Minimum execution time: 21_950_000 picoseconds. + Weight::from_parts(22_700_000, 3517) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -173,8 +196,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `327` // Estimated: `6044` - // Minimum execution time: 16_209_000 picoseconds. - Weight::from_parts(17_174_000, 6044) + // Minimum execution time: 29_240_000 picoseconds. + Weight::from_parts(29_691_000, 6044) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -184,16 +207,22 @@ impl WeightInfo for () { /// Proof: `AvnAnchor::NextCheckpointId` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::Nonces` (r:1 w:1) /// Proof: `AvnAnchor::Nonces` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `AvnAnchor::CheckpointFee` (r:1 w:0) + /// Proof: `AvnAnchor::CheckpointFee` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `TokenManager::AVTTokenContract` (r:1 w:0) + /// Proof: `TokenManager::AVTTokenContract` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::Checkpoints` (r:0 w:1) /// Proof: `AvnAnchor::Checkpoints` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) fn submit_checkpoint_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `297` - // Estimated: `3517` - // Minimum execution time: 16_084_000 picoseconds. - Weight::from_parts(17_521_000, 3517) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + // Measured: `648` + // Estimated: `6196` + // Minimum execution time: 92_751_000 picoseconds. + Weight::from_parts(93_781_000, 6196) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } /// Storage: `AvnAnchor::ChainHandlers` (r:1 w:1) /// Proof: `AvnAnchor::ChainHandlers` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) @@ -207,8 +236,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3517` - // Minimum execution time: 77_965_000 picoseconds. - Weight::from_parts(85_368_000, 3517) + // Minimum execution time: 163_752_000 picoseconds. + Weight::from_parts(166_722_000, 3517) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -222,8 +251,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `343` // Estimated: `6044` - // Minimum execution time: 87_020_000 picoseconds. - Weight::from_parts(103_962_000, 6044) + // Minimum execution time: 174_893_000 picoseconds. + Weight::from_parts(176_013_000, 6044) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -233,15 +262,31 @@ impl WeightInfo for () { /// Proof: `AvnAnchor::Nonces` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::NextCheckpointId` (r:1 w:1) /// Proof: `AvnAnchor::NextCheckpointId` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) + /// Storage: `AvnAnchor::CheckpointFee` (r:1 w:0) + /// Proof: `AvnAnchor::CheckpointFee` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + /// Storage: `TokenManager::AVTTokenContract` (r:1 w:0) + /// Proof: `TokenManager::AVTTokenContract` (`max_values`: Some(1), `max_size`: Some(20), added: 515, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `AvnAnchor::Checkpoints` (r:0 w:1) /// Proof: `AvnAnchor::Checkpoints` (`max_values`: None, `max_size`: Some(76), added: 2551, mode: `MaxEncodedLen`) fn signed_submit_checkpoint_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `297` - // Estimated: `3517` - // Minimum execution time: 82_980_000 picoseconds. - Weight::from_parts(90_203_000, 3517) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + // Measured: `614` + // Estimated: `6196` + // Minimum execution time: 236_483_000 picoseconds. + Weight::from_parts(238_713_000, 6196) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `AvnAnchor::CheckpointFee` (r:0 w:1) + /// Proof: `AvnAnchor::CheckpointFee` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) + fn set_checkpoint_fee() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 9_960_000 picoseconds. + Weight::from_parts(10_250_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } } \ No newline at end of file diff --git a/pallets/avn-anchor/src/lib.rs b/pallets/avn-anchor/src/lib.rs index af906db0..377f5561 100644 --- a/pallets/avn-anchor/src/lib.rs +++ b/pallets/avn-anchor/src/lib.rs @@ -5,14 +5,14 @@ extern crate alloc; #[cfg(not(feature = "std"))] use alloc::string::String; -use frame_support::{dispatch::DispatchResult, ensure}; +use frame_support::{dispatch::DispatchResult, ensure, traits::Currency}; pub mod default_weights; pub use default_weights::WeightInfo; use codec::Encode; use sp_avn_common::CallDecoder; -use sp_core::{ConstU32, H256}; +use sp_core::{ConstU32, Get, H256}; use sp_runtime::BoundedVec; use sp_std::prelude::*; @@ -36,12 +36,16 @@ pub use self::pallet::*; pub type ChainId = u32; pub type CheckpointId = u64; +pub(crate) type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::{dispatch::GetDispatchInfo, pallet_prelude::*, traits::IsSubType}; use frame_system::pallet_prelude::*; - use sp_avn_common::{verify_signature, InnerCallValidator, Proof}; + use sp_avn_common::{verify_signature, FeePaymentHandler, InnerCallValidator, Proof}; + use sp_core::H160; use sp_runtime::traits::{Dispatchable, IdentifyAccount, Verify}; pub type ChainId = u32; @@ -78,6 +82,24 @@ pub mod pallet { + TypeInfo; type WeightInfo: WeightInfo; + + /// Currency type for processing fee payment + type Currency: Currency; + + /// The type of token identifier + /// (a H160 because this is an Ethereum address) + type Token: Parameter + Default + Copy + From + Into + MaxEncodedLen; + + /// A handler to process relayer fee payments + type FeeHandler: FeePaymentHandler< + AccountId = Self::AccountId, + Token = Self::Token, + TokenBalance = >::Balance, + Error = DispatchError, + >; + + /// The default fee for checkpoint submission + type DefaultCheckpointFee: Get>; } #[pallet::pallet] @@ -94,6 +116,11 @@ pub mod pallet { /// A new checkpoint was submitted. [handler_account_id, chain_id, checkpoint_id, /// checkpoint] CheckpointSubmitted(T::AccountId, ChainId, CheckpointId, H256), + /// The checkpoint fee was updated. [new_fee] + CheckpointFeeUpdated { chain_id: ChainId, new_fee: BalanceOf }, + + /// Fee was charged for checkpoint submission [handler, fee, nonce] + CheckpointFeeCharged { handler: T::AccountId, chain_id: ChainId, fee: BalanceOf }, } #[pallet::error] @@ -111,6 +138,11 @@ pub mod pallet { NoChainDataAvailable, } + #[pallet::storage] + #[pallet::getter(fn checkpoint_fee)] + pub type CheckpointFee = + StorageMap<_, Blake2_128Concat, ChainId, BalanceOf, ValueQuery, T::DefaultCheckpointFee>; + #[pallet::storage] #[pallet::getter(fn nonces)] pub type Nonces = StorageMap<_, Blake2_128Concat, ChainId, u64, ValueQuery>; @@ -288,9 +320,38 @@ pub mod pallet { Ok(()) } + + #[pallet::weight(::WeightInfo::set_checkpoint_fee())] + #[pallet::call_index(6)] + pub fn set_checkpoint_fee( + origin: OriginFor, + chain_id: ChainId, + new_fee: BalanceOf, + ) -> DispatchResult { + ensure_root(origin)?; + + CheckpointFee::::insert(chain_id, new_fee); + Self::deposit_event(Event::CheckpointFeeUpdated { chain_id, new_fee }); + + Ok(()) + } } impl Pallet { + pub(crate) fn charge_fee(handler: T::AccountId, chain_id: ChainId) -> DispatchResult { + let checkpoint_fee = Self::checkpoint_fee(chain_id); + + T::FeeHandler::pay_treasury(&checkpoint_fee, &handler)?; + + Self::deposit_event(Event::CheckpointFeeCharged { + handler: handler.clone(), + fee: checkpoint_fee, + chain_id, + }); + + Ok(()) + } + fn get_next_chain_id() -> Result { NextChainId::::try_mutate(|id| { let current_id = *id; @@ -377,7 +438,7 @@ pub mod pallet { )); >::mutate(chain_id, |n| *n += 1); - + Self::charge_fee(handler.clone(), chain_id)?; Ok(()) } diff --git a/pallets/avn-anchor/src/mock.rs b/pallets/avn-anchor/src/mock.rs index 6f0f91de..af43a711 100644 --- a/pallets/avn-anchor/src/mock.rs +++ b/pallets/avn-anchor/src/mock.rs @@ -1,19 +1,24 @@ use crate::{self as avn_anchor, *}; use codec::{Decode, Encode}; +use core::cell::RefCell; use frame_support::{ pallet_prelude::*, parameter_types, - traits::{ConstU16, ConstU32, ConstU64, Everything}, + traits::{ConstU16, ConstU32, ConstU64, EqualPrivilegeOnly, Everything}, + PalletId, }; -use frame_system as system; +use frame_system::{self as system, limits::BlockWeights, EnsureRoot}; +use pallet_avn::BridgeInterfaceNotification; use pallet_avn_proxy::{self as avn_proxy, ProvableProxy}; +use pallet_session as session; use scale_info::TypeInfo; use sp_avn_common::{FeePaymentHandler, InnerCallValidator, Proof}; -use sp_core::{sr25519, H256}; +use sp_core::{sr25519, Pair, H160, H256}; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup, Verify}, - BuildStorage, + testing::{TestXt, UintAuthorityId}, + traits::{BlakeTwo256, ConvertInto, IdentityLookup, Verify}, + BuildStorage, Perbill, }; use std::sync::Arc; @@ -22,6 +27,59 @@ type Block = frame_system::mocking::MockBlock; pub type Signature = sr25519::Signature; pub type Balance = u128; pub type AccountId = ::Signer; +pub type SessionIndex = u32; +pub type Extrinsic = TestXt; +pub const BASE_FEE: u64 = 12; + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +const MAX_BLOCK_WEIGHT: Weight = + Weight::from_parts(2_000_000_000_000 as u64, 0).set_proof_size(u64::MAX); +pub const INITIAL_BALANCE: Balance = 1_000_000_000_000; +pub const ONE_AVT: Balance = 1_000000_000000_000000u128; +pub const HUNDRED_AVT: Balance = 100 * ONE_AVT; + +// TODO: Refactor this struct to be reused in all tests +pub struct TestAccount { + pub seed: [u8; 32], +} + +impl TestAccount { + pub fn new(seed: [u8; 32]) -> Self { + TestAccount { seed } + } + + pub fn account_id(&self) -> AccountId { + return AccountId::decode(&mut self.key_pair().public().to_vec().as_slice()).unwrap() + } + + pub fn key_pair(&self) -> sr25519::Pair { + return sr25519::Pair::from_seed(&self.seed) + } +} + +pub fn validator_id_1() -> AccountId { + TestAccount::new([1u8; 32]).account_id() +} +pub fn validator_id_2() -> AccountId { + TestAccount::new([2u8; 32]).account_id() +} +pub fn validator_id_3() -> AccountId { + TestAccount::new([3u8; 32]).account_id() +} + +thread_local! { + pub static MOCK_FEE_HANDLER_SHOULD_FAIL: RefCell = RefCell::new(false); + // validator accounts (aka public addresses, public keys-ish) + pub static VALIDATORS: RefCell>> = RefCell::new(Some(vec![ + validator_id_1(), + validator_id_2(), + validator_id_3(), + ])); +} + +pub fn set_mock_fee_handler_should_fail(should_fail: bool) { + MOCK_FEE_HANDLER_SHOULD_FAIL.with(|f| *f.borrow_mut() = should_fail); +} frame_support::construct_runtime!( pub enum TestRuntime @@ -31,9 +89,29 @@ frame_support::construct_runtime!( Avn: pallet_avn::{Pallet, Storage, Event}, AvnProxy: avn_proxy::{Pallet, Call, Storage, Event}, AvnAnchor: avn_anchor::{Pallet, Call, Storage, Event}, + TokenManager: pallet_token_manager::{Pallet, Call, Storage, Event}, + EthBridge: pallet_eth_bridge::{Pallet, Call, Storage, Event}, + Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, } ); +parameter_types! { + pub const Period: u64 = 1; + pub const Offset: u64 = 0; + pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(33); + pub const DefaultCheckpointFee: Balance = 1_000_000_000; +} + +impl frame_system::offchain::SendTransactionTypes for TestRuntime +where + RuntimeCall: From, +{ + type OverarchingCall = RuntimeCall; + type Extrinsic = Extrinsic; +} + impl system::Config for TestRuntime { type BaseCallFilter = Everything; type BlockWeights = (); @@ -62,6 +140,57 @@ impl system::Config for TestRuntime { parameter_types! { pub const ExistentialDeposit: Balance = 1; + pub const AvnTreasuryPotId: PalletId = PalletId(*b"Treasury"); + pub static TreasuryGrowthPercentage: Perbill = Perbill::from_percent(75); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(Weight::from_parts(10 as u64, 0)) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = Weight::from_parts(BASE_FEE as u64, 0); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAX_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAX_BLOCK_WEIGHT); + weights.reserved = Some( + MAX_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAX_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(Perbill::from_percent(0)) + .build_or_panic(); + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_scheduler::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<100>; + type WeightInfo = (); + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = (); +} + +impl pallet_token_manager::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProcessedEventsChecker = (); + type TokenId = sp_core::H160; + type TokenBalance = u128; + type Public = AccountId; + type Signature = Signature; + type AvnTreasuryPotId = AvnTreasuryPotId; + type TreasuryGrowthPercentage = TreasuryGrowthPercentage; + type OnGrowthLiftedHandler = (); + type WeightInfo = (); + type Scheduler = Scheduler; + type Preimages = (); + type PalletsOrigin = OriginCaller; + type BridgeInterface = EthBridge; } impl pallet_balances::Config for TestRuntime { @@ -101,12 +230,86 @@ impl avn_proxy::Config for TestRuntime { type Token = sp_core::H160; } +impl pallet_eth_bridge::Config for TestRuntime { + type MaxQueuedTxRequests = frame_support::traits::ConstU32<100>; + type RuntimeEvent = RuntimeEvent; + type TimeProvider = Timestamp; + type MinEthBlockConfirmation = ConstU64<20>; + type RuntimeCall = RuntimeCall; + type WeightInfo = (); + type AccountToBytesConvert = Avn; + type BridgeInterfaceNotification = Self; + type ReportCorroborationOffence = (); + type ProcessedEventsChecker = (); + type EthereumEventsFilter = (); +} + +impl pallet_timestamp::Config for TestRuntime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = frame_support::traits::ConstU64<12000>; + type WeightInfo = (); +} + +impl BridgeInterfaceNotification for TestRuntime { + fn process_result( + _tx_id: u32, + _caller_id: Vec, + _tx_succeeded: bool, + ) -> sp_runtime::DispatchResult { + Ok(()) + } +} + +pub struct TestSessionManager; +impl session::SessionManager for TestSessionManager { + fn new_session(_new_index: SessionIndex) -> Option> { + VALIDATORS.with(|l| l.borrow_mut().take()) + } + fn end_session(_: SessionIndex) {} + fn start_session(_: SessionIndex) {} +} + +impl session::Config for TestRuntime { + type SessionManager = + pallet_session::historical::NoteHistoricalRoot; + type Keys = UintAuthorityId; + type ShouldEndSession = session::PeriodicSessions; + type SessionHandler = (Avn,); + type RuntimeEvent = RuntimeEvent; + type ValidatorId = AccountId; + type ValidatorIdOf = ConvertInto; + type NextSessionRotation = session::PeriodicSessions; + type WeightInfo = (); +} + +impl pallet_session::historical::Config for TestRuntime { + type FullIdentification = AccountId; + type FullIdentificationOf = ConvertInto; +} + +impl pallet_session::historical::SessionManager for TestSessionManager { + fn new_session(_new_index: SessionIndex) -> Option> { + VALIDATORS.with(|l| { + l.borrow_mut() + .take() + .map(|validators| validators.iter().map(|v| (*v, *v)).collect()) + }) + } + fn end_session(_: SessionIndex) {} + fn start_session(_: SessionIndex) {} +} + impl Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type Public = AccountId; type Signature = Signature; type WeightInfo = default_weights::SubstrateWeight; + type FeeHandler = TokenManager; + type Token = sp_core::H160; + type Currency = Balances; + type DefaultCheckpointFee = DefaultCheckpointFee; } // Test Avn proxy configuration logic #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)] @@ -150,7 +353,16 @@ impl InnerCallValidator for TestAvnProxyConfig { pub fn new_test_ext() -> sp_io::TestExternalities { let keystore = MemoryKeystore::new(); - let t = system::GenesisConfig::::default().build_storage().unwrap(); + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (create_account_id(1), INITIAL_BALANCE), + (create_account_id(2), INITIAL_BALANCE), + (create_account_id(3), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.register_extension(KeystoreExt(Arc::new(keystore))); ext.execute_with(|| System::set_block_number(1)); @@ -180,6 +392,11 @@ pub fn inner_call_failed_event_emitted(call_dispatch_error: DispatchError) -> bo }) } +fn fake_treasury() -> AccountId { + let seed: [u8; 32] = [01; 32]; + return TestAccount::new(seed).account_id() +} + impl FeePaymentHandler for TestRuntime { type Token = sp_core::H160; type TokenBalance = u128; @@ -188,10 +405,32 @@ impl FeePaymentHandler for TestRuntime { fn pay_fee( _token: &Self::Token, - _amount: &Self::TokenBalance, - _payer: &Self::AccountId, - _recipient: &Self::AccountId, + amount: &Self::TokenBalance, + payer: &Self::AccountId, + recipient: &Self::AccountId, ) -> Result<(), Self::Error> { - return Err(DispatchError::Other("Test - Error")) + Ok(()) } + + fn pay_treasury( + amount: &Self::TokenBalance, + payer: &Self::AccountId, + ) -> Result<(), Self::Error> { + if MOCK_FEE_HANDLER_SHOULD_FAIL.with(|f| *f.borrow()) { + return Err(DispatchError::Other("Test - Error")) + } + + let recipient = fake_treasury(); + + Balances::transfer(RuntimeOrigin::signed(payer.clone()), recipient, *amount)?; + + Ok(()) + } +} +pub fn create_account_id(seed: u8) -> AccountId { + TestAccount::new([seed; 32]).account_id() +} + +pub fn get_balance(account: &AccountId) -> Balance { + Balances::free_balance(account) } diff --git a/pallets/avn-anchor/src/tests.rs b/pallets/avn-anchor/src/tests.rs index 78e75837..e35b2b53 100644 --- a/pallets/avn-anchor/src/tests.rs +++ b/pallets/avn-anchor/src/tests.rs @@ -7,7 +7,7 @@ use frame_support::{assert_noop, assert_ok, BoundedVec}; use pallet_avn_proxy::Error as avn_proxy_error; use sp_avn_common::Proof; use sp_core::{sr25519, ConstU32, Pair, H256}; -use sp_runtime::traits::Hash; +use sp_runtime::{traits::Hash, DispatchError}; fn create_account_id(seed: u8) -> AccountId { create_account_pair(seed).public() @@ -79,18 +79,6 @@ fn register_chain_handler_fails_for_empty_name() { }); } -// #[test] -// fn register_chain_handler_fails_for_duplicate_names() { -// new_test_ext().execute_with(|| { -// let handler = create_account_id(1); -// let name = bounded_vec(b"Test Chain"); - -// assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler), -// name.clone())); let second_handler = create_account_id(2); -// assert_err!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(second_handler), -// name.clone())); }); -// } - #[test] fn register_chain_with_max_length_name_succeeds() { new_test_ext().execute_with(|| { @@ -191,19 +179,16 @@ fn update_chain_handler_fails_for_non_handler() { Error::::ChainNotRegistered ); - // Verify that the handler hasn't changed let chain_id = AvnAnchor::chain_handlers(current_handler).unwrap(); let chain_data = AvnAnchor::chain_data(chain_id).unwrap(); assert_eq!(chain_data.chain_id, 0); assert_eq!(chain_data.name, name); - // Verify that the update is successful when initiated by the current handler assert_ok!(AvnAnchor::update_chain_handler( RuntimeOrigin::signed(current_handler), new_handler )); - // Verify that the handler has now changed assert!(AvnAnchor::chain_handlers(current_handler).is_none()); let updated_chain_id = AvnAnchor::chain_handlers(new_handler).unwrap(); let updated_chain_data = AvnAnchor::chain_data(updated_chain_id).unwrap(); @@ -224,18 +209,21 @@ fn submit_checkpoint_with_identity_works() { let checkpoint = H256::random(); assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler), name)); + + let chain_id = AvnAnchor::chain_handlers(handler).unwrap(); + let default_fee = DefaultCheckpointFee::get(); + assert_ok!(AvnAnchor::submit_checkpoint_with_identity( RuntimeOrigin::signed(handler), checkpoint )); - let chain_id = AvnAnchor::chain_handlers(handler).unwrap(); - assert_eq!(AvnAnchor::checkpoints(chain_id, 0), checkpoint); - assert_eq!(AvnAnchor::next_checkpoint_id(chain_id), 1); - - System::assert_last_event( + System::assert_has_event( Event::CheckpointSubmitted(handler, chain_id, 0, checkpoint).into(), ); + System::assert_has_event( + Event::CheckpointFeeCharged { handler, chain_id, fee: default_fee }.into(), + ); }); } @@ -531,7 +519,6 @@ fn proxy_signed_update_chain_handler_fails_with_invalid_signature() { name.clone() )); - // Create an invalid signature by signing a different payload let invalid_payload = b"invalid payload"; let invalid_signature = old_handler_pair.sign(invalid_payload); @@ -563,7 +550,6 @@ fn proxy_signed_update_chain_handler_fails_with_invalid_signature() { #[test] fn proxy_signed_submit_checkpoint_with_identity_fails_with_unregistered_handler() { new_test_ext().execute_with(|| { - // First, register a valid handler let registered_handler = create_account_id(1); let name = bounded_vec(b"Test Chain"); assert_ok!(AvnAnchor::register_chain_handler( @@ -614,13 +600,181 @@ fn checkpoint_id_overflow_fails() { assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler), name)); - // Set the next checkpoint ID to the maximum value NextCheckpointId::::insert(0, CheckpointId::MAX); - // Attempt to submit a checkpoint, which should fail due to overflow assert_noop!( AvnAnchor::submit_checkpoint_with_identity(RuntimeOrigin::signed(handler), checkpoint), Error::::NoAvailableCheckpointId ); }); } + +// Fees +#[test] +fn set_checkpoint_fee_works() { + new_test_ext().execute_with(|| { + let chain_id = 0; + let new_fee = 100; + + assert_ok!(AvnAnchor::set_checkpoint_fee(RuntimeOrigin::root(), chain_id, new_fee)); + + assert_eq!(AvnAnchor::checkpoint_fee(chain_id), new_fee); + System::assert_last_event(Event::CheckpointFeeUpdated { chain_id, new_fee }.into()); + }); +} + +#[test] +fn set_checkpoint_fee_fails_for_non_root() { + new_test_ext().execute_with(|| { + let non_root = create_account_id(1); + let chain_id = 0; + let new_fee = 100; + + assert_noop!( + AvnAnchor::set_checkpoint_fee(RuntimeOrigin::signed(non_root), chain_id, new_fee), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn charge_fee_works() { + new_test_ext().execute_with(|| { + let handler = create_account_id(1); + let chain_id = 0; + let fee = 100; + + assert_ok!(AvnAnchor::set_checkpoint_fee(RuntimeOrigin::root(), chain_id, fee)); + + assert_ok!(AvnAnchor::charge_fee(handler.clone(), chain_id)); + + System::assert_last_event( + Event::CheckpointFeeCharged { handler: handler.clone(), chain_id, fee }.into(), + ); + }); +} + +#[test] +fn submit_checkpoint_charges_correct_fee() { + new_test_ext().execute_with(|| { + let handler = create_account_id(1); + let chain_id = 0; + let fee = 100; + let name = bounded_vec(b"Test Chain"); + let checkpoint = H256::random(); + + assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler), name)); + assert_ok!(AvnAnchor::set_checkpoint_fee(RuntimeOrigin::root(), chain_id, fee)); + + let balance_before = get_balance(&handler); + + assert_ok!(AvnAnchor::submit_checkpoint_with_identity( + RuntimeOrigin::signed(handler), + checkpoint + )); + + let balance_after = get_balance(&handler); + assert_eq!( + balance_before - fee, + balance_after, + "Handler balance should be reduced by exactly the checkpoint fee amount" + ); + + System::assert_has_event( + Event::CheckpointSubmitted(handler, chain_id, 0, checkpoint).into(), + ); + System::assert_has_event(Event::CheckpointFeeCharged { handler, chain_id, fee }.into()); + }); +} + +#[test] +fn submit_checkpoint_charges_zero_fee() { + new_test_ext().execute_with(|| { + let handler = create_account_id(1); + let chain_id = 0; + let fee = 0; + let name = bounded_vec(b"Test Chain"); + let checkpoint = H256::random(); + + assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler), name)); + assert_ok!(AvnAnchor::set_checkpoint_fee(RuntimeOrigin::root(), chain_id, fee)); + + let balance_before = get_balance(&handler); + + assert_ok!(AvnAnchor::submit_checkpoint_with_identity( + RuntimeOrigin::signed(handler), + checkpoint + )); + + let balance_after = get_balance(&handler); + assert_eq!( + balance_before, balance_after, + "Handler balance should remain unchanged when checkpoint fee is zero" + ); + + System::assert_has_event( + Event::CheckpointSubmitted(handler, chain_id, 0, checkpoint).into(), + ); + System::assert_has_event(Event::CheckpointFeeCharged { handler, chain_id, fee }.into()); + }); +} + +#[test] +fn different_chains_can_have_different_fees() { + new_test_ext().execute_with(|| { + let handler1 = create_account_id(1); + let handler2 = create_account_id(2); + let name1 = bounded_vec(b"Chain 1"); + let name2 = bounded_vec(b"Chain 2"); + let fee1 = 100; + let fee2 = 200; + + assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler1), name1)); + assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler2), name2)); + + let chain_id1 = AvnAnchor::chain_handlers(handler1).unwrap(); + let chain_id2 = AvnAnchor::chain_handlers(handler2).unwrap(); + + assert_ok!(AvnAnchor::set_checkpoint_fee(RuntimeOrigin::root(), chain_id1, fee1)); + assert_ok!(AvnAnchor::set_checkpoint_fee(RuntimeOrigin::root(), chain_id2, fee2)); + + assert_eq!(AvnAnchor::checkpoint_fee(chain_id1), fee1); + assert_eq!(AvnAnchor::checkpoint_fee(chain_id2), fee2); + }); +} + +#[test] +fn default_fee_applies_when_no_override() { + new_test_ext().execute_with(|| { + let handler = create_account_id(1); + let name = bounded_vec(b"Test Chain"); + + assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler), name)); + + let chain_id = AvnAnchor::chain_handlers(handler).unwrap(); + + assert_eq!(AvnAnchor::checkpoint_fee(chain_id), DefaultCheckpointFee::get()); + }); +} + +#[test] +fn fee_override_works() { + new_test_ext().execute_with(|| { + let handler = create_account_id(1); + let name = bounded_vec(b"Test Chain"); + let override_fee = 500u128; + + assert_ok!(AvnAnchor::register_chain_handler(RuntimeOrigin::signed(handler), name)); + + let chain_id = AvnAnchor::chain_handlers(handler).unwrap(); + + assert_eq!(AvnAnchor::checkpoint_fee(chain_id), DefaultCheckpointFee::get()); + + assert_ok!(AvnAnchor::set_checkpoint_fee(RuntimeOrigin::root(), chain_id, override_fee)); + + assert_eq!(AvnAnchor::checkpoint_fee(chain_id), override_fee); + + let other_chain_id = chain_id + 1; + assert_eq!(AvnAnchor::checkpoint_fee(other_chain_id), DefaultCheckpointFee::get()); + }); +} diff --git a/pallets/ethereum-events/src/mock.rs b/pallets/ethereum-events/src/mock.rs index 7cf11b8a..ab64b89c 100644 --- a/pallets/ethereum-events/src/mock.rs +++ b/pallets/ethereum-events/src/mock.rs @@ -835,4 +835,11 @@ impl FeePaymentHandler for TestRuntime { ) -> Result<(), Self::Error> { return Err(DispatchError::Other("Test - Error")) } + + fn pay_treasury( + _amount: &Self::TokenBalance, + _payer: &Self::AccountId, + ) -> Result<(), Self::Error> { + return Err(DispatchError::Other("Test - Error")) + } } diff --git a/pallets/parachain-staking/src/tests/mock.rs b/pallets/parachain-staking/src/tests/mock.rs index 534fa157..9d2ecbf7 100644 --- a/pallets/parachain-staking/src/tests/mock.rs +++ b/pallets/parachain-staking/src/tests/mock.rs @@ -693,6 +693,12 @@ impl FeePaymentHandler for Test { ) -> Result<(), Self::Error> { return Err(DispatchError::Other("Test - Error")) } + fn pay_treasury( + _amount: &Self::TokenBalance, + _payer: &Self::AccountId, + ) -> Result<(), Self::Error> { + return Err(DispatchError::Other("Test - Error")) + } } /// Assert input equal to the last event emitted diff --git a/pallets/token-manager/src/lib.rs b/pallets/token-manager/src/lib.rs index e36d80c0..c8f9df2d 100644 --- a/pallets/token-manager/src/lib.rs +++ b/pallets/token-manager/src/lib.rs @@ -103,6 +103,7 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { + use super::*; use frame_support::{pallet_prelude::*, Blake2_128Concat}; use frame_system::{ensure_root, pallet_prelude::*}; @@ -330,7 +331,6 @@ pub mod pallet { pub balances: Vec<(H160, T::AccountId, u128)>, } - // #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { Self { @@ -1163,4 +1163,12 @@ impl FeePaymentHandler for Pallet { ) -> Result<(), Self::Error> { Self::settle_transfer(token_id, payer, recipient, amount) } + fn pay_treasury( + amount: &Self::TokenBalance, + payer: &Self::AccountId, + ) -> Result<(), Self::Error> { + let recipient = Self::compute_treasury_account_id(); + let token: Self::Token = self::AVTTokenContract::::get().into(); + Self::settle_transfer(&token, payer, &recipient, amount) + } } diff --git a/primitives/avn-common/src/lib.rs b/primitives/avn-common/src/lib.rs index fc518cdd..9d23d1ed 100644 --- a/primitives/avn-common/src/lib.rs +++ b/primitives/avn-common/src/lib.rs @@ -126,6 +126,11 @@ pub trait FeePaymentHandler { _payer: &Self::AccountId, _recipient: &Self::AccountId, ) -> Result<(), Self::Error>; + + fn pay_treasury( + _amount: &Self::TokenBalance, + _payer: &Self::AccountId, + ) -> Result<(), Self::Error>; } impl FeePaymentHandler for () { @@ -142,6 +147,13 @@ impl FeePaymentHandler for () { ) -> Result<(), ()> { Err(()) } + + fn pay_treasury( + _amount: &Self::TokenBalance, + _payer: &Self::AccountId, + ) -> Result<(), Self::Error> { + Err(()) + } } pub fn safe_add_block_numbers( diff --git a/runtime/avn/src/lib.rs b/runtime/avn/src/lib.rs index 46a110d3..3c3a9a98 100644 --- a/runtime/avn/src/lib.rs +++ b/runtime/avn/src/lib.rs @@ -172,7 +172,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("avn-parachain"), impl_name: create_runtime_str!("avn-parachain"), authoring_version: 1, - spec_version: 76, + spec_version: 77, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -637,9 +637,13 @@ impl pallet_avn_transaction_payment::Config for Runtime { impl pallet_avn_anchor::Config for Runtime { type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; + type Currency = Balances; type WeightInfo = pallet_avn_anchor::default_weights::SubstrateWeight; type Public = ::Signer; + type FeeHandler = TokenManager; type Signature = Signature; + type Token = EthAddress; + type DefaultCheckpointFee = DefaultCheckpointFee; } use sp_avn_common::{ @@ -679,6 +683,7 @@ parameter_types! { pub const StringLimit: u32 = 50; pub const MetadataDepositBase: Balance = 1 * MILLI_AVT; pub const MetadataDepositPerByte: Balance = 100 * MICRO_AVT; + pub const DefaultCheckpointFee: Balance = 60 * MILLI_AVT; } const ASSET_ACCOUNT_DEPOSIT: Balance = 100 * MICRO_AVT; diff --git a/runtime/test/src/lib.rs b/runtime/test/src/lib.rs index 2f4a6583..166c0c08 100644 --- a/runtime/test/src/lib.rs +++ b/runtime/test/src/lib.rs @@ -196,7 +196,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("avn-test-parachain"), impl_name: create_runtime_str!("avn-test-parachain"), authoring_version: 1, - spec_version: 76, + spec_version: 77, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -646,9 +646,13 @@ impl pallet_summary::Config for Runtime { impl pallet_avn_anchor::Config for Runtime { type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; + type Currency = Balances; type WeightInfo = pallet_avn_anchor::default_weights::SubstrateWeight; type Public = ::Signer; + type FeeHandler = TokenManager; type Signature = Signature; + type Token = EthAddress; + type DefaultCheckpointFee = DefaultCheckpointFee; } pub type EthAddress = H160; @@ -715,6 +719,7 @@ parameter_types! { pub const StringLimit: u32 = 50; pub const MetadataDepositBase: Balance = 1 * MILLI_AVT; pub const MetadataDepositPerByte: Balance = 100 * MICRO_AVT; + pub const DefaultCheckpointFee: Balance = 100 * MILLI_AVT; } const ASSET_ACCOUNT_DEPOSIT: Balance = 100 * MICRO_AVT; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5ed66130..3bb02264 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.70" +channel = "nightly-2023-05-22" components = [ "cargo", "clippy",