From 1cae3030715b99b10005ac8cc1d97db93b378ab1 Mon Sep 17 00:00:00 2001 From: JasonT <50426640+JasonTulp@users.noreply.github.com> Date: Wed, 15 May 2024 17:32:43 +1200 Subject: [PATCH] Ethy on_initialise optimisations (#831) * Update base pallet to frame v2 * Partially fix tests * Fix mock and tests * Update events to named type * Remove storage getters * Migrate Notary keys to WeakBoundedVec * Add bounds for other storage items * Fix tests * Uncomment EthCallOracle * Address PR Comments * Fix staker_payout tests * Move PendingEventProof processing to on_idle and add tests * Initial benchmarking files * Add benchmarks to pallet_ethy * Add limit to ProcessedMessageIds * Add MissedMessageIds and fallback extrinsics with tests * Add Benchmarks for new extrinsics * Fix merge issues * Address PR Comments * Add bound to ProcessedMessageIds + test * Add migration for BoundedVec * Fix build errors * Rebase and fix tests --- Cargo.lock | 1 + pallet/ethy/Cargo.toml | 2 + pallet/ethy/src/benchmarking.rs | 263 +++++++++++++++ pallet/ethy/src/impls.rs | 142 ++++++-- pallet/ethy/src/lib.rs | 277 ++++++++++------ pallet/ethy/src/mock.rs | 3 + pallet/ethy/src/tests.rs | 506 ++++++++++++++++++++++++++--- pallet/ethy/src/weights.rs | 381 ++++++++++++++++++++++ runtime/Cargo.toml | 1 + runtime/src/lib.rs | 16 +- runtime/src/migrations/ethy.rs | 85 ++++- runtime/src/weights/mod.rs | 1 + runtime/src/weights/pallet_ethy.rs | 188 +++++++++++ 13 files changed, 1677 insertions(+), 189 deletions(-) create mode 100644 pallet/ethy/src/benchmarking.rs create mode 100644 pallet/ethy/src/weights.rs create mode 100644 runtime/src/weights/pallet_ethy.rs diff --git a/Cargo.lock b/Cargo.lock index 7167af1ca..213621253 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5300,6 +5300,7 @@ version = "1.0.1" dependencies = [ "ethabi", "ethereum-types", + "frame-benchmarking", "frame-support", "frame-system", "hex", diff --git a/pallet/ethy/Cargo.toml b/pallet/ethy/Cargo.toml index 2a985909f..0899ff46b 100644 --- a/pallet/ethy/Cargo.toml +++ b/pallet/ethy/Cargo.toml @@ -23,6 +23,7 @@ seed-pallet-common = { path = "../common", default-features = false } # Substrate packages frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30", optional = true, default-features = false } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.30", default-features = false } @@ -57,4 +58,5 @@ std = [ "seed-primitives/std", "seed-pallet-common/std", ] +runtime-benchmarks = ["frame-benchmarking"] try-runtime = ["frame-support/try-runtime"] diff --git a/pallet/ethy/src/benchmarking.rs b/pallet/ethy/src/benchmarking.rs new file mode 100644 index 000000000..ef9f9e891 --- /dev/null +++ b/pallet/ethy/src/benchmarking.rs @@ -0,0 +1,263 @@ +// Copyright 2022-2023 Futureverse Corporation Limited +// +// 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. +// You may obtain a copy of the License at the root of this project source code + +use super::*; + +use ethabi::Token; +use frame_benchmarking::{account as bench_account, benchmarks, impl_benchmark_test_suite}; +use frame_support::{assert_ok, traits::fungibles::Mutate}; +use frame_system::RawOrigin; +use sp_core::crypto::ByteArray; + +use crate::Pallet as EthBridge; + +/// This is a helper function to get an account. +pub fn account(name: &'static str) -> T::AccountId { + bench_account(name, 0, 0) +} + +pub fn origin(acc: &T::AccountId) -> RawOrigin { + RawOrigin::Signed(acc.clone()) +} + +fn encode_event_message( + event_id: EventClaimId, + source: H160, + destination: H160, + message: &[u8], +) -> Vec { + ethabi::encode(&[ + Token::Uint(event_id.into()), + Token::Address(source), + Token::Address(destination), + Token::Bytes(message.to_vec()), + ]) +} + +benchmarks! { + where_clause { where ::EthyId: ByteArray} + + set_xrpl_door_signers { + let p in 1 .. (T::MaxNewSigners::get() as u32 - 1); + let mut new_signers = vec![]; + for i in 0..p { + // Generate random signer + let slice = [i as u8; 33]; + let new_signer = T::EthyId::from_slice(&slice).unwrap(); + new_signers.push(new_signer); + } + let new_signers_joined = new_signers.clone().into_iter().map(|x| (x, true)).collect::>(); + }: _(RawOrigin::Root, new_signers_joined.clone()) + verify { + for signer in new_signers { + assert_eq!(XrplDoorSigners::::get(&signer), true); + } + } + + set_relayer { + let relayer = account::("//Alice"); + assert_ok!(T::MultiCurrency::mint_into(T::NativeAssetId::get().into(), &relayer, T::RelayerBond::get())); + assert_ok!(EthBridge::::deposit_relayer_bond(origin::(&relayer).into())); + }: _(RawOrigin::Root, relayer.clone()) + verify { + assert_eq!(Relayer::::get().unwrap(), relayer); + } + + deposit_relayer_bond { + let relayer = account::("//Alice"); + assert_ok!(T::MultiCurrency::mint_into(T::NativeAssetId::get().into(), &relayer, T::RelayerBond::get())); + }: _(origin::(&relayer)) + verify { + assert_eq!(RelayerPaidBond::::get(relayer), T::RelayerBond::get()); + } + + withdraw_relayer_bond { + let relayer = account::("//Alice"); + assert_ok!(T::MultiCurrency::mint_into(T::NativeAssetId::get().into(), &relayer, T::RelayerBond::get())); + assert_ok!(EthBridge::::deposit_relayer_bond(origin::(&relayer).into())); + }: _(origin::(&relayer)) + verify { + assert_eq!(RelayerPaidBond::::get(relayer), 0); + } + + set_event_block_confirmations { + let confirmations: u64 = 123; + }: _(RawOrigin::Root, confirmations) + verify { + assert_eq!(EventBlockConfirmations::::get(), confirmations); + } + + set_delayed_event_proofs_per_block { + let count: u8 = 123; + }: _(RawOrigin::Root, count) + verify { + assert_eq!(DelayedEventProofsPerBlock::::get(), count); + } + + set_challenge_period { + let blocks: T::BlockNumber = T::BlockNumber::from(100_u32); + }: _(RawOrigin::Root, blocks) + verify { + assert_eq!(ChallengePeriod::::get(), blocks); + } + + set_contract_address { + let contract_address = H160::from_low_u64_be(123); + }: _(RawOrigin::Root, contract_address) + verify { + assert_eq!(ContractAddress::::get(), contract_address); + } + + set_bridge_paused { + let paused = true; + // Sanity check + assert!(!EthBridge::::bridge_paused()); + }: _(RawOrigin::Root, paused) + verify { + assert!(EthBridge::::bridge_paused()); + } + + finalise_authorities_change { + let next_keys = vec![ + T::EthyId::from_slice( + hex!("03e2161ca58ac2f2fa7dfd9f6980fdda1059b467e375ee78cdd5749dc058c0b2c9") + .as_slice(), + ).unwrap(), + ]; + let next_notary_keys = WeakBoundedVec::try_from(next_keys.clone()).unwrap(); + }: _(RawOrigin::None, next_notary_keys.clone()) + verify { + assert_eq!(NotaryKeys::::get(), next_notary_keys); + } + + remove_missing_event_id { + let range = (2,5); + MissedMessageIds::::put(vec![1,2,3,4,5,6]); + }: _(RawOrigin::Root, range) + verify { + assert_eq!(MissedMessageIds::::get(), vec![1,6]); + } + + submit_missing_event { + let relayer = account::("//Alice"); + Relayer::::put(&relayer); + let tx_hash: H256 = EthHash::from_low_u64_be(33); + let (event_id, source, destination, message) = + (1_u64, H160::from_low_u64_be(555), H160::from_low_u64_be(555), &[1_u8, 2, 3, 4, 5]); + let event_data = encode_event_message(event_id, source, destination, message); + MissedMessageIds::::put(vec![1]); + }: _(origin::(&relayer), tx_hash, event_data) + verify { + let process_at = >::block_number() + ChallengePeriod::::get(); + assert_eq!(MessagesValidAt::::get(process_at).into_inner(), [event_id]); + } + + submit_event { + let relayer = account::("//Alice"); + Relayer::::put(&relayer); + let tx_hash: H256 = EthHash::from_low_u64_be(33); + let (event_id, source, destination, message) = + (1_u64, H160::from_low_u64_be(555), H160::from_low_u64_be(555), &[1_u8, 2, 3, 4, 5]); + let event_data = encode_event_message(event_id, source, destination, message); + }: _(origin::(&relayer), tx_hash, event_data) + verify { + let process_at = >::block_number() + ChallengePeriod::::get(); + assert_eq!(MessagesValidAt::::get(process_at).into_inner(), [event_id]); + } + + submit_challenge { + let challenger = account::("//Bob"); + let relayer = account::("//Alice"); + Relayer::::put(&relayer); + assert_ok!(T::MultiCurrency::mint_into(T::NativeAssetId::get().into(), &challenger, T::ChallengeBond::get())); + + let tx_hash: H256 = EthHash::from_low_u64_be(33); + let (event_id, source, destination, message) = + (1_u64, H160::from_low_u64_be(555), H160::from_low_u64_be(555), &[1_u8, 2, 3, 4, 5]); + let event_data = encode_event_message(event_id, source, destination, message); + assert_ok!(EthBridge::::submit_event(origin::(&relayer).into(), tx_hash, event_data)); + }: _(origin::(&challenger), event_id) + verify { + assert_eq!( + PendingClaimStatus::::get(event_id), + Some(EventClaimStatus::Challenged) + ); + } + + submit_notarization { + let challenger = account::("//Bob"); + let relayer = account::("//Alice"); + assert_ok!(T::MultiCurrency::mint_into(T::NativeAssetId::get().into(), &relayer, T::RelayerBond::get())); + assert_ok!(EthBridge::::deposit_relayer_bond(origin::(&relayer).into())); + Relayer::::put(&relayer); + + let tx_hash: H256 = EthHash::from_low_u64_be(33); + let (event_id, source, destination, message) = + (1_u64, H160::from_low_u64_be(555), H160::from_low_u64_be(555), &[1_u8, 2, 3, 4, 5]); + let event_data = encode_event_message(event_id, source, destination, message); + + assert_ok!(T::MultiCurrency::mint_into(T::NativeAssetId::get().into(), &challenger, T::ChallengeBond::get())); + assert_ok!(EthBridge::::submit_event(origin::(&relayer).into(), tx_hash, event_data)); + assert_ok!(EthBridge::::submit_challenge(origin::(&challenger).into(), event_id)); + + let result = EventClaimResult::Valid; + let authority_index: u16 = 0; + let notary_key = T::EthyId::from_slice( + hex!("03e2161ca58ac2f2fa7dfd9f6980fdda1059b467e375ee78cdd5749dc058c0b2c9") + .as_slice(), + ).unwrap(); + let notary_keys = vec![notary_key.clone()]; + let notary_keys = WeakBoundedVec::try_from(notary_keys.clone()).unwrap(); + NotaryKeys::::put(notary_keys); + + let payload = NotarizationPayload::Event { event_claim_id: event_id, result, authority_index }; + let key = T::EthyId::generate_pair(None); + let signature = key.sign(&payload.encode()).unwrap(); + + }: _(RawOrigin::None, payload, signature) + verify { + assert_eq!(PendingClaimChallenges::::get(), vec![]); + } + + handle_authorities_change { + let notary_key = T::EthyId::from_slice( + hex!("03e2161ca58ac2f2fa7dfd9f6980fdda1059b467e375ee78cdd5749dc058c0b2c9") + .as_slice(), + ).unwrap(); + let notary_keys = vec![notary_key.clone()]; + let notary_keys = WeakBoundedVec::try_from(notary_keys.clone()).unwrap(); + NotaryKeys::::put(notary_keys); + + let next_notary_key = T::EthyId::from_slice( + hex!("04e2161ca58ac2f2fa7dfd9f6980fdda1059b467e375ee78cdd5749dc058c0b2c0") + .as_slice(), + ).unwrap(); + let next_notary_keys = vec![next_notary_key.clone()]; + let next_notary_keys = WeakBoundedVec::try_from(next_notary_keys.clone()).unwrap(); + NextNotaryKeys::::put(next_notary_keys); + + NextAuthorityChange::::put(T::BlockNumber::default()); + }: {crate::Pallet::::handle_authorities_change()} + verify { + assert!(EthBridge::::bridge_paused()); + assert_eq!(NextAuthorityChange::::get(), None); + } +} + +impl_benchmark_test_suite!( + EthBridge, + crate::mock::ExtBuilder::default().with_keystore().build(), + crate::mock::Test +); diff --git a/pallet/ethy/src/impls.rs b/pallet/ethy/src/impls.rs index 0669e81e0..0f93869ce 100644 --- a/pallet/ethy/src/impls.rs +++ b/pallet/ethy/src/impls.rs @@ -100,6 +100,115 @@ impl Pallet { paused_status.manual_pause || paused_status.authorities_change } + /// Prunes claim ids that are less than the max contiguous claim id. + pub fn prune_claim_ids(claim_ids: &mut Vec) -> Weight { + let mut used_weight = Weight::zero(); + // if < 1 element, nothing to do + if let 0..=1 = claim_ids.len() { + return used_weight + } + + // Keep the last MaxProcessedMessageIds elements in the list + let removed = claim_ids + .drain(..claim_ids.len().saturating_sub(T::MaxProcessedMessageIds::get() as usize)); + let removed: Vec = removed.collect(); + + // Check if we are aggressively pruning and event_ids that have not been processed + if !removed.is_empty() { + let mut missing_ids = MissedMessageIds::::get(); + // Add all missing ids from removed to missing + for id in removed[0]..claim_ids[0] { + if removed.contains(&id) { + continue + } + // Insert the missing ID from the removed list into the missing_ids list + if let Err(idx) = missing_ids.binary_search(&id) { + missing_ids.insert(idx, id); + } + } + MissedMessageIds::::put(missing_ids); + used_weight += DbWeight::get().reads_writes(1, 1); + } + + // get the index of the fist element that's non contiguous. + let first_noncontinuous_idx = claim_ids.iter().enumerate().position(|(i, &x)| { + if i > 0 { + x != claim_ids[i - 1] + 1 + } else { + false + } + }); + // drain the array from start to (first_noncontinuous_idx - 1) since we need the max + // contiguous element in the pruned vector. + match first_noncontinuous_idx { + Some(idx) => claim_ids.drain(..idx - 1), + None => claim_ids.drain(..claim_ids.len() - 1), // we need the last element to remain + }; + + used_weight + } + + /// Decode event data into it's respective parts. + pub fn decode_event_data( + tx_hash: H256, + event_data: Vec, + ) -> Result<(EventClaimId, EventClaim), DispatchError> { + // event SendMessage(uint256 messageId, address source, address destination, bytes + // message, uint256 fee); + if let [Token::Uint(event_id), Token::Address(source), Token::Address(destination), Token::Bytes(data), Token::Uint(_fee)] = + ethabi::decode( + &[ + ParamType::Uint(64), + ParamType::Address, + ParamType::Address, + ParamType::Bytes, + ParamType::Uint(64), + ], + event_data.as_slice(), + ) + .map_err(|_| Error::::InvalidClaim)? + .as_slice() + { + let event_id: EventClaimId = (*event_id).saturated_into(); + let data = BoundedVec::try_from(data.as_slice().to_vec()) + .map_err(|_| Error::::InvalidClaim)?; + let event_claim = EventClaim { + tx_hash, + source: *source, + destination: *destination, + data: data.clone(), + }; + return Ok((event_id, event_claim)) + } + Err(Error::::InvalidClaim.into()) + } + + // Store an event claim in the pallet to be processed after the ChallengePeriod + // Ensures the event is not already contained within PendingEventClaims + // Note. This function does not contain any replay protection logic + pub(crate) fn do_submit_event( + event_id: EventClaimId, + event_claim: EventClaim, + ) -> DispatchResult { + ensure!(!PendingEventClaims::::contains_key(event_id), Error::::EventReplayPending); + + let process_at: T::BlockNumber = + >::block_number() + ChallengePeriod::::get(); + MessagesValidAt::::try_mutate(process_at.clone(), |v| -> DispatchResult { + v.try_push(event_id).map_err(|_| Error::::MessageTooLarge)?; + Ok(()) + })?; + + PendingEventClaims::::insert(event_id, &event_claim); + PendingClaimStatus::::insert(event_id, EventClaimStatus::Pending); + Self::deposit_event(Event::::EventSubmit { + event_claim_id: event_id, + event_claim, + process_at, + }); + Ok(()) + } + pub fn update_xrpl_notary_keys(validator_list: &WeakBoundedVec) { let validators = Self::get_xrpl_notary_keys(&validator_list.clone().into_inner()); >::put(WeakBoundedVec::force_from( @@ -489,15 +598,14 @@ impl Pallet { } } - // Claim is invalid (nays > (100% - NotarizationThreshold)) if Percent::from_rational(nay_count, notary_count) > (Percent::from_parts(100_u8 - T::NotarizationThreshold::get().deconstruct())) { + // Claim is invalid (nays > (100% - NotarizationThreshold)) Self::handle_invalid_claim(event_claim_id)?; - } - - // Claim is valid - if Percent::from_rational(yay_count, notary_count) >= T::NotarizationThreshold::get() { + } else if Percent::from_rational(yay_count, notary_count) >= T::NotarizationThreshold::get() + { + // Claim is valid Self::handle_valid_claim(event_claim_id)?; } @@ -1059,27 +1167,3 @@ impl EthCallOracle for Pallet { call_id } } - -/// Prunes claim ids that are less than the max contiguous claim id. -pub(crate) fn prune_claim_ids(claim_ids: &mut Vec) { - // if < 1 element, nothing to do - if let 0..=1 = claim_ids.len() { - return - } - // sort first - claim_ids.sort(); - // get the index of the fist element that's non contiguous. - let first_noncontinuous_idx = claim_ids.iter().enumerate().position(|(i, &x)| { - if i > 0 { - x != claim_ids[i - 1] + 1 - } else { - false - } - }); - // drain the array from start to (first_noncontinuous_idx - 1) since we need the max contiguous - // element in the pruned vector. - match first_noncontinuous_idx { - Some(idx) => claim_ids.drain(..idx - 1), - None => claim_ids.drain(..claim_ids.len() - 1), // we need the last element to remain - }; -} diff --git a/pallet/ethy/src/lib.rs b/pallet/ethy/src/lib.rs index a3f62b794..c7acbf316 100644 --- a/pallet/ethy/src/lib.rs +++ b/pallet/ethy/src/lib.rs @@ -37,11 +37,11 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; -use ethabi::{ParamType, Token}; +use ethabi::ParamType; use frame_support::{ pallet_prelude::*, traits::{ - fungibles::Transfer, + fungibles::{Mutate, Transfer}, schedule::{Anon, DispatchTime}, UnixTime, ValidatorSet as ValidatorSetT, }, @@ -59,7 +59,7 @@ use seed_primitives::{AssetId, Balance}; use sp_core::bounded::WeakBoundedVec; use sp_runtime::{ offchain as rt_offchain, - traits::{MaybeSerializeDeserialize, Member, SaturatedConversion}, + traits::{MaybeSerializeDeserialize, Member}, Percent, RuntimeAppPublic, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; @@ -67,14 +67,20 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; mod ethereum_http_cli; pub use ethereum_http_cli::EthereumRpcClient; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod impls; #[cfg(test)] mod mock; #[cfg(test)] mod tests; + mod types; pub use types::*; +pub mod weights; +pub use weights::WeightInfo; + /// The type to sign and send transactions. const UNSIGNED_TXS_PRIORITY: u64 = 100; /// Max notarization claims to attempt per block/OCW invocation @@ -174,7 +180,9 @@ pub mod pallet { #[pallet::constant] type MaxNewSigners: Get; /// Handles a multi-currency fungible asset system - type MultiCurrency: Transfer + Hold; + type MultiCurrency: Transfer + + Hold + + Mutate; /// The native token asset Id (managed by pallet-balances) #[pallet::constant] type NativeAssetId: Get; @@ -188,6 +196,10 @@ pub mod pallet { type Scheduler: Anon::RuntimeCall, Self::PalletsOrigin>; /// Overarching type of all pallets origins. type PalletsOrigin: From>; + /// Maximum number of processed message Ids that will we keep as a buffer to prevent + /// replays. + #[pallet::constant] + type MaxProcessedMessageIds: Get; /// Returns the block timestamp type UnixTime: UnixTime; /// Max Xrpl notary (validator) public keys @@ -210,6 +222,8 @@ pub mod pallet { /// Maximum number of Eth Call Requests #[pallet::constant] type MaxCallRequests: Get; + /// Provides the public call to weight mapping + type WeightInfo: WeightInfo; } /// Flag to indicate whether authorities have been changed during the current era @@ -332,7 +346,13 @@ pub mod pallet { /// Tracks processed message Ids (prevent replay) /// Must remain unbounded as this list will grow indefinitely #[pallet::storage] - pub type ProcessedMessageIds = StorageValue<_, Vec, ValueQuery>; + pub type ProcessedMessageIds = + StorageValue<_, BoundedVec, ValueQuery>; + + /// Tracks message Ids that are outside of the MessageId buffer and were not processed + /// These message Ids can be either processed or cleared by the relayer + #[pallet::storage] + pub type MissedMessageIds = StorageValue<_, Vec, ValueQuery>; /// The block in which we process the next authority change #[pallet::storage] @@ -446,6 +466,8 @@ pub mod pallet { ChallengePeriodSet { period: T::BlockNumber }, /// The bridge has been manually paused or unpaused BridgeManualPause { paused: bool }, + /// A range of missing event Ids were removed + MissingEventIdsRemoved { range: (EventClaimId, EventClaimId) }, } #[pallet::error] @@ -500,23 +522,34 @@ pub mod pallet { /// 3) Process any deferred event proofs that were submitted while the bridge was paused /// (should only happen on the first few blocks in a new era) (outgoing) fn on_initialize(block_number: T::BlockNumber) -> Weight { - let mut consumed_weight = Weight::zero(); + // Reads: NextAuthorityChange, MessagesValidAt, ProcessedMessageIds + let mut consumed_weight = DbWeight::get().reads(3u64); // 1) Handle authority change if Some(block_number) == NextAuthorityChange::::get() { // Change authority keys, we are 5 minutes before the next epoch log!(trace, "💎 Epoch ends in 5 minutes, changing authorities"); Self::handle_authorities_change(); + consumed_weight = + consumed_weight.saturating_add(T::WeightInfo::handle_authorities_change()); } // 2) Process validated messages // Removed message_id from MessagesValidAt and processes - let mut processed_message_ids = ProcessedMessageIds::::get(); + let mut processed_message_ids = ProcessedMessageIds::::get().into_inner(); + let mut message_processed: bool = false; for message_id in MessagesValidAt::::take(block_number) { + // reads: PendingClaimStatus, PendingEventClaims + // writes: PendingClaimStatus, PendingEventClaims + consumed_weight = + consumed_weight.saturating_add(DbWeight::get().reads_writes(2_u64, 2_u64)); if PendingClaimStatus::::get(message_id) == Some(EventClaimStatus::Challenged) { // We are still waiting on the challenge to be processed, push out by challenge // period let new_process_at = block_number + ChallengePeriod::::get(); + // read + write: MessagesValidAt + consumed_weight = + consumed_weight.saturating_add(DbWeight::get().reads_writes(1_u64, 1_u64)); MessagesValidAt::::mutate(new_process_at.clone(), |v| { let mut message_ids = v.clone().into_inner(); message_ids.push(message_id); @@ -556,31 +589,81 @@ pub mod pallet { }, } } - // mark as processed - if let Err(idx) = processed_message_ids.binary_search(&message_id) { - processed_message_ids.insert(idx, message_id); + + let first_processed = processed_message_ids.first().cloned().unwrap_or_default(); + // Is this message_id within the MaxProcessedMessageIds? + if message_id >= first_processed { + // mark as processed + if let Err(idx) = processed_message_ids.binary_search(&message_id) { + processed_message_ids.insert(idx, message_id); + } + } else { + // REMOVE event_id to MissedMessageIds + // read + write: MissedMessageIds + consumed_weight = + consumed_weight.saturating_add(DbWeight::get().reads_writes(1_u64, 1_u64)); + MissedMessageIds::::mutate(|missed_message_ids| { + if let Ok(idx) = missed_message_ids.binary_search(&message_id) { + missed_message_ids.remove(idx); + } + }); } + // Tidy up status check PendingClaimStatus::::remove(message_id); + message_processed = true; } - if !processed_message_ids.is_empty() { - impls::prune_claim_ids(&mut processed_message_ids); + + if message_processed && !processed_message_ids.is_empty() { + // write: ProcessedMessageIds + consumed_weight = consumed_weight.saturating_add(DbWeight::get().writes(1_u64)); + let prune_weight = Self::prune_claim_ids(&mut processed_message_ids); + consumed_weight = consumed_weight.saturating_add(prune_weight); + // Truncate is safe as the length is asserted within prune_claim_ids + let processed_message_ids = BoundedVec::truncate_from(processed_message_ids); ProcessedMessageIds::::put(processed_message_ids); } - // 3) Try process delayed proofs - consumed_weight += DbWeight::get().reads(2u64); - if !Self::bridge_paused() && PendingEventProofs::::iter().next().is_some() { - let max_delayed_events = DelayedEventProofsPerBlock::::get(); - consumed_weight = consumed_weight.saturating_add(DbWeight::get().reads(1u64)); - consumed_weight = consumed_weight - .saturating_add(DbWeight::get().writes(2u64).mul(max_delayed_events as u64)); - for (event_proof_id, signing_request) in - PendingEventProofs::::iter().take(max_delayed_events as usize) - { - Self::do_request_event_proof(event_proof_id, signing_request); - PendingEventProofs::::remove(event_proof_id); + consumed_weight + } + + fn on_idle(_n: T::BlockNumber, remaining_weight: Weight) -> Weight { + // Minimum weight to read the initial values: + // - BridgePaused, PendingEventProofs, DelayedEventProofsPerBlock + let base_weight = DbWeight::get().reads(3u64); + + // do_request_proof weight: + // reads - BridgePaused + // writes - PendingEventProofs || frame_system::Digest + // loop weight: + // reads_writes - PendingEventProofs + let weight_per_proof = DbWeight::get().reads_writes(2, 2); + + // Do we have enough weight to process one proof? + if remaining_weight.all_lte(base_weight + weight_per_proof) { + return Weight::zero() + } + + // Don't do anything if the bridge is paused + if Self::bridge_paused() { + return DbWeight::get().reads(1u64) + } + + let mut consumed_weight = base_weight; + let mut pending_event_proofs = PendingEventProofs::::drain(); + let max_delayed_events = DelayedEventProofsPerBlock::::get(); + + for _ in 0..max_delayed_events { + // Check if we have enough weight to process this iteration + let new_consumed_weight = consumed_weight.saturating_add(weight_per_proof); + if remaining_weight.all_lte(new_consumed_weight) { + break } + let Some((event_proof_id, signing_request)) = pending_event_proofs.next() else { + break + }; + consumed_weight = new_consumed_weight; + Self::do_request_event_proof(event_proof_id, signing_request); } consumed_weight @@ -626,7 +709,7 @@ pub mod pallet { #[pallet::call] impl Pallet { /// Set new XRPL door signers - #[pallet::weight(DbWeight::get().writes(new_signers.len() as u64).saturating_add(DbWeight::get().reads_writes(4, 3)))] + #[pallet::weight(T::WeightInfo::set_xrpl_door_signers(new_signers.len() as u32))] pub fn set_xrpl_door_signers( origin: OriginFor, new_signers: Vec<(T::EthyId, bool)>, @@ -647,7 +730,7 @@ pub mod pallet { } /// Set the relayer address - #[pallet::weight(DbWeight::get().writes(1))] + #[pallet::weight(T::WeightInfo::set_relayer())] pub fn set_relayer(origin: OriginFor, relayer: T::AccountId) -> DispatchResult { ensure_root(origin)?; // Ensure relayer has bonded more than relayer bond amount @@ -661,7 +744,7 @@ pub mod pallet { } /// Submit bond for relayer account - #[pallet::weight(DbWeight::get().reads_writes(5, 6))] + #[pallet::weight(T::WeightInfo::deposit_relayer_bond())] pub fn deposit_relayer_bond(origin: OriginFor) -> DispatchResult { let origin = ensure_signed(origin)?; @@ -685,7 +768,7 @@ pub mod pallet { } /// Withdraw relayer bond amount - #[pallet::weight(DbWeight::get().reads_writes(3, 3))] + #[pallet::weight(T::WeightInfo::withdraw_relayer_bond())] pub fn withdraw_relayer_bond(origin: OriginFor) -> DispatchResult { let origin = ensure_signed(origin)?; @@ -714,7 +797,7 @@ pub mod pallet { /// Set event confirmations (blocks). Required block confirmations for an Ethereum event to /// be notarized by Seed - #[pallet::weight(DbWeight::get().writes(1))] + #[pallet::weight(T::WeightInfo::set_event_block_confirmations())] pub fn set_event_block_confirmations( origin: OriginFor, confirmations: u64, @@ -726,7 +809,7 @@ pub mod pallet { } /// Set max number of delayed events that can be processed per block - #[pallet::weight(DbWeight::get().writes(1))] + #[pallet::weight(T::WeightInfo::set_delayed_event_proofs_per_block())] pub fn set_delayed_event_proofs_per_block( origin: OriginFor, count: u8, @@ -739,7 +822,7 @@ pub mod pallet { /// Set challenge period, this is the window in which an event can be challenged before /// processing - #[pallet::weight(DbWeight::get().writes(1))] + #[pallet::weight(T::WeightInfo::set_challenge_period())] pub fn set_challenge_period( origin: OriginFor, blocks: T::BlockNumber, @@ -751,7 +834,7 @@ pub mod pallet { } /// Set the bridge contract address on Ethereum (requires governance) - #[pallet::weight(DbWeight::get().writes(1))] + #[pallet::weight(T::WeightInfo::set_contract_address())] pub fn set_contract_address( origin: OriginFor, contract_address: EthAddress, @@ -763,7 +846,7 @@ pub mod pallet { } /// Pause or unpause the bridge (requires governance) - #[pallet::weight(DbWeight::get().writes(1))] + #[pallet::weight(T::WeightInfo::set_bridge_paused())] pub fn set_bridge_paused(origin: OriginFor, paused: bool) -> DispatchResult { ensure_root(origin)?; BridgePaused::::mutate(|p| p.manual_pause = paused); @@ -773,7 +856,7 @@ pub mod pallet { /// Finalise authority changes, unpauses bridge and sets new notary keys /// Called internally after force new era - #[pallet::weight(DbWeight::get().writes(1))] + #[pallet::weight(T::WeightInfo::finalise_authorities_change())] pub fn finalise_authorities_change( origin: OriginFor, next_notary_keys: WeakBoundedVec, @@ -783,85 +866,83 @@ pub mod pallet { Ok(()) } + /// Admin function to manually remove an event_id from MissedMessageIds + /// This should only be used if the event_id is confirmed to be invalid + /// event_id_range is the lower and upper event_ids to clear (Both Inclusive) + #[pallet::weight(T::WeightInfo::remove_missing_event_id())] + pub fn remove_missing_event_id( + origin: OriginFor, + event_id_range: (EventClaimId, EventClaimId), + ) -> DispatchResult { + ensure_root(origin)?; + let mut missed_message_ids = MissedMessageIds::::get(); + missed_message_ids = missed_message_ids + .into_iter() + .filter(|id| *id < event_id_range.0 || *id > event_id_range.1) + .collect::>(); + MissedMessageIds::::put(missed_message_ids); + Self::deposit_event(Event::::MissingEventIdsRemoved { range: event_id_range }); + Ok(()) + } + /// Submit ABI encoded event data from the Ethereum bridge contract + /// Used to recover events that were pruned but not handled by the pruning algorithn, + /// Only events contained within MissedMessageIds can be processed here /// - tx_hash The Ethereum transaction hash which triggered the event /// - event ABI encoded bridge event - #[pallet::weight(DbWeight::get().writes(1))] - pub fn submit_event(origin: OriginFor, tx_hash: H256, event: Vec) -> DispatchResult { + #[pallet::weight(T::WeightInfo::submit_missing_event())] + #[transactional] + pub fn submit_missing_event( + origin: OriginFor, + tx_hash: H256, + event: Vec, + ) -> DispatchResult { let origin = ensure_signed(origin)?; + ensure!(Some(origin) == Relayer::::get(), Error::::NoPermission); + + let (event_id, event_claim) = Self::decode_event_data(tx_hash, event)?; + + // Ensure the message Id is contained within missed message ids + // This is to handle the case where a message ID was pruned but not processed + let missed_message_ids: Vec = MissedMessageIds::::get(); + ensure!( + missed_message_ids.binary_search(&event_id).is_ok(), + Error::::EventReplayProcessed + ); + + Self::do_submit_event(event_id, event_claim)?; + Ok(()) + } + /// Submit ABI encoded event data from the Ethereum bridge contract + /// - tx_hash The Ethereum transaction hash which triggered the event + /// - event ABI encoded bridge event + #[pallet::weight(T::WeightInfo::submit_event())] + pub fn submit_event(origin: OriginFor, tx_hash: H256, event: Vec) -> DispatchResult { + let origin = ensure_signed(origin)?; ensure!(Some(origin) == Relayer::::get(), Error::::NoPermission); - // TODO: place some limit on `data` length (it should match on contract side) - // event SendMessage(uint256 messageId, address source, address destination, bytes - // message, uint256 fee); - if let [Token::Uint(event_id), Token::Address(source), Token::Address(destination), Token::Bytes(data), Token::Uint(_fee)] = - ethabi::decode( - &[ - ParamType::Uint(64), - ParamType::Address, - ParamType::Address, - ethabi::ParamType::Bytes, - ParamType::Uint(64), - ], - event.as_slice(), - ) - .map_err(|_| Error::::InvalidClaim)? - .as_slice() - { - let event_id: EventClaimId = (*event_id).saturated_into(); + let (event_id, event_claim) = Self::decode_event_data(tx_hash, event)?; + + // Verify that the event_id is not contained within ProcessedMessageIds + // to prevent replay + let processed_message_ids: Vec = + ProcessedMessageIds::::get().into_inner(); + if !processed_message_ids.is_empty() { ensure!( - !PendingEventClaims::::contains_key(event_id), - Error::::EventReplayPending + event_id > processed_message_ids[0] && + processed_message_ids.binary_search(&event_id).is_err(), + Error::::EventReplayProcessed ); - if !ProcessedMessageIds::::get().is_empty() { - ensure!( - event_id > ProcessedMessageIds::::get()[0] && - ProcessedMessageIds::::get().binary_search(&event_id).is_err(), - Error::::EventReplayProcessed - ); - } - let data = BoundedVec::try_from(data.as_slice().to_vec()) - .map_err(|_| Error::::InvalidClaim)?; - let event_claim = EventClaim { - tx_hash, - source: *source, - destination: *destination, - data: data.clone(), - }; - - PendingEventClaims::::insert(event_id, &event_claim); - PendingClaimStatus::::insert(event_id, EventClaimStatus::Pending); - - // TODO: there should be some limit per block - let process_at: T::BlockNumber = - >::block_number() + ChallengePeriod::::get(); - MessagesValidAt::::mutate(process_at.clone(), |v| { - let mut message_ids = v.clone().into_inner(); - message_ids.push(event_id); - let message_ids_bounded = WeakBoundedVec::force_from( - message_ids, - Some( - "Warning: There are more MessagesValidAt than expected. \ - A runtime configuration adjustment may be needed.", - ), - ); - *v = message_ids_bounded; - }); - - Self::deposit_event(Event::::EventSubmit { - event_claim_id: event_id, - event_claim, - process_at, - }); } + Self::do_submit_event(event_id, event_claim)?; Ok(()) } /// Submit a challenge for an event /// Challenged events won't be processed until verified by validators /// An event can only be challenged once - #[pallet::weight(DbWeight::get().writes(1) + DbWeight::get().reads(2))] + #[pallet::weight(T::WeightInfo::submit_challenge())] #[transactional] pub fn submit_challenge( origin: OriginFor, @@ -900,7 +981,7 @@ pub mod pallet { /// Internal only /// Validators will submit inherents with their notarization vote for a given claim - #[pallet::weight(1_000_000)] + #[pallet::weight(T::WeightInfo::submit_notarization())] #[transactional] pub fn submit_notarization( origin: OriginFor, diff --git a/pallet/ethy/src/mock.rs b/pallet/ethy/src/mock.rs index d2249a9ab..c521f30b1 100644 --- a/pallet/ethy/src/mock.rs +++ b/pallet/ethy/src/mock.rs @@ -87,6 +87,7 @@ parameter_types! { pub const MaxChallenges: u32 = 100; pub const MaxMessagesPerBlock: u32 = 1000; pub const MaxCallRequests: u32 = 1000; + pub const MaxProcessedMessageIds: u32 = 10; } impl Config for Test { type AuthorityChangeDelay = AuthorityChangeDelay; @@ -116,6 +117,8 @@ impl Config for Test { type MaxChallenges = MaxChallenges; type MaxMessagesPerBlock = MaxMessagesPerBlock; type MaxCallRequests = MaxCallRequests; + type WeightInfo = (); + type MaxProcessedMessageIds = MaxProcessedMessageIds; } pub struct MockXrplBridgeAdapter; diff --git a/pallet/ethy/src/tests.rs b/pallet/ethy/src/tests.rs index 585c2742b..bed575a2f 100644 --- a/pallet/ethy/src/tests.rs +++ b/pallet/ethy/src/tests.rs @@ -15,7 +15,6 @@ #![cfg(test)] use crate::{ - impls::prune_claim_ids, mock::*, types::{ BridgePauseStatus, CheckedEthCallRequest, CheckedEthCallResult, EthAddress, EthBlock, @@ -24,15 +23,15 @@ use crate::{ }, AuthoritiesChangedThisEra, BridgePaused, ChallengePeriod, ChallengerAccount, Config, ContractAddress, DelayedEventProofsPerBlock, Error, EthCallNotarizationsAggregated, - EthCallRequestInfo, Event, EventClaimStatus, MessagesValidAt, NextAuthorityChange, - NextEventProofId, NextNotaryKeys, NotaryKeys, NotarySetId, NotarySetProofId, NotaryXrplKeys, - Pallet, PendingClaimChallenges, PendingClaimStatus, PendingEventClaims, PendingEventProofs, - ProcessedMessageIds, Relayer, RelayerPaidBond, XrplDoorSigners, XrplNotarySetProofId, - ETHY_ENGINE_ID, SUBMIT_BRIDGE_EVENT_SELECTOR, + EthCallRequestInfo, Event, EventClaimStatus, MessagesValidAt, MissedMessageIds, + NextAuthorityChange, NextEventProofId, NextNotaryKeys, NotaryKeys, NotarySetId, + NotarySetProofId, NotaryXrplKeys, Pallet, PendingClaimChallenges, PendingClaimStatus, + PendingEventClaims, PendingEventProofs, ProcessedMessageIds, Relayer, RelayerPaidBond, + XrplDoorSigners, XrplNotarySetProofId, ETHY_ENGINE_ID, SUBMIT_BRIDGE_EVENT_SELECTOR, }; use codec::Encode; use ethabi::Token; -use frame_support::traits::{fungibles::Inspect, OnInitialize, OneSessionHandler, UnixTime}; +use frame_support::traits::{fungibles::Inspect, Hooks, OneSessionHandler, UnixTime}; use hex_literal::hex; use seed_pallet_common::test_prelude::*; use seed_primitives::{ @@ -229,6 +228,180 @@ fn submit_event_tracks_completed() { }); } +#[test] +fn submit_missing_event_id_works() { + let relayer = H160::from_low_u64_be(123); + ExtBuilder::default().relayer(relayer).build().execute_with(|| { + let missed_event_id: u64 = 3; + // Setup storage to simulate missed_event_id being invalid but stored in Missing + ProcessedMessageIds::::put(BoundedVec::truncate_from(vec![missed_event_id + 1])); + MissedMessageIds::::put(vec![missed_event_id]); + let tx_hash = EthHash::from_low_u64_be(33); + let (source, destination, message) = + (H160::from_low_u64_be(555), H160::from_low_u64_be(555), &[1_u8, 2, 3, 4, 5]); + let event_item_data = encode_event_message(missed_event_id, source, destination, message); + + // Submit event should fail due to replay protection + assert_noop!( + EthBridge::submit_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_item_data.clone(), + ), + Error::::EventReplayProcessed + ); + + // Submit missing event should work as event id is stored within MissedMessageIds + assert_ok!(EthBridge::submit_missing_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_item_data.clone(), + )); + + assert_eq!( + PendingEventClaims::::get(missed_event_id), + Some(EventClaim { + tx_hash, + source, + destination, + data: BoundedVec::truncate_from(message.to_vec()) + }) + ); + + let process_at = System::block_number() + ChallengePeriod::::get(); + EthBridge::on_initialize(process_at); + // ProcessedMessageIds should be unchanged (As it was before the lowest + assert_eq!(ProcessedMessageIds::::get().into_inner(), vec![missed_event_id + 1]); + assert!(MissedMessageIds::::get().is_empty()); + + // Submit missing event should now not work as the event is processed + assert_noop!( + EthBridge::submit_missing_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_item_data.clone(), + ), + Error::::EventReplayProcessed + ); + }); +} + +#[test] +fn submit_missing_event_id_prevents_replay() { + let relayer = H160::from_low_u64_be(123); + ExtBuilder::default().relayer(relayer).build().execute_with(|| { + let missed_event_id: u64 = 3; + // Setup storage to simulate missed_event_id being invalid but stored in Missing + ProcessedMessageIds::::put(BoundedVec::truncate_from(vec![missed_event_id + 1])); + MissedMessageIds::::put(vec![missed_event_id]); + let tx_hash = EthHash::from_low_u64_be(33); + let (source, destination, message) = + (H160::from_low_u64_be(555), H160::from_low_u64_be(555), &[1_u8, 2, 3, 4, 5]); + let event_item_data = encode_event_message(missed_event_id, source, destination, message); + + // Submit missing event should work as event id is stored within MissedMessageIds + assert_ok!(EthBridge::submit_missing_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_item_data.clone(), + )); + + // Submit missing event should now not work as the event is pending + assert_noop!( + EthBridge::submit_missing_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_item_data.clone(), + ), + Error::::EventReplayPending + ); + }); +} + +#[test] +fn submit_missing_event_id_fails_if_not_in_missing() { + let relayer = H160::from_low_u64_be(123); + ExtBuilder::default().relayer(relayer).build().execute_with(|| { + let missed_event_id: u64 = 3; + // Setup storage to simulate missed_event_id not in missing (i.e. processed + ProcessedMessageIds::::put(BoundedVec::truncate_from(vec![5])); + MissedMessageIds::::put(vec![2, 4]); + let tx_hash = EthHash::from_low_u64_be(33); + let (source, destination, message) = + (H160::from_low_u64_be(555), H160::from_low_u64_be(555), &[1_u8, 2, 3, 4, 5]); + let event_item_data = encode_event_message(missed_event_id, source, destination, message); + + // Submit missing event should not work as it is not in MissedMessageIds + assert_noop!( + EthBridge::submit_missing_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_item_data.clone(), + ), + Error::::EventReplayProcessed + ); + + assert_noop!( + EthBridge::submit_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_item_data.clone(), + ), + Error::::EventReplayProcessed + ); + }); +} + +#[test] +fn remove_missing_event_id_works() { + ExtBuilder::default().build().execute_with(|| { + let event_ids = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; + MissedMessageIds::::put(event_ids); + + let range = (3, 6); + assert_ok!(EthBridge::remove_missing_event_id(RuntimeOrigin::root(), range)); + let event_ids = vec![1, 2, 7, 8, 9]; + assert_eq!(MissedMessageIds::::get(), event_ids); + }); +} + +#[test] +fn remove_missing_event_id_single_id() { + ExtBuilder::default().build().execute_with(|| { + let event_ids = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; + MissedMessageIds::::put(event_ids); + + let range = (3, 3); // Remove one ID + assert_ok!(EthBridge::remove_missing_event_id(RuntimeOrigin::root(), range)); + let event_ids = vec![1, 2, 4, 5, 6, 7, 8, 9]; + assert_eq!(MissedMessageIds::::get(), event_ids); + }); +} + +#[test] +fn remove_missing_event_id_outside_range() { + ExtBuilder::default().build().execute_with(|| { + let event_ids = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; + MissedMessageIds::::put(&event_ids); + + let range = (10, 1000000000); // Outside range is noop + assert_ok!(EthBridge::remove_missing_event_id(RuntimeOrigin::root(), range)); + assert_eq!(MissedMessageIds::::get(), event_ids); + }); +} + +#[test] +fn remove_missing_event_id_full_range() { + ExtBuilder::default().build().execute_with(|| { + let event_ids = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; + MissedMessageIds::::put(&event_ids); + + let range = (1, 9); // Full range of ids + assert_ok!(EthBridge::remove_missing_event_id(RuntimeOrigin::root(), range)); + assert!(MissedMessageIds::::get().is_empty()); + }); +} + #[test] fn set_relayer_no_bond_should_fail() { let relayer = H160::from_low_u64_be(123); @@ -630,11 +803,10 @@ fn process_valid_challenged_event() { ); assert_eq!(MessagesValidAt::::get(process_at), vec![event_id_1]); - // Weight returned should include the 1000 that we specified in our mock - assert_eq!( - EthBridge::on_initialize(process_at), - DbWeight::get().reads(2u64) + Weight::from_ref_time(1000u64) - ); + // Weight returned should include the 1000 that we specified in MockEventRouter + let expected_weight = + DbWeight::get().reads_writes(5u64, 3u64) + Weight::from_ref_time(1000u64); + assert_eq!(EthBridge::on_initialize(process_at), expected_weight); // Storage should now be fully cleared assert!(PendingClaimChallenges::::get().is_empty()); @@ -643,7 +815,7 @@ fn process_valid_challenged_event() { assert!(PendingClaimStatus::::get(event_id_1).is_none()); assert!(MessagesValidAt::::get(process_at).is_empty()); // The event is processed! - assert_eq!(ProcessedMessageIds::::get(), vec![event_id_1]); + assert_eq!(ProcessedMessageIds::::get().into_inner(), vec![event_id_1]); }); } @@ -693,9 +865,10 @@ fn process_valid_challenged_event_delayed() { assert_eq!(MessagesValidAt::::get(process_at), vec![event_id_1]); - // Weight returned should not include the 1000 that we specified in our mock as a + // Weight returned should not include the 1000 that we specified in MockEventRouter as a // consensus has not been reached - assert_eq!(EthBridge::on_initialize(process_at), DbWeight::get().reads(2u64)); + let expected_weight = DbWeight::get().reads_writes(6u64, 3u64); + assert_eq!(EthBridge::on_initialize(process_at), expected_weight); assert_eq!(MessagesValidAt::::get(process_at_extended), vec![event_id_1]); assert!(MessagesValidAt::::get(process_at).is_empty()); @@ -725,11 +898,10 @@ fn process_valid_challenged_event_delayed() { ); assert_eq!(MessagesValidAt::::get(process_at_extended), vec![event_id_1]); - // Weight returned should include the 1000 that we specified in our mock - assert_eq!( - EthBridge::on_initialize(process_at_extended), - DbWeight::get().reads(2u64) + Weight::from_ref_time(1000u64) - ); + // Weight returned should include the 1000 that we specified in MockEventRouter + let expected_weight = + DbWeight::get().reads_writes(5u64, 3u64) + Weight::from_ref_time(1000u64); + assert_eq!(EthBridge::on_initialize(process_at_extended), expected_weight); // Storage should now be fully cleared assert!(PendingClaimChallenges::::get().is_empty()); @@ -738,7 +910,7 @@ fn process_valid_challenged_event_delayed() { assert!(PendingClaimStatus::::get(event_id_1).is_none()); assert!(MessagesValidAt::::get(process_at_extended).is_empty()); // The event is processed! - assert_eq!(ProcessedMessageIds::::get(), vec![event_id_1]); + assert_eq!(ProcessedMessageIds::::get().into_inner(), vec![event_id_1]); }); } @@ -998,7 +1170,7 @@ fn on_new_session_updates_keys() { let event_proof_id = NextEventProofId::::get(); let next_validator_set_id = NotarySetId::::get() + 1; - // Now call on_initialise with the expected block to check it gets processed correctly + // Now call on_initialize with the expected block to check it gets processed correctly EthBridge::on_initialize(expected_block.into()); // Storage updated @@ -1396,7 +1568,7 @@ fn send_event() { // On initialize does up to 2 reads to check for delayed proofs assert_eq!( EthBridge::on_initialize(frame_system::Pallet::::block_number() + 1), - DbWeight::get().reads(2u64) + DbWeight::get().reads(3u64) ); }); } @@ -1489,12 +1661,13 @@ fn delayed_event_proof() { // Re-enable bridge assert_ok!(EthBridge::set_bridge_paused(frame_system::RawOrigin::Root.into(), false)); // initialize pallet and initiate event proof - let expected_weight: Weight = DbWeight::get().reads(3u64) + DbWeight::get().writes(2u64); - let max_delayed_events = DelayedEventProofsPerBlock::::get() as u64; let expected_weight: Weight = - DbWeight::get().reads(3u64) + DbWeight::get().writes(2u64) * max_delayed_events; + DbWeight::get().reads(3u64) + DbWeight::get().reads_writes(2, 2); assert_eq!( - EthBridge::on_initialize(frame_system::Pallet::::block_number() + 1), + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + Weight::from_ref_time(1_000_000_000_000) + ), expected_weight ); // Ensure event has been removed from delayed claims @@ -1561,8 +1734,11 @@ fn delayed_event_proof_updates_validator_set_id_on_last_minute_authorities_chang assert_eq!(NotarySetId::::get(), next_validator_set_id); assert!(!EthBridge::bridge_paused()); - // initialize pallet and initiate event proof - EthBridge::on_initialize(frame_system::Pallet::::block_number() + 1); + // Call on_idle and initiate event proof + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + Weight::from_ref_time(1_000_000_000_000), + ); // Ensure event has been removed from delayed claims assert!(PendingEventProofs::::get(event_proof_id).is_none()); @@ -1609,7 +1785,6 @@ fn delayed_event_proof_updates_validator_set_id_on_normal_authorities_change() { ); assert_eq!(NextNotaryKeys::::get(), next_keys.clone()); - let event_proof_id = NextEventProofId::::get(); // Now call on_initialise with the expected block to check it gets processed correctly let expected_block: BlockNumber = NextAuthorityChange::::get().unwrap(); EthBridge::on_initialize(expected_block.into()); @@ -1643,8 +1818,11 @@ fn delayed_event_proof_updates_validator_set_id_on_normal_authorities_change() { assert!(!EthBridge::bridge_paused()); assert_eq!(NotarySetId::::get(), next_validator_set_id); - // initialize pallet and initiate event proof - EthBridge::on_initialize(frame_system::Pallet::::block_number() + 1); + // call on_idle and initiate event proof + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + Weight::from_ref_time(1_000_000_000_000), + ); // Ensure event has been removed from delayed claims assert!(PendingEventProofs::::get(event_proof_id).is_none()); @@ -1696,10 +1874,15 @@ fn multiple_delayed_event_proof() { // Re-enable bridge assert_ok!(EthBridge::set_bridge_paused(frame_system::RawOrigin::Root.into(), false)); - // initialize pallet and initiate event proof + // Call on_idle to process half of the pending event proofs + let expected_weight: Weight = DbWeight::get().reads(3u64) + + DbWeight::get().reads_writes(2, 2) * max_delayed_events as u64; assert_eq!( - EthBridge::on_initialize(frame_system::Pallet::::block_number() + 1), - DbWeight::get().reads(3u64) + DbWeight::get().writes(2u64) * max_delayed_events as u64 + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + Weight::from_ref_time(1_000_000_000_000) + ), + expected_weight ); let mut removed_count = 0; @@ -1717,10 +1900,65 @@ fn multiple_delayed_event_proof() { // Should have only processed max amount assert_eq!(removed_count, max_delayed_events); - // Now initialize next block and process the rest + // Calling on_idle should process the rest + assert_eq!( + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + Weight::from_ref_time(1_000_000_000_000) + ), + expected_weight + ); + + // All events should have now been processed + for i in 0..event_count { + assert!(PendingEventProofs::::get(event_ids[i as usize]).is_none()); + } + }); +} + +#[test] +fn on_idle_limits_processing() { + ExtBuilder::default().build().execute_with(|| { + let message = &b"hello world"[..]; + let source = H160::from_low_u64_be(444); + let destination = H160::from_low_u64_be(555); + assert_ok!(EthBridge::set_bridge_paused(frame_system::RawOrigin::Root.into(), true)); + + let max_delayed_events = DelayedEventProofsPerBlock::::get(); + let event_count: u8 = max_delayed_events; + let mut event_ids: Vec = vec![]; + let mut events_for_proving = vec![]; + for _ in 0..event_count { + let event_proof_id = NextEventProofId::::get(); + event_ids.push(event_proof_id); + let event_proof_info = EthySigningRequest::Ethereum(EthereumEventInfo { + source, + destination: destination.clone(), + message: BoundedVec::truncate_from(message.to_vec()), + validator_set_id: EthBridge::validator_set().id, + event_proof_id, + }); + events_for_proving.push(event_proof_info.clone()); + // Generate event proof + assert_ok!(EthBridge::send_event(&source, &destination, &message)); + // Ensure event has been added to delayed claims + assert_eq!(PendingEventProofs::::get(event_proof_id), Some(event_proof_info)); + assert_eq!(NextEventProofId::::get(), event_proof_id + 1); + } + + // Re-enable bridge + assert_ok!(EthBridge::set_bridge_paused(frame_system::RawOrigin::Root.into(), false)); + + // Call on_idle with only enough weight to process 2 claims + let claims_to_process = 2; + let expected_weight: Weight = DbWeight::get().reads(3u64) + + DbWeight::get().reads_writes(2, 2) * claims_to_process as u64; assert_eq!( - EthBridge::on_initialize(frame_system::Pallet::::block_number() + 2), - DbWeight::get().reads(3u64) + DbWeight::get().writes(2u64) * max_delayed_events as u64 + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + expected_weight + Weight::from_ref_time(1) + ), + expected_weight ); let mut removed_count = 0; @@ -1728,10 +1966,79 @@ fn multiple_delayed_event_proof() { // Ensure event has been removed from delayed claims if PendingEventProofs::::get(event_ids[i as usize]).is_none() { removed_count += 1; + } else { + assert_eq!( + PendingEventProofs::::get(event_ids[i as usize]), + Some(events_for_proving[i as usize].clone()) + ) } } + // Should have only processed max amount + assert_eq!(removed_count, claims_to_process); + + // Call on_idle with enough weight to process the rest + let claims_to_process = event_count - claims_to_process; + let expected_weight: Weight = DbWeight::get().reads(3u64) + + DbWeight::get().reads_writes(2, 2) * claims_to_process as u64; + assert_eq!( + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + Weight::from_ref_time(1_000_000_000_000) + ), + expected_weight + ); + // All events should have now been processed - assert_eq!(removed_count, event_count); + for i in 0..event_count { + assert!(PendingEventProofs::::get(event_ids[i as usize]).is_none()); + } + }); +} + +#[test] +fn on_idle_no_remaining_weight_is_noop() { + ExtBuilder::default().build().execute_with(|| { + let message = &b"hello world"[..]; + let source = H160::from_low_u64_be(444); + let destination = H160::from_low_u64_be(555); + assert_ok!(EthBridge::set_bridge_paused(frame_system::RawOrigin::Root.into(), true)); + + let max_delayed_events = DelayedEventProofsPerBlock::::get(); + let event_count: u8 = max_delayed_events * 2; + let mut event_ids: Vec = vec![]; + let mut events_for_proving = vec![]; + for _ in 0..event_count { + let event_proof_id = NextEventProofId::::get(); + event_ids.push(event_proof_id); + let event_proof_info = EthySigningRequest::Ethereum(EthereumEventInfo { + source, + destination: destination.clone(), + message: BoundedVec::truncate_from(message.to_vec()), + validator_set_id: EthBridge::validator_set().id, + event_proof_id, + }); + events_for_proving.push(event_proof_info.clone()); + // Generate event proof + assert_ok!(EthBridge::send_event(&source, &destination, &message)); + // Ensure event has been added to delayed claims + assert_eq!(PendingEventProofs::::get(event_proof_id), Some(event_proof_info)); + assert_eq!(NextEventProofId::::get(), event_proof_id + 1); + } + + // Re-enable bridge + assert_ok!(EthBridge::set_bridge_paused(frame_system::RawOrigin::Root.into(), false)); + // Calling on_idle with not enough weight to process one claim should do nothing + let minimum_weight: Weight = + DbWeight::get().reads(3u64) + DbWeight::get().reads_writes(2, 2); + assert_eq!( + EthBridge::on_idle(frame_system::Pallet::::block_number() + 1, minimum_weight), + Weight::from_ref_time(0) + ); + + // All PendingEventProofs should still be present + for i in 0..event_count { + assert!(PendingEventProofs::::get(event_ids[i as usize]).is_some()); + } }); } @@ -1772,11 +2079,15 @@ fn set_delayed_event_proofs_per_block() { // Re-enable bridge assert_ok!(EthBridge::set_bridge_paused(frame_system::RawOrigin::Root.into(), false)); - // initialize pallet and initiate event proof + // call on_idle for pallet and initiate event proof + let expected_weight: Weight = DbWeight::get().reads(3u64) + + DbWeight::get().reads_writes(2, 2) * new_max_delayed_events as u64; assert_eq!( - EthBridge::on_initialize(frame_system::Pallet::::block_number() + 1), - DbWeight::get().reads(3u64) + - DbWeight::get().writes(2u64) * new_max_delayed_events as u64 + EthBridge::on_idle( + frame_system::Pallet::::block_number() + 1, + Weight::from_ref_time(1_000_000_000_000) + ), + expected_weight ); for i in 0..new_max_delayed_events { @@ -2361,36 +2672,127 @@ fn handle_call_notarization_aborts_no_consensus() { fn test_prune_claim_ids() { { let mut test_vec = vec![1, 2, 3, 4, 6, 7]; - prune_claim_ids(&mut test_vec); + Pallet::::prune_claim_ids(&mut test_vec); assert_eq!(test_vec, vec![4, 6, 7]); } { let mut test_vec = vec![4, 5, 6, 7]; - prune_claim_ids(&mut test_vec); + Pallet::::prune_claim_ids(&mut test_vec); assert_eq!(test_vec, vec![7]); } { let mut test_vec: Vec = vec![]; - prune_claim_ids(&mut test_vec); + Pallet::::prune_claim_ids(&mut test_vec); assert_eq!(test_vec, vec![] as Vec); } { let mut test_vec = vec![5]; - prune_claim_ids(&mut test_vec); + Pallet::::prune_claim_ids(&mut test_vec); assert_eq!(test_vec, vec![5]); } { let mut test_vec = vec![0, 0, 0]; // event_id will be unique. Hence not applicable - prune_claim_ids(&mut test_vec); + Pallet::::prune_claim_ids(&mut test_vec); assert_eq!(test_vec, vec![0, 0, 0]); } { - let mut test_vec = vec![5, 2, 0, 1, 1]; // event_id will be unique. Hence not applicable - prune_claim_ids(&mut test_vec); + let mut test_vec = vec![0, 1, 1, 2, 5]; // event_id will be unique. Hence not applicable + Pallet::::prune_claim_ids(&mut test_vec); assert_eq!(test_vec, vec![1, 1, 2, 5]); } } +#[test] +fn prune_claim_ids_keeps_missed() { + ExtBuilder::default().build().execute_with(|| { + let mut test_vec = vec![1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + Pallet::::prune_claim_ids(&mut test_vec); + assert_eq!(test_vec, vec![12]); + assert_eq!(MissedMessageIds::::get(), vec![2]); + }); + + ExtBuilder::default().build().execute_with(|| { + let mut test_vec = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26]; + Pallet::::prune_claim_ids(&mut test_vec); + assert_eq!(test_vec, vec![8, 10, 12, 14, 16, 18, 20, 22, 24, 26]); + assert_eq!(MissedMessageIds::::get(), vec![3, 5, 7]); + }); + + ExtBuilder::default().build().execute_with(|| { + let mut test_vec = vec![1, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14]; + Pallet::::prune_claim_ids(&mut test_vec); + assert_eq!(test_vec, vec![6, 8, 9, 10, 11, 12, 13, 14]); + assert_eq!(MissedMessageIds::::get(), vec![2, 3]); + }); +} + +#[test] +fn on_initialize_doesnt_prune_if_not_needed() { + ExtBuilder::default().build().execute_with(|| { + // These ids will be pruned if the pruning function gets called in on_initialize + // However there is nothing to process so it doesn't prune + // This prevents pruning happening when no changes are made + let event_ids = vec![1, 2, 3, 4, 5]; + ProcessedMessageIds::::put(BoundedVec::truncate_from(event_ids.clone())); + + // Call on_initialize with nothing to prune + let consumed_weight = EthBridge::on_initialize(25); + assert_eq!(consumed_weight, DbWeight::get().reads(3u64)); + + // ProcessedMessageIds unchanged + assert_eq!(ProcessedMessageIds::::get().into_inner(), event_ids) + }); +} + +#[test] +fn on_initialize_moves_missed_to_vec() { + let relayer = H160::from_low_u64_be(123); + ExtBuilder::default().relayer(relayer).build().execute_with(|| { + // Setup data for even ids up to 26 + let event_ids = vec![1, 10, 12, 14, 16, 18, 20, 22, 24, 26]; + ProcessedMessageIds::::put(BoundedVec::truncate_from(event_ids.clone())); + + // submit event 28 and 30 + let event_data = encode_event_message( + 28 as u64, + H160::from_low_u64_be(555), + H160::from_low_u64_be(555), + &[2, 3, 4, 5], + ); + let tx_hash = EthHash::from_low_u64_be(33); + assert_ok!(EthBridge::submit_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_data.clone(), + )); + let event_data = encode_event_message( + 30 as u64, + H160::from_low_u64_be(555), + H160::from_low_u64_be(555), + &[2, 3, 4, 5], + ); + let tx_hash = EthHash::from_low_u64_be(33); + assert_ok!(EthBridge::submit_event( + RuntimeOrigin::signed(relayer.into()), + tx_hash.clone(), + event_data.clone(), + )); + + // Process the messages + let process_at = System::block_number() + ChallengePeriod::::get(); + EthBridge::on_initialize(process_at); + + // ProcessedMessageIds updated with new id and pruned old ids + assert_eq!( + ProcessedMessageIds::::get().into_inner(), + vec![12, 14, 16, 18, 20, 22, 24, 26, 28, 30] + ); + + // MissedMessageIds contains pruned ids + assert_eq!(MissedMessageIds::::get(), vec![2, 3, 4, 5, 6, 7, 8, 9, 11]); + }); +} + #[test] fn test_submit_event_replay_check() { let relayer = H160::from_low_u64_be(123); @@ -2422,7 +2824,7 @@ fn test_submit_event_replay_check() { let process_at = System::block_number() + ChallengePeriod::::get(); EthBridge::on_initialize(process_at); // check the processed_message_ids has [1, 3] - assert_eq!(ProcessedMessageIds::::get(), vec![1, 3]); + assert_eq!(ProcessedMessageIds::::get().into_inner(), vec![1, 3]); // try to resubmit claim 0 again. assert_noop!( EthBridge::submit_event( @@ -2444,7 +2846,7 @@ fn test_submit_event_replay_check() { EthBridge::on_initialize(process_at2); // check the processed_message_ids has [3] - assert_eq!(ProcessedMessageIds::::get(), vec![3]); + assert_eq!(ProcessedMessageIds::::get().into_inner(), vec![3]); }); } diff --git a/pallet/ethy/src/weights.rs b/pallet/ethy/src/weights.rs new file mode 100644 index 000000000..a8446e761 --- /dev/null +++ b/pallet/ethy/src/weights.rs @@ -0,0 +1,381 @@ +// 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. + +//! Autogenerated weights for `pallet_ethy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-05-01, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Jasons-Ubuntu`, CPU: `AMD Ryzen 9 7950X 16-Core Processor` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/seed +// benchmark +// pallet +// --chain +// dev +// --steps=50 +// --repeat=20 +// --pallet=pallet-ethy +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output +// ./output/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_ethy. +pub trait WeightInfo { + fn set_xrpl_door_signers(p: u32, ) -> Weight; + fn set_relayer() -> Weight; + fn deposit_relayer_bond() -> Weight; + fn withdraw_relayer_bond() -> Weight; + fn set_event_block_confirmations() -> Weight; + fn set_delayed_event_proofs_per_block() -> Weight; + fn set_challenge_period() -> Weight; + fn set_contract_address() -> Weight; + fn set_bridge_paused() -> Weight; + fn finalise_authorities_change() -> Weight; + fn remove_missing_event_id() -> Weight; + fn submit_missing_event() -> Weight; + fn submit_event() -> Weight; + fn submit_challenge() -> Weight; + fn submit_notarization() -> Weight; + fn handle_authorities_change() -> Weight; +} + +/// Weights for pallet_ethy using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge XrplDoorSigners (r:1 w:1) + // Storage: EthBridge NotaryXrplKeys (r:0 w:1) + /// The range of component `p` is `[1, 19]`. + fn set_xrpl_door_signers(p: u32, ) -> Weight { + Weight::from_ref_time(17_804_000 as u64) + // Standard Error: 1_893 + .saturating_add(Weight::from_ref_time(866_563 as u64).saturating_mul(p as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + } + // Storage: EthBridge RelayerPaidBond (r:1 w:0) + // Storage: EthBridge Relayer (r:0 w:1) + fn set_relayer() -> Weight { + Weight::from_ref_time(16_110_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge RelayerPaidBond (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + fn deposit_relayer_bond() -> Weight { + Weight::from_ref_time(58_120_000 as u64) + .saturating_add(T::DbWeight::get().reads(7 as u64)) + .saturating_add(T::DbWeight::get().writes(7 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge RelayerPaidBond (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + fn withdraw_relayer_bond() -> Weight { + Weight::from_ref_time(60_995_000 as u64) + .saturating_add(T::DbWeight::get().reads(8 as u64)) + .saturating_add(T::DbWeight::get().writes(7 as u64)) + } + // Storage: EthBridge EventBlockConfirmations (r:0 w:1) + fn set_event_block_confirmations() -> Weight { + Weight::from_ref_time(3_727_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge DelayedEventProofsPerBlock (r:0 w:1) + fn set_delayed_event_proofs_per_block() -> Weight { + Weight::from_ref_time(3_777_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge ChallengePeriod (r:0 w:1) + fn set_challenge_period() -> Weight { + Weight::from_ref_time(3_797_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge ContractAddress (r:0 w:1) + fn set_contract_address() -> Weight { + Weight::from_ref_time(11_171_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge BridgePaused (r:0 w:1) + fn set_bridge_paused() -> Weight { + Weight::from_ref_time(3_847_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge NotarySetId (r:1 w:1) + // Storage: System Digest (r:1 w:1) + // Storage: EthBridge XrplDoorSigners (r:1 w:0) + // Storage: EthBridge AuthoritiesChangedThisEra (r:0 w:1) + // Storage: EthBridge NotaryXrplKeys (r:0 w:1) + // Storage: EthBridge NotaryKeys (r:0 w:1) + // Storage: EthBridge BridgePaused (r:0 w:1) + fn finalise_authorities_change() -> Weight { + Weight::from_ref_time(15_600_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(6 as u64)) + } + // Storage: EthBridge MissedMessageIds (r:1 w:1) + fn remove_missing_event_id() -> Weight { + Weight::from_ref_time(7_003_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge MissedMessageIds (r:1 w:0) + // Storage: EthBridge PendingEventClaims (r:1 w:1) + // Storage: EthBridge ChallengePeriod (r:1 w:0) + // Storage: EthBridge MessagesValidAt (r:1 w:1) + // Storage: EthBridge PendingClaimStatus (r:0 w:1) + fn submit_missing_event() -> Weight { + Weight::from_ref_time(23_405_000 as u64) + .saturating_add(T::DbWeight::get().reads(5 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge PendingEventClaims (r:1 w:1) + // Storage: EthBridge ProcessedMessageIds (r:1 w:0) + // Storage: EthBridge ChallengePeriod (r:1 w:0) + // Storage: EthBridge MessagesValidAt (r:1 w:1) + // Storage: EthBridge PendingClaimStatus (r:0 w:1) + fn submit_event() -> Weight { + Weight::from_ref_time(22_543_000 as u64) + .saturating_add(T::DbWeight::get().reads(5 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: EthBridge PendingEventClaims (r:1 w:0) + // Storage: EthBridge PendingClaimStatus (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + // Storage: EthBridge PendingClaimChallenges (r:1 w:1) + // Storage: EthBridge ChallengerAccount (r:0 w:1) + fn submit_challenge() -> Weight { + Weight::from_ref_time(65_424_000 as u64) + .saturating_add(T::DbWeight::get().reads(9 as u64)) + .saturating_add(T::DbWeight::get().writes(9 as u64)) + } + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge PendingClaimStatus (r:1 w:1) + // Storage: Session Validators (r:1 w:0) + // Storage: EthBridge EventNotarizations (r:2 w:1) + // Storage: EthBridge PendingClaimChallenges (r:1 w:1) + // Storage: EthBridge PendingEventClaims (r:1 w:0) + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge ChallengerAccount (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:1 w:1) + fn submit_notarization() -> Weight { + Weight::from_ref_time(90_962_000 as u64) + .saturating_add(T::DbWeight::get().reads(14 as u64)) + .saturating_add(T::DbWeight::get().writes(9 as u64)) + } + // Storage: EthBridge NextNotaryKeys (r:1 w:0) + // Storage: EthBridge NotarySetId (r:1 w:0) + // Storage: EthBridge ContractAddress (r:1 w:0) + // Storage: EthBridge NextEventProofId (r:1 w:1) + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge BridgePaused (r:1 w:1) + // Storage: System Digest (r:1 w:1) + // Storage: EthBridge XrplDoorSigners (r:1 w:0) + // Storage: EthBridge NotaryXrplKeys (r:1 w:0) + // Storage: XRPLBridge DoorAddress (r:1 w:0) + // Storage: EthBridge NextAuthorityChange (r:0 w:1) + // Storage: EthBridge AuthoritiesChangedThisEra (r:0 w:1) + // Storage: EthBridge NotarySetProofId (r:0 w:1) + fn handle_authorities_change() -> Weight { + Weight::from_ref_time(41_599_000 as u64) + .saturating_add(T::DbWeight::get().reads(10 as u64)) + .saturating_add(T::DbWeight::get().writes(6 as u64)) + } +} + +/// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge XrplDoorSigners (r:1 w:1) + // Storage: EthBridge NotaryXrplKeys (r:0 w:1) + /// The range of component `p` is `[1, 19]`. + fn set_xrpl_door_signers(p: u32, ) -> Weight { + Weight::from_ref_time(17_804_000 as u64) + // Standard Error: 1_893 + .saturating_add(Weight::from_ref_time(866_563 as u64).saturating_mul(p as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + } + // Storage: EthBridge RelayerPaidBond (r:1 w:0) + // Storage: EthBridge Relayer (r:0 w:1) + fn set_relayer() -> Weight { + Weight::from_ref_time(16_110_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge RelayerPaidBond (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + fn deposit_relayer_bond() -> Weight { + Weight::from_ref_time(58_120_000 as u64) + .saturating_add(RocksDbWeight::get().reads(7 as u64)) + .saturating_add(RocksDbWeight::get().writes(7 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge RelayerPaidBond (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + fn withdraw_relayer_bond() -> Weight { + Weight::from_ref_time(60_995_000 as u64) + .saturating_add(RocksDbWeight::get().reads(8 as u64)) + .saturating_add(RocksDbWeight::get().writes(7 as u64)) + } + // Storage: EthBridge EventBlockConfirmations (r:0 w:1) + fn set_event_block_confirmations() -> Weight { + Weight::from_ref_time(3_727_000 as u64) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge DelayedEventProofsPerBlock (r:0 w:1) + fn set_delayed_event_proofs_per_block() -> Weight { + Weight::from_ref_time(3_777_000 as u64) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge ChallengePeriod (r:0 w:1) + fn set_challenge_period() -> Weight { + Weight::from_ref_time(3_797_000 as u64) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge ContractAddress (r:0 w:1) + fn set_contract_address() -> Weight { + Weight::from_ref_time(11_171_000 as u64) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge BridgePaused (r:0 w:1) + fn set_bridge_paused() -> Weight { + Weight::from_ref_time(3_847_000 as u64) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge NotarySetId (r:1 w:1) + // Storage: System Digest (r:1 w:1) + // Storage: EthBridge XrplDoorSigners (r:1 w:0) + // Storage: EthBridge AuthoritiesChangedThisEra (r:0 w:1) + // Storage: EthBridge NotaryXrplKeys (r:0 w:1) + // Storage: EthBridge NotaryKeys (r:0 w:1) + // Storage: EthBridge BridgePaused (r:0 w:1) + fn finalise_authorities_change() -> Weight { + Weight::from_ref_time(15_600_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(6 as u64)) + } + // Storage: EthBridge MissedMessageIds (r:1 w:1) + fn remove_missing_event_id() -> Weight { + Weight::from_ref_time(7_003_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge MissedMessageIds (r:1 w:0) + // Storage: EthBridge PendingEventClaims (r:1 w:1) + // Storage: EthBridge ChallengePeriod (r:1 w:0) + // Storage: EthBridge MessagesValidAt (r:1 w:1) + // Storage: EthBridge PendingClaimStatus (r:0 w:1) + fn submit_missing_event() -> Weight { + Weight::from_ref_time(23_405_000 as u64) + .saturating_add(RocksDbWeight::get().reads(5 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge PendingEventClaims (r:1 w:1) + // Storage: EthBridge ProcessedMessageIds (r:1 w:0) + // Storage: EthBridge ChallengePeriod (r:1 w:0) + // Storage: EthBridge MessagesValidAt (r:1 w:1) + // Storage: EthBridge PendingClaimStatus (r:0 w:1) + fn submit_event() -> Weight { + Weight::from_ref_time(22_543_000 as u64) + .saturating_add(RocksDbWeight::get().reads(5 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + // Storage: EthBridge PendingEventClaims (r:1 w:0) + // Storage: EthBridge PendingClaimStatus (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + // Storage: EthBridge PendingClaimChallenges (r:1 w:1) + // Storage: EthBridge ChallengerAccount (r:0 w:1) + fn submit_challenge() -> Weight { + Weight::from_ref_time(65_424_000 as u64) + .saturating_add(RocksDbWeight::get().reads(9 as u64)) + .saturating_add(RocksDbWeight::get().writes(9 as u64)) + } + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge PendingClaimStatus (r:1 w:1) + // Storage: Session Validators (r:1 w:0) + // Storage: EthBridge EventNotarizations (r:2 w:1) + // Storage: EthBridge PendingClaimChallenges (r:1 w:1) + // Storage: EthBridge PendingEventClaims (r:1 w:0) + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge ChallengerAccount (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:1 w:1) + fn submit_notarization() -> Weight { + Weight::from_ref_time(90_962_000 as u64) + .saturating_add(RocksDbWeight::get().reads(14 as u64)) + .saturating_add(RocksDbWeight::get().writes(9 as u64)) + } + // Storage: EthBridge NextNotaryKeys (r:1 w:0) + // Storage: EthBridge NotarySetId (r:1 w:0) + // Storage: EthBridge ContractAddress (r:1 w:0) + // Storage: EthBridge NextEventProofId (r:1 w:1) + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge BridgePaused (r:1 w:1) + // Storage: System Digest (r:1 w:1) + // Storage: EthBridge XrplDoorSigners (r:1 w:0) + // Storage: EthBridge NotaryXrplKeys (r:1 w:0) + // Storage: XRPLBridge DoorAddress (r:1 w:0) + // Storage: EthBridge NextAuthorityChange (r:0 w:1) + // Storage: EthBridge AuthoritiesChangedThisEra (r:0 w:1) + // Storage: EthBridge NotarySetProofId (r:0 w:1) + fn handle_authorities_change() -> Weight { + Weight::from_ref_time(41_599_000 as u64) + .saturating_add(RocksDbWeight::get().reads(10 as u64)) + .saturating_add(RocksDbWeight::get().writes(6 as u64)) + } +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index a6c846efb..bba6176ae 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -332,6 +332,7 @@ runtime-benchmarks = [ "pallet-fee-control/runtime-benchmarks", "pallet-nft-peg/runtime-benchmarks", "pallet-erc20-peg/runtime-benchmarks", + "pallet-ethy/runtime-benchmarks", "pallet-echo/runtime-benchmarks", "pallet-assets-ext/runtime-benchmarks", "pallet-evm-chain-id/runtime-benchmarks", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8b80dc509..e379925dd 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -983,6 +983,7 @@ parameter_types! { pub const MaxChallenges: u32 = 100; pub const MaxMessagesPerBlock: u32 = 1000; pub const MaxCallRequests: u32 = 1000; + pub const MaxProcessedMessageIds: u32 = 1000; } impl pallet_ethy::Config for Runtime { @@ -998,14 +999,14 @@ impl pallet_ethy::Config for Runtime { type ChallengeBond = ChallengeBond; // The duration in blocks of one epoch type EpochDuration = EpochDuration; - /// The runtime event type. - type RuntimeEvent = RuntimeEvent; /// Subscribers to completed 'eth_call' jobs type EthCallSubscribers = (); - /// Subscribers to completed event - type EventRouter = EthereumEventRouter; /// Provides Ethereum JSON-RPC client to the pallet (OCW friendly) type EthereumRpcClient = pallet_ethy::EthereumRpcClient; + /// The runtime event type. + type RuntimeEvent = RuntimeEvent; + /// Subscribers to completed event + type EventRouter = EthereumEventRouter; /// The identifier type for Ethy notaries type EthyId = EthBridgeId; /// Reports final session status of an era @@ -1021,10 +1022,11 @@ impl pallet_ethy::Config for Runtime { type RelayerBond = RelayerBond; /// The pallet handling scheduled Runtime calls type Scheduler = Scheduler; - /// Timestamp provider - type UnixTime = Timestamp; /// Pallets origin type type PalletsOrigin = OriginCaller; + type MaxProcessedMessageIds = MaxProcessedMessageIds; + /// Timestamp provider + type UnixTime = Timestamp; /// Max Xrpl notary (validator) public keys type MaxXrplKeys = MaxXrplKeys; /// Xrpl-bridge adapter @@ -1034,6 +1036,7 @@ impl pallet_ethy::Config for Runtime { type MaxChallenges = MaxChallenges; type MaxMessagesPerBlock = MaxMessagesPerBlock; type MaxCallRequests = MaxCallRequests; + type WeightInfo = weights::pallet_ethy::WeightInfo; } impl frame_system::offchain::SigningTypes for Runtime { @@ -2164,6 +2167,7 @@ mod benches { [pallet_xrpl_bridge, XRPLBridge] [pallet_xrpl, Xrpl] [pallet_erc20_peg, Erc20Peg] + [pallet_ethy, EthBridge] [pallet_echo, Echo] [pallet_assets_ext, AssetsExt] [pallet_evm_chain_id, EVMChainId] diff --git a/runtime/src/migrations/ethy.rs b/runtime/src/migrations/ethy.rs index 7b369a99f..96ecd4d65 100644 --- a/runtime/src/migrations/ethy.rs +++ b/runtime/src/migrations/ethy.rs @@ -77,7 +77,7 @@ pub mod v1 { weights::{constants::RocksDbWeight, Weight}, BoundedVec, StorageHasher, Twox64Concat, }; - use pallet_ethy::{BridgePauseStatus, BridgePaused}; + use pallet_ethy::{BridgePauseStatus, BridgePaused, ProcessedMessageIds}; use scale_info::TypeInfo; use seed_primitives::ethy::EventClaimId; use sp_core::{Get, H160}; @@ -88,8 +88,19 @@ pub mod v1 { where AccountId: From, { + log::info!(target: "Migration", "Ethy: migrating ProcessedMessageIds"); + let mut weight: Weight = RocksDbWeight::get().reads_writes(1, 1); + + let mut processed_message_ids = + Value::unsafe_storage_get::>(b"EthBridge", b"ProcessedMessageIds") + .unwrap_or_default(); + let prune_weight = pallet_ethy::Pallet::::prune_claim_ids(&mut processed_message_ids); + weight = weight.saturating_add(prune_weight); + let message_ids = BoundedVec::truncate_from(processed_message_ids); + ProcessedMessageIds::::put(message_ids); + log::info!(target: "Migration", "Ethy: migrating BridgePaused"); - let weight: Weight = RocksDbWeight::get().reads_writes(1, 1); + weight = weight.saturating_add(RocksDbWeight::get().reads_writes(1, 1)); let bridge_paused = Value::unsafe_storage_get::(b"EthBridge", b"BridgePaused").unwrap_or_default(); @@ -98,7 +109,7 @@ pub mod v1 { BridgePauseStatus { manual_pause: bridge_paused, authorities_change: false }; BridgePaused::::put(paused_status); - log::info!(target: "Migration", "Ethy: successfully migrated BridgePaused"); + log::info!(target: "Migration", "Ethy: successfully migrated BridgePaused and ProcessedMessageIds"); weight } @@ -106,7 +117,73 @@ pub mod v1 { #[cfg(test)] mod tests { use super::*; - use crate::migrations::tests::new_test_ext; + use crate::{migrations::tests::new_test_ext, MaxProcessedMessageIds}; + use pallet_ethy::MissedMessageIds; + + #[test] + fn migration_test_1() { + new_test_ext().execute_with(|| { + // Setup storage + StorageVersion::new(0).put::(); + + // token locks with no listings + let event_ids: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + Value::unsafe_storage_put::>( + b"EthBridge", + b"ProcessedMessageIds", + event_ids, + ); + + // Do runtime upgrade + Upgrade::on_runtime_upgrade(); + assert_eq!(EthBridge::on_chain_storage_version(), 1); + + let event_ids = ProcessedMessageIds::::get(); + assert_eq!(event_ids.into_inner(), vec![10]); + let missed_ids = MissedMessageIds::::get(); + assert!(missed_ids.is_empty()); + }); + } + + #[test] + fn migration_test_2() { + new_test_ext().execute_with(|| { + // Setup storage + StorageVersion::new(0).put::(); + + // Create false data with all even numbers between 0 and 4000 + let max = MaxProcessedMessageIds::get() as u64; + let event_ids: Vec = + (0u64..max * 2).into_iter().map(|x| x * 2).collect(); + + assert_eq!(event_ids.len(), (max * 2) as usize); + Value::unsafe_storage_put::>( + b"EthBridge", + b"ProcessedMessageIds", + event_ids, + ); + + // Do runtime upgrade + Upgrade::on_runtime_upgrade(); + assert_eq!(EthBridge::on_chain_storage_version(), 1); + + // ProcessedMessageIds should be the second half of the original list + // i.e. all even numbers between 2000 and 4000 + let expected_event_ids: Vec = + (max..max * 2).into_iter().map(|x| x * 2).collect(); + let event_ids = ProcessedMessageIds::::get(); + assert_eq!(event_ids.len(), max as usize); + assert_eq!(event_ids.into_inner(), expected_event_ids); + + // MissedMessageIds should be all the odd message Ids in the first half of the + // original list. i.e. 1,3,5,7 + let expected_missed_ids: Vec = + (0..max).into_iter().map(|x| x * 2 + 1).collect(); + let missed_ids = MissedMessageIds::::get(); + assert_eq!(missed_ids.len(), max as usize); + assert_eq!(missed_ids, expected_missed_ids); + }); + } #[test] fn migration_test_bridge_paused_1() { diff --git a/runtime/src/weights/mod.rs b/runtime/src/weights/mod.rs index b2a9002ef..251fd808e 100644 --- a/runtime/src/weights/mod.rs +++ b/runtime/src/weights/mod.rs @@ -9,6 +9,7 @@ pub mod pallet_doughnut; pub mod pallet_echo; pub mod pallet_election_provider_multi_phase; pub mod pallet_erc20_peg; +pub mod pallet_ethy; pub mod pallet_evm_chain_id; pub mod pallet_fee_control; pub mod pallet_futurepass; diff --git a/runtime/src/weights/pallet_ethy.rs b/runtime/src/weights/pallet_ethy.rs new file mode 100644 index 000000000..a631a2ee2 --- /dev/null +++ b/runtime/src/weights/pallet_ethy.rs @@ -0,0 +1,188 @@ + +//! Autogenerated weights for `pallet_ethy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2024-05-01, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Jasons-Ubuntu`, CPU: `AMD Ryzen 9 7950X 16-Core Processor` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/seed +// benchmark +// pallet +// --chain +// dev +// --steps=50 +// --repeat=20 +// --pallet=pallet-ethy +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output +// ./output/ + +#![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_ethy`. +pub struct WeightInfo(PhantomData); +impl pallet_ethy::WeightInfo for WeightInfo { + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge XrplDoorSigners (r:1 w:1) + // Storage: EthBridge NotaryXrplKeys (r:0 w:1) + /// The range of component `p` is `[1, 19]`. + fn set_xrpl_door_signers(p: u32, ) -> Weight { + Weight::from_ref_time(17_804_000 as u64) + // Standard Error: 1_893 + .saturating_add(Weight::from_ref_time(866_563 as u64).saturating_mul(p as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(p as u64))) + } + // Storage: EthBridge RelayerPaidBond (r:1 w:0) + // Storage: EthBridge Relayer (r:0 w:1) + fn set_relayer() -> Weight { + Weight::from_ref_time(16_110_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge RelayerPaidBond (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + fn deposit_relayer_bond() -> Weight { + Weight::from_ref_time(58_120_000 as u64) + .saturating_add(T::DbWeight::get().reads(7 as u64)) + .saturating_add(T::DbWeight::get().writes(7 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge RelayerPaidBond (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + fn withdraw_relayer_bond() -> Weight { + Weight::from_ref_time(60_995_000 as u64) + .saturating_add(T::DbWeight::get().reads(8 as u64)) + .saturating_add(T::DbWeight::get().writes(7 as u64)) + } + // Storage: EthBridge EventBlockConfirmations (r:0 w:1) + fn set_event_block_confirmations() -> Weight { + Weight::from_ref_time(3_727_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge DelayedEventProofsPerBlock (r:0 w:1) + fn set_delayed_event_proofs_per_block() -> Weight { + Weight::from_ref_time(3_777_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge ChallengePeriod (r:0 w:1) + fn set_challenge_period() -> Weight { + Weight::from_ref_time(3_797_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge ContractAddress (r:0 w:1) + fn set_contract_address() -> Weight { + Weight::from_ref_time(11_171_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge BridgePaused (r:0 w:1) + fn set_bridge_paused() -> Weight { + Weight::from_ref_time(3_847_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge NotarySetId (r:1 w:1) + // Storage: System Digest (r:1 w:1) + // Storage: EthBridge XrplDoorSigners (r:1 w:0) + // Storage: EthBridge AuthoritiesChangedThisEra (r:0 w:1) + // Storage: EthBridge NotaryXrplKeys (r:0 w:1) + // Storage: EthBridge NotaryKeys (r:0 w:1) + // Storage: EthBridge BridgePaused (r:0 w:1) + fn finalise_authorities_change() -> Weight { + Weight::from_ref_time(15_600_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(6 as u64)) + } + // Storage: EthBridge MissedMessageIds (r:1 w:1) + fn remove_missing_event_id() -> Weight { + Weight::from_ref_time(7_003_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge MissedMessageIds (r:1 w:0) + // Storage: EthBridge PendingEventClaims (r:1 w:1) + // Storage: EthBridge ChallengePeriod (r:1 w:0) + // Storage: EthBridge MessagesValidAt (r:1 w:1) + // Storage: EthBridge PendingClaimStatus (r:0 w:1) + fn submit_missing_event() -> Weight { + Weight::from_ref_time(23_405_000 as u64) + .saturating_add(T::DbWeight::get().reads(5 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge PendingEventClaims (r:1 w:1) + // Storage: EthBridge ProcessedMessageIds (r:1 w:0) + // Storage: EthBridge ChallengePeriod (r:1 w:0) + // Storage: EthBridge MessagesValidAt (r:1 w:1) + // Storage: EthBridge PendingClaimStatus (r:0 w:1) + fn submit_event() -> Weight { + Weight::from_ref_time(22_543_000 as u64) + .saturating_add(T::DbWeight::get().reads(5 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: EthBridge PendingEventClaims (r:1 w:0) + // Storage: EthBridge PendingClaimStatus (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:2 w:2) + // Storage: EthBridge PendingClaimChallenges (r:1 w:1) + // Storage: EthBridge ChallengerAccount (r:0 w:1) + fn submit_challenge() -> Weight { + Weight::from_ref_time(65_424_000 as u64) + .saturating_add(T::DbWeight::get().reads(9 as u64)) + .saturating_add(T::DbWeight::get().writes(9 as u64)) + } + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge PendingClaimStatus (r:1 w:1) + // Storage: Session Validators (r:1 w:0) + // Storage: EthBridge EventNotarizations (r:2 w:1) + // Storage: EthBridge PendingClaimChallenges (r:1 w:1) + // Storage: EthBridge PendingEventClaims (r:1 w:0) + // Storage: EthBridge Relayer (r:1 w:0) + // Storage: EthBridge ChallengerAccount (r:1 w:1) + // Storage: AssetsExt Holds (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:2 w:2) + // Storage: System Account (r:1 w:1) + fn submit_notarization() -> Weight { + Weight::from_ref_time(90_962_000 as u64) + .saturating_add(T::DbWeight::get().reads(14 as u64)) + .saturating_add(T::DbWeight::get().writes(9 as u64)) + } + // Storage: EthBridge NextNotaryKeys (r:1 w:0) + // Storage: EthBridge NotarySetId (r:1 w:0) + // Storage: EthBridge ContractAddress (r:1 w:0) + // Storage: EthBridge NextEventProofId (r:1 w:1) + // Storage: EthBridge NotaryKeys (r:1 w:0) + // Storage: EthBridge BridgePaused (r:1 w:1) + // Storage: System Digest (r:1 w:1) + // Storage: EthBridge XrplDoorSigners (r:1 w:0) + // Storage: EthBridge NotaryXrplKeys (r:1 w:0) + // Storage: XRPLBridge DoorAddress (r:1 w:0) + // Storage: EthBridge NextAuthorityChange (r:0 w:1) + // Storage: EthBridge AuthoritiesChangedThisEra (r:0 w:1) + // Storage: EthBridge NotarySetProofId (r:0 w:1) + fn handle_authorities_change() -> Weight { + Weight::from_ref_time(41_599_000 as u64) + .saturating_add(T::DbWeight::get().reads(10 as u64)) + .saturating_add(T::DbWeight::get().writes(6 as u64)) + } +}