From 2f457775bbcec10ffcd3203f858d63064f270ffb Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky <svyatonik@gmail.com> Date: Wed, 10 Feb 2021 20:26:47 +0300 Subject: [PATCH] Account proof size in weight formula (#679) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix broken message lane benchmarks * proof-size related benchmarks * impl Size for proof parameters * include proof weight into weight formula * left TODO * fixed proof size * WeightInfoExt::receive_messages_proof_weight * charge for extra message bytes delivery in send_message * removed default impl of WeightsInfoExt * moved weight formulas to WeightInfoExt * receive_messages_proof_outbound_lane_state_overhead is included twice in weight * typo * typo * fixed TODO * more asserts * started wotk on message-lane documentation * expected_extra_storage_proof_size() is actually expected in delivery confirmation tx * update README.md * ensure_able_to_receive_confirmation * test rialto message lane weights * removed TODO * removed unnecessary trait requirements * fixed arguments * fix compilation * decreased basic delivery tx weight * fmt * clippy * Update modules/message-lane/src/benchmarking.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * structs * Update primitives/millau/src/lib.rs Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> * removed readme.md * removed obsolete trait bounds * Revert "removed readme.md" This reverts commit 50b7376a41687a94c27bf77565434be153f87ca1. * Update bin/runtime-common/src/messages.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update bin/runtime-common/src/messages.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update bin/runtime-common/src/messages.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update bin/runtime-common/src/messages.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update bin/runtime-common/src/messages.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update bin/runtime-common/src/messages.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update bin/runtime-common/src/messages.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * PreComputedSize Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com> Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> --- bridges/bin/millau/runtime/src/lib.rs | 38 ++- .../bin/millau/runtime/src/rialto_messages.rs | 22 +- bridges/bin/rialto/runtime/src/lib.rs | 47 +++- .../bin/rialto/runtime/src/millau_messages.rs | 23 +- bridges/bin/runtime-common/src/messages.rs | 196 +++++++++------ .../src/messages_benchmarking.rs | 43 ++-- bridges/modules/message-lane/Cargo.toml | 2 + bridges/modules/message-lane/README.md | 132 ++++++++++ .../modules/message-lane/src/benchmarking.rs | 182 ++++++++++---- bridges/modules/message-lane/src/lib.rs | 59 +++-- bridges/modules/message-lane/src/mock.rs | 20 +- bridges/modules/message-lane/src/weights.rs | 118 +++++---- .../modules/message-lane/src/weights_ext.rs | 226 ++++++++++++++++-- bridges/primitives/message-lane/Cargo.toml | 5 + .../message-lane/src/source_chain.rs | 3 +- .../message-lane/src/target_chain.rs | 3 +- bridges/primitives/millau/src/lib.rs | 7 +- bridges/primitives/rialto/src/lib.rs | 7 +- bridges/primitives/runtime/src/lib.rs | 10 + bridges/relays/substrate-client/src/client.rs | 4 +- bridges/relays/substrate/Cargo.toml | 1 + .../relays/substrate/src/messages_source.rs | 16 +- .../relays/substrate/src/messages_target.rs | 13 +- .../src/millau_messages_to_rialto.rs | 9 +- .../src/rialto_messages_to_millau.rs | 9 +- 25 files changed, 905 insertions(+), 290 deletions(-) create mode 100644 bridges/modules/message-lane/README.md diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 65cd08970f3..bf688f5eb19 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -623,13 +623,49 @@ where #[cfg(test)] mod tests { use super::*; + use bridge_runtime_common::messages; #[test] fn ensure_millau_message_lane_weights_are_correct() { // TODO: https://github.com/paritytech/parity-bridges-common/issues/390 - pallet_message_lane::ensure_weights_are_correct::<pallet_message_lane::weights::RialtoWeight<Runtime>>( + type Weights = pallet_message_lane::weights::RialtoWeight<Runtime>; + + pallet_message_lane::ensure_weights_are_correct::<Weights>( bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT, bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, ); + + let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add( + messages::target::maximal_incoming_message_size(bp_millau::max_extrinsic_size()), + ); + pallet_message_lane::ensure_able_to_receive_message::<Weights>( + bp_millau::max_extrinsic_size(), + bp_millau::max_extrinsic_weight(), + max_incoming_message_proof_size, + bridge_runtime_common::messages::transaction_weight_without_multiplier( + bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, + max_incoming_message_proof_size as _, + 0, + ), + messages::target::maximal_incoming_message_dispatch_weight(bp_millau::max_extrinsic_weight()), + ); + + let max_incoming_inbound_lane_data_proof_size = bp_message_lane::InboundLaneData::<()>::encoded_size_hint( + bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE as _, + ) + .unwrap_or(u32::MAX); + pallet_message_lane::ensure_able_to_receive_confirmation::<Weights>( + bp_millau::max_extrinsic_size(), + bp_millau::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, + bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, + bridge_runtime_common::messages::transaction_weight_without_multiplier( + bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, + max_incoming_inbound_lane_data_proof_size as _, + 0, + ), + ); } } diff --git a/bridges/bin/millau/runtime/src/rialto_messages.rs b/bridges/bin/millau/runtime/src/rialto_messages.rs index 4ae8d09d64c..9538f18f554 100644 --- a/bridges/bin/millau/runtime/src/rialto_messages.rs +++ b/bridges/bin/millau/runtime/src/rialto_messages.rs @@ -67,10 +67,10 @@ pub type FromRialtoMessagePayload = messages::target::FromBridgedChainMessagePay pub type FromRialtoEncodedCall = messages::target::FromBridgedChainEncodedMessageCall<WithRialtoMessageBridge>; /// Messages proof for Rialto -> Millau messages. -type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof<WithRialtoMessageBridge>; +type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof<bp_rialto::Hash>; /// Messages delivery proof for Millau -> Rialto messages. -type ToRialtoMessagesDeliveryProof = messages::source::FromBridgedChainMessagesDeliveryProof<WithRialtoMessageBridge>; +type ToRialtoMessagesDeliveryProof = messages::source::FromBridgedChainMessagesDeliveryProof<bp_rialto::Hash>; /// Call-dispatch based message dispatch for Rialto -> Millau messages. pub type FromRialtoMessageDispatch = messages::target::FromBridgedChainMessageDispatch< @@ -97,7 +97,7 @@ impl MessageBridge for WithRialtoMessageBridge { fn weight_limits_of_message_on_bridged_chain(message_payload: &[u8]) -> RangeInclusive<Weight> { // we don't want to relay too large messages + keep reserve for future upgrades - let upper_limit = bp_rialto::max_extrinsic_weight() / 2; + let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(bp_rialto::max_extrinsic_weight()); // given Rialto chain parameters (`TransactionByteFee`, `WeightToFee`, `FeeMultiplierUpdate`), // the minimal weight of the message may be computed as message.length() @@ -109,13 +109,17 @@ impl MessageBridge for WithRialtoMessageBridge { } fn weight_of_delivery_transaction(message_payload: &[u8]) -> Weight { + let message_payload_len = u32::try_from(message_payload.len()) + .map(Into::into) + .unwrap_or(Weight::MAX); + let extra_bytes_in_payload = + message_payload_len.saturating_sub(pallet_message_lane::EXPECTED_DEFAULT_MESSAGE_LENGTH.into()); messages::transaction_weight_without_multiplier( - bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, - u32::try_from(message_payload.len()) - .map(Into::into) - .unwrap_or(Weight::MAX) - .saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE as _), - bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT, + bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, + message_payload_len.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE as _), + extra_bytes_in_payload + .saturating_mul(bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + .saturating_add(bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT), ) } diff --git a/bridges/bin/rialto/runtime/src/lib.rs b/bridges/bin/rialto/runtime/src/lib.rs index 3ebb487fe99..d2ade4592af 100644 --- a/bridges/bin/rialto/runtime/src/lib.rs +++ b/bridges/bin/rialto/runtime/src/lib.rs @@ -829,6 +829,7 @@ impl_runtime_apis! { MessageDeliveryProofParams as MessageLaneMessageDeliveryProofParams, MessageParams as MessageLaneMessageParams, MessageProofParams as MessageLaneMessageProofParams, + ProofSize as MessageLaneProofSize, }; impl MessageLaneConfig<WithMillauMessageLaneInstance> for Runtime { @@ -882,7 +883,11 @@ impl_runtime_apis! { use pallet_message_lane::storage_keys; use sp_runtime::traits::Header; - let call = Call::System(SystemCall::remark(vec![])); + let remark = match params.size { + MessageLaneProofSize::Minimal(ref size) => vec![0u8; *size as _], + _ => vec![], + }; + let call = Call::System(SystemCall::remark(remark)); let call_weight = call.get_dispatch_info().weight; let millau_account_id: bp_millau::AccountId = Default::default(); @@ -939,7 +944,7 @@ impl_runtime_apis! { fn prepare_message_delivery_proof( params: MessageLaneMessageDeliveryProofParams<Self::AccountId>, - ) -> millau_messages::FromMillauMessagesDeliveryProof { + ) -> millau_messages::ToMillauMessagesDeliveryProof { use crate::millau_messages::{Millau, WithMillauMessageBridge}; use bridge_runtime_common::{ messages::ChainWithMessageLanes, @@ -1013,6 +1018,7 @@ where mod tests { use super::*; use bp_currency_exchange::DepositInto; + use bridge_runtime_common::messages; fn run_deposit_into_test(test: impl Fn(AccountId) -> Balance) { let mut ext: sp_io::TestExternalities = SystemConfig::default().build_storage::<Runtime>().unwrap().into(); @@ -1051,10 +1057,45 @@ mod tests { #[test] fn ensure_rialto_message_lane_weights_are_correct() { - pallet_message_lane::ensure_weights_are_correct::<pallet_message_lane::weights::RialtoWeight<Runtime>>( + type Weights = pallet_message_lane::weights::RialtoWeight<Runtime>; + + pallet_message_lane::ensure_weights_are_correct::<Weights>( bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT, bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, ); + + let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add( + messages::target::maximal_incoming_message_size(bp_rialto::max_extrinsic_size()), + ); + pallet_message_lane::ensure_able_to_receive_message::<Weights>( + bp_rialto::max_extrinsic_size(), + bp_rialto::max_extrinsic_weight(), + max_incoming_message_proof_size, + bridge_runtime_common::messages::transaction_weight_without_multiplier( + bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, + max_incoming_message_proof_size as _, + 0, + ), + messages::target::maximal_incoming_message_dispatch_weight(bp_rialto::max_extrinsic_weight()), + ); + + let max_incoming_inbound_lane_data_proof_size = bp_message_lane::InboundLaneData::<()>::encoded_size_hint( + bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE as _, + ) + .unwrap_or(u32::MAX); + pallet_message_lane::ensure_able_to_receive_confirmation::<Weights>( + bp_rialto::max_extrinsic_size(), + bp_rialto::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, + bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, + bridge_runtime_common::messages::transaction_weight_without_multiplier( + bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, + max_incoming_inbound_lane_data_proof_size as _, + 0, + ), + ); } #[test] diff --git a/bridges/bin/rialto/runtime/src/millau_messages.rs b/bridges/bin/rialto/runtime/src/millau_messages.rs index 222da01f1cd..2810e888e7b 100644 --- a/bridges/bin/rialto/runtime/src/millau_messages.rs +++ b/bridges/bin/rialto/runtime/src/millau_messages.rs @@ -74,11 +74,10 @@ pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDi >; /// Messages proof for Millau -> Rialto messages. -pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof<WithMillauMessageBridge>; +pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof<bp_millau::Hash>; /// Messages delivery proof for Rialto -> Millau messages. -pub type FromMillauMessagesDeliveryProof = - messages::source::FromBridgedChainMessagesDeliveryProof<WithMillauMessageBridge>; +pub type ToMillauMessagesDeliveryProof = messages::source::FromBridgedChainMessagesDeliveryProof<bp_millau::Hash>; /// Millau <-> Rialto message bridge. #[derive(RuntimeDebug, Clone, Copy)] @@ -98,7 +97,7 @@ impl MessageBridge for WithMillauMessageBridge { fn weight_limits_of_message_on_bridged_chain(message_payload: &[u8]) -> RangeInclusive<Weight> { // we don't want to relay too large messages + keep reserve for future upgrades - let upper_limit = bp_millau::max_extrinsic_weight() / 2; + let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(bp_millau::max_extrinsic_weight()); // given Millau chain parameters (`TransactionByteFee`, `WeightToFee`, `FeeMultiplierUpdate`), // the minimal weight of the message may be computed as message.length() @@ -110,13 +109,17 @@ impl MessageBridge for WithMillauMessageBridge { } fn weight_of_delivery_transaction(message_payload: &[u8]) -> Weight { + let message_payload_len = u32::try_from(message_payload.len()) + .map(Into::into) + .unwrap_or(Weight::MAX); + let extra_bytes_in_payload = + message_payload_len.saturating_sub(pallet_message_lane::EXPECTED_DEFAULT_MESSAGE_LENGTH.into()); messages::transaction_weight_without_multiplier( bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic, - u32::try_from(message_payload.len()) - .map(Into::into) - .unwrap_or(Weight::MAX) - .saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE as _), - bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT, + message_payload_len.saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE as _), + extra_bytes_in_payload + .saturating_mul(bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + .saturating_add(bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT), ) } @@ -186,7 +189,7 @@ impl TargetHeaderChain<ToMillauMessagePayload, bp_millau::AccountId> for Millau // - hash of the header this proof has been created with; // - the storage proof of one or several keys; // - id of the lane we prove state of. - type MessagesDeliveryProof = FromMillauMessagesDeliveryProof; + type MessagesDeliveryProof = ToMillauMessagesDeliveryProof; fn verify_message(payload: &ToMillauMessagePayload) -> Result<(), Self::Error> { messages::source::verify_chain_message::<WithMillauMessageBridge>(payload) diff --git a/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs index 13b3a4e9458..3054334d52d 100644 --- a/bridges/bin/runtime-common/src/messages.rs +++ b/bridges/bin/runtime-common/src/messages.rs @@ -26,13 +26,13 @@ use bp_message_lane::{ target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, }; -use bp_runtime::InstanceId; +use bp_runtime::{InstanceId, Size}; use codec::{Decode, Encode}; use frame_support::{traits::Instance, weights::Weight, RuntimeDebug}; use hash_db::Hasher; use pallet_substrate_bridge::StorageProofChecker; use sp_runtime::traits::{CheckedAdd, CheckedDiv, CheckedMul}; -use sp_std::{cmp::PartialOrd, marker::PhantomData, ops::RangeInclusive, vec::Vec}; +use sp_std::{cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, ops::RangeInclusive, vec::Vec}; use sp_trie::StorageProof; /// Bidirectional message bridge. @@ -115,6 +115,9 @@ pub(crate) type BalanceOf<C> = <C as ChainWithMessageLanes>::Balance; pub(crate) type CallOf<C> = <C as ChainWithMessageLanes>::Call; pub(crate) type MessageLaneInstanceOf<C> = <C as ChainWithMessageLanes>::MessageLaneInstance; +/// Raw storage proof type (just raw trie nodes). +type RawStorageProof = Vec<Vec<u8>>; + /// Compute weight of transaction at runtime where: /// /// - transaction payment pallet is being used; @@ -160,7 +163,26 @@ pub mod source { /// - hash of finalized header; /// - storage proof of inbound lane state; /// - lane id. - pub type FromBridgedChainMessagesDeliveryProof<B> = (HashOf<BridgedChain<B>>, StorageProof, LaneId); + #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug)] + pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> { + /// Hash of the bridge header the proof is for. + pub bridged_header_hash: BridgedHeaderHash, + /// Storage trie proof generated for [`Self::bridged_header_hash`]. + pub storage_proof: RawStorageProof, + /// Lane id of which messages were delivered and the proof is for. + pub lane: LaneId, + } + + impl<BridgedHeaderHash> Size for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> { + fn size_hint(&self) -> u32 { + u32::try_from( + self.storage_proof + .iter() + .fold(0usize, |sum, node| sum.saturating_add(node.len())), + ) + .unwrap_or(u32::MAX) + } + } /// 'Parsed' message delivery proof - inbound lane id and its state. pub type ParsedMessagesDeliveryProofFromBridgedChain<B> = (LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>); @@ -204,7 +226,7 @@ pub mod source { /// Return maximal message size of This -> Bridged chain message. pub fn maximal_message_size<B: MessageBridge>() -> u32 { - B::maximal_extrinsic_size_on_target_chain() / 3 * 2 + super::target::maximal_incoming_message_size(B::maximal_extrinsic_size_on_target_chain()) } /// Do basic Bridged-chain specific verification of This -> Bridged chain message. @@ -274,7 +296,7 @@ pub mod source { /// Verify proof of This -> Bridged chain messages delivery. pub fn verify_messages_delivery_proof<B: MessageBridge, ThisRuntime>( - proof: FromBridgedChainMessagesDeliveryProof<B>, + proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>, ) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<B>, &'static str> where ThisRuntime: pallet_substrate_bridge::Config, @@ -282,10 +304,14 @@ pub mod source { HashOf<BridgedChain<B>>: Into<bp_runtime::HashOf<<ThisRuntime as pallet_substrate_bridge::Config>::BridgedChain>>, { - let (bridged_header_hash, bridged_storage_proof, lane) = proof; + let FromBridgedChainMessagesDeliveryProof { + bridged_header_hash, + storage_proof, + lane, + } = proof; pallet_substrate_bridge::Module::<ThisRuntime>::parse_finalized_storage_proof( bridged_header_hash.into(), - bridged_storage_proof, + StorageProof::new(storage_proof), |storage| { // Messages delivery proof is just proof of single storage key read => any error // is fatal. @@ -332,13 +358,29 @@ pub mod target { /// - storage proof of messages and (optionally) outbound lane state; /// - lane id; /// - nonces (inclusive range) of messages which are included in this proof. - pub type FromBridgedChainMessagesProof<B> = ( - HashOf<BridgedChain<B>>, - StorageProof, - LaneId, - MessageNonce, - MessageNonce, - ); + #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug)] + pub struct FromBridgedChainMessagesProof<BridgedHeaderHash> { + /// Hash of the finalized bridged header the proof is for. + pub bridged_header_hash: BridgedHeaderHash, + /// A storage trie proof of messages being delivered. + pub storage_proof: RawStorageProof, + pub lane: LaneId, + /// Nonce of the first message being delivered. + pub nonces_start: MessageNonce, + /// Nonce of the last message being delivered. + pub nonces_end: MessageNonce, + } + + impl<BridgedHeaderHash> Size for FromBridgedChainMessagesProof<BridgedHeaderHash> { + fn size_hint(&self) -> u32 { + u32::try_from( + self.storage_proof + .iter() + .fold(0usize, |sum, node| sum.saturating_add(node.len())), + ) + .unwrap_or(u32::MAX) + } + } /// Encoded Call of This chain as it is transferred over bridge. /// @@ -391,13 +433,23 @@ pub mod target { } } + /// Return maximal dispatch weight of the message we're able to receive. + pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight { + maximal_extrinsic_weight / 2 + } + + /// Return maximal message size given maximal extrinsic size. + pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 { + maximal_extrinsic_size / 3 * 2 + } + /// Verify proof of Bridged -> This chain messages. /// /// The `messages_count` argument verification (sane limits) is supposed to be made /// outside of this function. This function only verifies that the proof declares exactly /// `messages_count` messages. pub fn verify_messages_proof<B: MessageBridge, ThisRuntime>( - proof: FromBridgedChainMessagesProof<B>, + proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, messages_count: u32, ) -> Result<ProvedMessages<Message<BalanceOf<BridgedChain<B>>>>, &'static str> where @@ -412,7 +464,7 @@ pub mod target { |bridged_header_hash, bridged_storage_proof| { pallet_substrate_bridge::Module::<ThisRuntime>::parse_finalized_storage_proof( bridged_header_hash.into(), - bridged_storage_proof, + StorageProof::new(bridged_storage_proof), |storage_adapter| storage_adapter, ) .map(|storage| StorageProofCheckerAdapter::<_, B, ThisRuntime> { @@ -486,18 +538,24 @@ pub mod target { /// Verify proof of Bridged -> This chain messages using given message proof parser. pub(crate) fn verify_messages_proof_with_parser<B: MessageBridge, BuildParser, Parser>( - proof: FromBridgedChainMessagesProof<B>, + proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, messages_count: u32, build_parser: BuildParser, ) -> Result<ProvedMessages<Message<BalanceOf<BridgedChain<B>>>>, MessageProofError> where - BuildParser: FnOnce(HashOf<BridgedChain<B>>, StorageProof) -> Result<Parser, MessageProofError>, + BuildParser: FnOnce(HashOf<BridgedChain<B>>, RawStorageProof) -> Result<Parser, MessageProofError>, Parser: MessageProofParser, { - let (bridged_header_hash, bridged_storage_proof, lane_id, begin, end) = proof; + let FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane, + nonces_start, + nonces_end, + } = proof; // receiving proofs where end < begin is ok (if proof includes outbound lane state) - let messages_in_the_proof = if let Some(nonces_difference) = end.checked_sub(begin) { + let messages_in_the_proof = if let Some(nonces_difference) = nonces_end.checked_sub(nonces_start) { // let's check that the user (relayer) has passed correct `messages_count` // (this bounds maximal capacity of messages vec below) let messages_in_the_proof = nonces_difference.saturating_add(1); @@ -510,15 +568,15 @@ pub mod target { 0 }; - let parser = build_parser(bridged_header_hash, bridged_storage_proof)?; + let parser = build_parser(bridged_header_hash, storage_proof)?; // Read messages first. All messages that are claimed to be in the proof must // be in the proof. So any error in `read_value`, or even missing value is fatal. // // Mind that we allow proofs with no messages if outbound lane state is proved. let mut messages = Vec::with_capacity(messages_in_the_proof as _); - for nonce in begin..=end { - let message_key = MessageKey { lane_id, nonce }; + for nonce in nonces_start..=nonces_end { + let message_key = MessageKey { lane_id: lane, nonce }; let raw_message_data = parser .read_raw_message(&message_key) .ok_or(MessageProofError::MissingRequiredMessage)?; @@ -536,7 +594,7 @@ pub mod target { lane_state: None, messages, }; - let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane_id); + let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane); if let Some(raw_outbound_lane_data) = raw_outbound_lane_data { proved_lane_messages.lane_state = Some( OutboundLaneData::decode(&mut &raw_outbound_lane_data[..]) @@ -551,7 +609,7 @@ pub mod target { // We only support single lane messages in this schema let mut proved_messages = ProvedMessages::new(); - proved_messages.insert(lane_id, proved_lane_messages); + proved_messages.insert(lane, proved_lane_messages); Ok(proved_messages) } @@ -573,7 +631,7 @@ mod tests { const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; /// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from BridgedChain; - #[derive(Debug, PartialEq)] + #[derive(Debug, PartialEq, Eq)] struct OnThisChainBridge; impl MessageBridge for OnThisChainBridge { @@ -614,7 +672,7 @@ mod tests { } /// Bridge that is deployed on BridgedChain and allows sending/receiving messages to/from ThisChain; - #[derive(Debug, PartialEq)] + #[derive(Debug, PartialEq, Eq)] struct OnBridgedChainBridge; impl MessageBridge for OnBridgedChainBridge { @@ -1011,11 +1069,21 @@ mod tests { 1..=0 } + fn messages_proof(nonces_end: MessageNonce) -> target::FromBridgedChainMessagesProof<()> { + target::FromBridgedChainMessagesProof { + bridged_header_hash: (), + storage_proof: vec![], + lane: Default::default(), + nonces_start: 1, + nonces_end, + } + } + #[test] fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() { assert_eq!( target::verify_messages_proof_with_parser::<OnThisChainBridge, _, TestMessageProofParser>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10), + messages_proof(10), 5, |_, _| unreachable!(), ), @@ -1027,7 +1095,7 @@ mod tests { fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() { assert_eq!( target::verify_messages_proof_with_parser::<OnThisChainBridge, _, TestMessageProofParser>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10), + messages_proof(10), 15, |_, _| unreachable!(), ), @@ -1039,7 +1107,7 @@ mod tests { fn message_proof_is_rejected_if_build_parser_fails() { assert_eq!( target::verify_messages_proof_with_parser::<OnThisChainBridge, _, TestMessageProofParser>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10), + messages_proof(10), 10, |_, _| Err(target::MessageProofError::Custom("test")), ), @@ -1050,15 +1118,13 @@ mod tests { #[test] fn message_proof_is_rejected_if_required_message_is_missing() { assert_eq!( - target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10), - 10, - |_, _| Ok(TestMessageProofParser { + target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(10), 10, |_, _| Ok( + TestMessageProofParser { failing: false, messages: 1..=5, outbound_lane_data: None, - }), - ), + } + ),), Err(target::MessageProofError::MissingRequiredMessage), ); } @@ -1066,15 +1132,13 @@ mod tests { #[test] fn message_proof_is_rejected_if_message_decode_fails() { assert_eq!( - target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10), - 10, - |_, _| Ok(TestMessageProofParser { + target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(10), 10, |_, _| Ok( + TestMessageProofParser { failing: true, messages: 1..=10, outbound_lane_data: None, - }), - ), + } + ),), Err(target::MessageProofError::FailedToDecodeMessage), ); } @@ -1082,10 +1146,8 @@ mod tests { #[test] fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() { assert_eq!( - target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 0), - 0, - |_, _| Ok(TestMessageProofParser { + target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(0), 0, |_, _| Ok( + TestMessageProofParser { failing: true, messages: no_messages_range(), outbound_lane_data: Some(OutboundLaneData { @@ -1093,8 +1155,8 @@ mod tests { latest_received_nonce: 1, latest_generated_nonce: 1, }), - }), - ), + } + ),), Err(target::MessageProofError::FailedToDecodeOutboundLaneState), ); } @@ -1102,15 +1164,13 @@ mod tests { #[test] fn message_proof_is_rejected_if_it_is_empty() { assert_eq!( - target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 0), - 0, - |_, _| Ok(TestMessageProofParser { + target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(0), 0, |_, _| Ok( + TestMessageProofParser { failing: false, messages: no_messages_range(), outbound_lane_data: None, - }), - ), + } + ),), Err(target::MessageProofError::Empty), ); } @@ -1118,10 +1178,8 @@ mod tests { #[test] fn non_empty_message_proof_without_messages_is_accepted() { assert_eq!( - target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 0), - 0, - |_, _| Ok(TestMessageProofParser { + target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(0), 0, |_, _| Ok( + TestMessageProofParser { failing: false, messages: no_messages_range(), outbound_lane_data: Some(OutboundLaneData { @@ -1129,8 +1187,8 @@ mod tests { latest_received_nonce: 1, latest_generated_nonce: 1, }), - }), - ), + } + ),), Ok(vec![( Default::default(), ProvedLaneMessages { @@ -1150,10 +1208,8 @@ mod tests { #[test] fn non_empty_message_proof_is_accepted() { assert_eq!( - target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>( - (Default::default(), StorageProof::new(vec![]), Default::default(), 1, 1), - 1, - |_, _| Ok(TestMessageProofParser { + target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(1), 1, |_, _| Ok( + TestMessageProofParser { failing: false, messages: 1..=1, outbound_lane_data: Some(OutboundLaneData { @@ -1161,8 +1217,8 @@ mod tests { latest_received_nonce: 1, latest_generated_nonce: 1, }), - }), - ), + } + ),), Ok(vec![( Default::default(), ProvedLaneMessages { @@ -1192,13 +1248,7 @@ mod tests { fn verify_messages_proof_with_parser_does_not_panic_if_messages_count_mismatches() { assert_eq!( target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>( - ( - Default::default(), - StorageProof::new(vec![]), - Default::default(), - 0, - u64::MAX - ), + messages_proof(u64::MAX), 0, |_, _| Ok(TestMessageProofParser { failing: false, diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index fdae3a7fcc9..4aa2abbd6b4 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -32,9 +32,7 @@ use pallet_message_lane::benchmarking::{MessageDeliveryProofParams, MessageProof use sp_core::Hasher; use sp_runtime::traits::Header; use sp_std::prelude::*; -use sp_trie::{ - read_trie_value_with, record_all_keys, trie_types::TrieDBMut, Layout, MemoryDB, Recorder, StorageProof, TrieMut, -}; +use sp_trie::{record_all_keys, trie_types::TrieDBMut, Layout, MemoryDB, Recorder, TrieMut}; /// Generate ed25519 signature to be used in `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`. /// @@ -71,7 +69,7 @@ pub fn prepare_message_proof<B, H, R, MM, ML, MH>( make_bridged_header: MH, message_dispatch_weight: Weight, message_payload: MessagePayload, -) -> (FromBridgedChainMessagesProof<B>, Weight) +) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight) where B: MessageBridge, H: Hasher, @@ -134,13 +132,13 @@ where pallet_substrate_bridge::initialize_for_benchmarks::<R>(bridged_header); ( - ( - bridged_header_hash.into(), - StorageProof::new(storage_proof), - params.lane, - *params.message_nonces.start(), - *params.message_nonces.end(), - ), + FromBridgedChainMessagesProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane: params.lane, + nonces_start: *params.message_nonces.start(), + nonces_end: *params.message_nonces.end(), + }, message_dispatch_weight .checked_mul(message_count) .expect("too many messages requested by benchmark"), @@ -152,7 +150,7 @@ pub fn prepare_message_delivery_proof<B, H, R, ML, MH>( params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>, make_bridged_inbound_lane_data_key: ML, make_bridged_header: MH, -) -> FromBridgedChainMessagesDeliveryProof<B> +) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>> where B: MessageBridge, H: Hasher, @@ -171,12 +169,13 @@ where .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in benchmarks"); } + root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain let mut proof_recorder = Recorder::<H::Out>::new(); - read_trie_value_with::<Layout<H>, _, _>(&mdb, &root, &storage_key, &mut proof_recorder) - .map_err(|_| "read_trie_value_with has failed") - .expect("read_trie_value_with should not fail in benchmarks"); + record_all_keys::<Layout<H>, _>(&mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_keys has failed") + .expect("record_all_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); // prepare Bridged chain header and insert it into the Substrate pallet @@ -184,17 +183,17 @@ where let bridged_header_hash = bridged_header.hash(); pallet_substrate_bridge::initialize_for_benchmarks::<R>(bridged_header); - ( - bridged_header_hash.into(), - StorageProof::new(storage_proof), - params.lane, - ) + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane: params.lane, + } } -/// Populate trie with dummy keys+values until trie has (approximately) at least given size. +/// Populate trie with dummy keys+values until trie has at least given size. fn grow_trie<H: Hasher>(mut root: H::Out, mdb: &mut MemoryDB<H>, trie_size: ProofSize) -> H::Out { let (iterations, leaf_size, minimal_trie_size) = match trie_size { - ProofSize::Minimal => return root, + ProofSize::Minimal(_) => return root, ProofSize::HasLargeLeaf(size) => (1, size, size), ProofSize::HasExtraNodes(size) => (8, 1, size), }; diff --git a/bridges/modules/message-lane/Cargo.toml b/bridges/modules/message-lane/Cargo.toml index 300b0493912..83721a645fd 100644 --- a/bridges/modules/message-lane/Cargo.toml +++ b/bridges/modules/message-lane/Cargo.toml @@ -14,6 +14,7 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] } # Bridge dependencies bp-message-lane = { path = "../../primitives/message-lane", default-features = false } +bp-rialto = { path = "../../primitives/rialto", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Dependencies @@ -35,6 +36,7 @@ default = ["std"] std = [ "bp-message-lane/std", "bp-runtime/std", + "bp-rialto/std", "codec/std", "frame-support/std", "frame-system/std", diff --git a/bridges/modules/message-lane/README.md b/bridges/modules/message-lane/README.md new file mode 100644 index 00000000000..3213aafe251 --- /dev/null +++ b/bridges/modules/message-lane/README.md @@ -0,0 +1,132 @@ +# Message Lane Module + +The Message Lane Module is used to deliver messages from source to target chain. Message is (almost) opaque to the module and the final goal is to hand message to the message dispatch mechanism. + +## Overview + +*In progress* + +## Weights of module extrinsics + +The main assumptions behind weight formulas is: +- all possible costs are paid in advance by the message submitter; +- whenever possible, relayer tries to minimize cost of its transactions. So e.g. even though sender always pays for delivering outbound lane state proof, relayer may not include it in the delivery transaction (unless message lane module on target chain requires that); +- weight formula should incentivize relayer to not to submit any redundand data in the extrinsics arguments; +- the extrinsic shall never be executing slower (i.e. has larger actual weight) than defined by the formula. + +### Weight of `send_message` call + +#### Related benchmarks + +| Benchmark | Description | +|-----------------------------------|--------------------------------------------------------| +| `send_minimal_message_worst_case` | Sends 0-size message with worst possible conditions | +| `send_1_kb_message_worst_case` | Sends 1KB-size message with worst possible conditions | +| `send_16_kb_message_worst_case` | Sends 16KB-size message with worst possible conditions | + +#### Weight formula + +The weight formula is: +``` +Weight = BaseWeight + MessageSizeInKilobytes * MessageKiloByteSendWeight +``` + +Where: + +| Component | How it is computed? | Description | +|-----------------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| +| `SendMessageOverhead` | `send_minimal_message_worst_case` | Weight of sending minimal (0 bytes) message | +| `MessageKiloByteSendWeight` | `(send_16_kb_message_worst_case - send_1_kb_message_worst_case)/15` | Weight of sending every additional kilobyte of the message | + +### Weight of `receive_messages_proof` call + +#### Related benchmarks + +| Benchmark | Description* | +|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| `receive_single_message_proof` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message | +| `receive_two_messages_proof` | Receives proof of two identical `EXPECTED_DEFAULT_MESSAGE_LENGTH` messages | +| `receive_single_message_proof_with_outbound_lane_state` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message and proof of outbound lane state at the source chain | +| `receive_single_message_proof_1_kb` | Receives proof of single message. The proof has size of approximately 1KB** | +| `receive_single_message_proof_16_kb` | Receives proof of single message. The proof has size of approximately 16KB** | + +*\* - In all benchmarks all received messages are dispatched and their dispatch cost is near to zero* + +*\*\* - Trie leafs are assumed to have minimal values. The proof is derived from the minimal proof by including more trie nodes. That's because according to `receive_message_proofs_with_large_leaf` and `receive_message_proofs_with_extra_nodes` benchmarks, increasing proof by including more nodes has slightly larger impact on performance than increasing values stored in leafs*. + +#### Weight formula + +The weight formula is: +``` +Weight = BaseWeight + OutboundStateDeliveryWeight + MessagesCount * MessageDeliveryWeight + MessagesDispatchWeight + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight +``` + +Where: + +| Component | How it is computed? | Description | +|-------------------------------|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `BaseWeight` | `2*receive_single_message_proof - receive_two_messages_proof` | Weight of receiving and parsing minimal proof | +| `OutboundStateDeliveryWeight` | `receive_single_message_proof_with_outbound_lane_state - receive_single_message_proof` | Additional weight when proof includes outbound lane state | +| `MessageDeliveryWeight` | `receive_two_messages_proof - receive_single_message_proof` | Weight of of parsing and dispatching (without actual dispatch cost) of every message | +| `MessagesCount` | | Provided by relayer | +| `MessagesDispatchWeight` | | Provided by relayer | +| `ActualProofSize` | | Provided by relayer | +| `ExpectedProofSize` | `EXPECTED_DEFAULT_MESSAGE_LENGTH * MessagesCount + EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting. This only includes `EXTRA_STORAGE_PROOF_SIZE` once, because we assume that intermediate nodes likely to be included in the proof only once. This may be wrong, but since weight of processing proof with many nodes is almost equal to processing proof with large leafs, additional cost will be covered because we're charging for extra proof bytes anyway | +| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit | + +#### Why for every message sent using `send_message` we will be able to craft `receive_messages_proof` transaction? + +We have following checks in `send_message` transaction on the source chain: +- message size should be less than or equal to `2/3` of maximal extrinsic size on the target chain; +- message dispatch weight should be less than or equal to the `1/2` of maximal extrinsic dispatch weight on the target chain. + +Delivery transaction is an encoded delivery call and signed extensions. So we have `1/3` of maximal extrinsic size reserved for: +- storage proof, excluding the message itself. Currently, on our test chains, the overhead is always within `EXTRA_STORAGE_PROOF_SIZE` limits (1024 bytes); +- signed extras and other call arguments (`relayer_id: SourceChain::AccountId`, `messages_count: u32`, `dispatch_weight: u64`). + +On Millau chain, maximal extrinsic size is `0.75 * 2MB`, so `1/3` is `512KB` (`524_288` bytes). This should be enough to cover these extra arguments and signed extensions. + +Let's exclude message dispatch cost from single message delivery transaction weight formula: +``` +Weight = BaseWeight + OutboundStateDeliveryWeight + MessageDeliveryWeight + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight +``` + +So we have `1/2` of maximal extrinsic weight to cover these components. `BaseWeight`, `OutboundStateDeliveryWeight` and `MessageDeliveryWeight` are determined using benchmarks and are hardcoded into runtime. Adequate relayer would only include required trie nodes into the proof. So if message size would be maximal (`2/3` of `MaximalExtrinsicSize`), then the extra proof size would be `MaximalExtrinsicSize / 3 * 2 - EXPECTED_DEFAULT_MESSAGE_LENGTH`. + +Both conditions are verified by `pallet_message_lane::ensure_weights_are_correct` and `pallet_message_lane::ensure_able_to_receive_messages` functions, which must be called from every runtime' tests. + +### Weight of `receive_messages_delivery_proof` call + +#### Related benchmarks + +| Benchmark | Description | +|-------------------------------------------------------------|------------------------------------------------------------------------------------------| +| `receive_delivery_proof_for_single_message` | Receives proof of single message delivery | +| `receive_delivery_proof_for_two_messages_by_single_relayer` | Receives proof of two messages delivery. Both messages are delivered by the same relayer | +| `receive_delivery_proof_for_two_messages_by_two_relayers` | Receives proof of two messages delivery. Messages are delivered by different relayers | + +#### Weight formula + +The weight formula is: +``` +Weight = BaseWeight + MessagesCount * MessageConfirmationWeight + RelayersCount * RelayerRewardWeight + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight +``` + +Where: + +| Component | How it is computed? | Description | +|---------------------------|-----------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `BaseWeight` | `2*receive_delivery_proof_for_single_message - receive_delivery_proof_for_two_messages_by_single_relayer` | Weight of receiving and parsing minimal delivery proof | +| `MessageDeliveryWeight` | `receive_delivery_proof_for_two_messages_by_single_relayer - receive_delivery_proof_for_single_message` | Weight of confirming every additional message | +| `MessagesCount` | | Provided by relayer | +| `RelayerRewardWeight` | `receive_delivery_proof_for_two_messages_by_two_relayers - receive_delivery_proof_for_two_messages_by_single_relayer` | Weight of rewarding every additional relayer | +| `RelayersCount` | | Provided by relayer | +| `ActualProofSize` | | Provided by relayer | +| `ExpectedProofSize` | `EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting | +| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit. We're using the same formula, as for message delivery, because proof mechanism is assumed to be the same in both cases | + +#### Why we're always able to craft `receive_messages_delivery_proof` transaction? + +There can be at most `<PeerRuntime as pallet_message_lane::Config>::MaxUnconfirmedMessagesAtInboundLane` messages and at most `<PeerRuntime as pallet_message_lane::Config>::MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers in the single delivery confirmation transaction. + +We're checking that this transaction may be crafted in the `pallet_message_lane::ensure_able_to_receive_confirmation` function, which must be called from every runtime' tests. diff --git a/bridges/modules/message-lane/src/benchmarking.rs b/bridges/modules/message-lane/src/benchmarking.rs index 0ad445385f2..675bc310f59 100644 --- a/bridges/modules/message-lane/src/benchmarking.rs +++ b/bridges/modules/message-lane/src/benchmarking.rs @@ -16,6 +16,7 @@ //! Message lane pallet benchmarking. +use crate::weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH; use crate::{inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, Call, Instance}; use bp_message_lane::{ @@ -37,8 +38,9 @@ pub struct Module<T: Config<I>, I: crate::Instance>(crate::Module<T, I>); /// Proof size requirements. pub enum ProofSize { - /// The proof is expected to be minimal. - Minimal, + /// The proof is expected to be minimal. If value size may be changed, then it is expected to + /// have given size. + Minimal(u32), /// The proof is expected to have at least given size and grow by increasing number of trie nodes /// included in the proof. HasExtraNodes(u32), @@ -73,6 +75,8 @@ pub struct MessageDeliveryProofParams<ThisChainAccountId> { pub lane: LaneId, /// The proof needs to include this inbound lane data. pub inbound_lane_data: InboundLaneData<ThisChainAccountId>, + /// Proof size requirements. + pub size: ProofSize, } /// Trait that must be implemented by runtime. @@ -224,17 +228,20 @@ benchmarks_instance! { let relayer_id_on_source = T::bridged_relayer_id(); let relayer_id_on_target = account("relayer", 0, SEED); + // mark messages 1..=20 as delivered + receive_messages::<T, I>(20); + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { lane: bench_lane_id(), - message_nonces: 1..=1, + message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::Minimal, + size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { assert_eq!( crate::Module::<T, I>::inbound_latest_received_nonce(bench_lane_id()), - 1, + 21, ); } @@ -252,17 +259,20 @@ benchmarks_instance! { let relayer_id_on_source = T::bridged_relayer_id(); let relayer_id_on_target = account("relayer", 0, SEED); + // mark messages 1..=20 as delivered + receive_messages::<T, I>(20); + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { lane: bench_lane_id(), - message_nonces: 1..=2, + message_nonces: 21..=22, outbound_lane_data: None, - size: ProofSize::Minimal, + size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) verify { assert_eq!( crate::Module::<T, I>::inbound_latest_received_nonce(bench_lane_id()), - 2, + 22, ); } @@ -291,7 +301,7 @@ benchmarks_instance! { latest_received_nonce: 20, latest_generated_nonce: 21, }), - size: ProofSize::Minimal, + size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { @@ -305,6 +315,68 @@ benchmarks_instance! { ); } + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * the proof has many redundand trie nodes with total size of approximately 1KB; + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher. + // + // With single KB of messages proof, the weight of the call is increased (roughly) by + // `(receive_single_message_proof_16KB - receive_single_message_proof_1_kb) / 15`. + receive_single_message_proof_1_kb { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + + // mark messages 1..=20 as delivered + receive_messages::<T, I>(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + size: ProofSize::HasExtraNodes(1024), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::Module::<T, I>::inbound_latest_received_nonce(bench_lane_id()), + 21, + ); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * the proof has many redundand trie nodes with total size of approximately 16KB; + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher. + // + // Size of proof grows because it contains extra trie nodes in it. + // + // With single KB of messages proof, the weight of the call is increased (roughly) by + // `(receive_single_message_proof_16KB - receive_single_message_proof) / 15`. + receive_single_message_proof_16_kb { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + + // mark messages 1..=20 as delivered + receive_messages::<T, I>(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + size: ProofSize::HasExtraNodes(16 * 1024), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::Module::<T, I>::inbound_latest_received_nonce(bench_lane_id()), + 21, + ); + } + // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: // * single relayer is rewarded for relaying single message; // * relayer account does not exist (in practice it needs to exist in production environment). @@ -329,7 +401,8 @@ benchmarks_instance! { inbound_lane_data: InboundLaneData { relayers: vec![(1, 1, relayer_id.clone())].into_iter().collect(), last_confirmed_nonce: 0, - } + }, + size: ProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { @@ -366,7 +439,8 @@ benchmarks_instance! { inbound_lane_data: InboundLaneData { relayers: vec![(1, 2, relayer_id.clone())].into_iter().collect(), last_confirmed_nonce: 0, - } + }, + size: ProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { @@ -405,7 +479,8 @@ benchmarks_instance! { (2, 2, relayer2_id.clone()), ].into_iter().collect(), last_confirmed_nonce: 0, - } + }, + size: ProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) verify { @@ -459,17 +534,20 @@ benchmarks_instance! { // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. So it may be used // to verify that the other approximation is correct. receive_multiple_messages_proof { - let i in 1..128; + let i in 1..64; let relayer_id_on_source = T::bridged_relayer_id(); let relayer_id_on_target = account("relayer", 0, SEED); let messages_count = i as _; + // mark messages 1..=20 as delivered + receive_messages::<T, I>(20); + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { lane: bench_lane_id(), - message_nonces: 1..=i as _, + message_nonces: 21..=(20 + i as MessageNonce), outbound_lane_data: None, - size: ProofSize::Minimal, + size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), }); }: receive_messages_proof( RawOrigin::Signed(relayer_id_on_target), @@ -481,38 +559,32 @@ benchmarks_instance! { verify { assert_eq!( crate::Module::<T, I>::inbound_latest_received_nonce(bench_lane_id()), - i as MessageNonce, + 20 + i as MessageNonce, ); } - // Benchmark `receive_messages_proof` extrinsic with multiple minimal-weight messages and following conditions: - // * proof includes outbound lane state proof; + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * proof does not include outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; // * message is successfully dispatched; // * message requires all heavy checks done by dispatcher. // - // This benchmarks gives us an approximation of outbound lane state delivery weight. It is similar to the - // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. - // So it may be used to verify that the other approximation is correct. - receive_multiple_messages_proof_with_outbound_lane_state { - let i in 1..128; + // Results of this benchmark may be used to check how proof size affects `receive_message_proof` performance. + receive_message_proofs_with_extra_nodes { + let i in 0..T::maximal_message_size(); let relayer_id_on_source = T::bridged_relayer_id(); let relayer_id_on_target = account("relayer", 0, SEED); - let messages_count = i as _; + let messages_count = 1u32; // mark messages 1..=20 as delivered receive_messages::<T, I>(20); let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { lane: bench_lane_id(), - message_nonces: 21..=20 + i as MessageNonce, - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 21, - latest_received_nonce: 20, - latest_generated_nonce: 21, - }), - size: ProofSize::Minimal, + message_nonces: 21..=21, + outbound_lane_data: None, + size: ProofSize::HasExtraNodes(i as _), }); }: receive_messages_proof( RawOrigin::Signed(relayer_id_on_target), @@ -524,11 +596,7 @@ benchmarks_instance! { verify { assert_eq!( crate::Module::<T, I>::inbound_latest_received_nonce(bench_lane_id()), - 20 + i as MessageNonce, - ); - assert_eq!( - crate::Module::<T, I>::inbound_latest_confirmed_nonce(bench_lane_id()), - 20, + 21, ); } @@ -538,8 +606,8 @@ benchmarks_instance! { // * message is successfully dispatched; // * message requires all heavy checks done by dispatcher. // - // Results of this benchmark may be used to check how extra nodes in proof affect transaction performance. - receive_message_proofs_with_extra_nodes { + // Results of this benchmark may be used to check how message size affects `receive_message_proof` performance. + receive_message_proofs_with_large_leaf { let i in 0..T::maximal_message_size(); let relayer_id_on_source = T::bridged_relayer_id(); @@ -553,7 +621,7 @@ benchmarks_instance! { lane: bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::HasExtraNodes(i as _), + size: ProofSize::HasLargeLeaf(i as _), }); }: receive_messages_proof( RawOrigin::Signed(relayer_id_on_target), @@ -569,28 +637,34 @@ benchmarks_instance! { ); } - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * proof does not include outbound lane state proof; + // Benchmark `receive_messages_proof` extrinsic with multiple minimal-weight messages and following conditions: + // * proof includes outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; // * message is successfully dispatched; // * message requires all heavy checks done by dispatcher. // - // Results of this benchmark may be used to check how large (extra) leaf in proof affect transaction performance. - receive_message_proofs_with_large_leaf { - let i in 0..T::maximal_message_size(); + // This benchmarks gives us an approximation of outbound lane state delivery weight. It is similar to the + // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. + // So it may be used to verify that the other approximation is correct. + receive_multiple_messages_proof_with_outbound_lane_state { + let i in 1..128; let relayer_id_on_source = T::bridged_relayer_id(); let relayer_id_on_target = account("relayer", 0, SEED); - let messages_count = 1u32; + let messages_count = i as _; // mark messages 1..=20 as delivered receive_messages::<T, I>(20); let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { lane: bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - size: ProofSize::HasLargeLeaf(i as _), + message_nonces: 21..=20 + i as MessageNonce, + outbound_lane_data: Some(OutboundLaneData { + oldest_unpruned_nonce: 21, + latest_received_nonce: 20, + latest_generated_nonce: 21, + }), + size: ProofSize::Minimal(0), }); }: receive_messages_proof( RawOrigin::Signed(relayer_id_on_target), @@ -602,7 +676,11 @@ benchmarks_instance! { verify { assert_eq!( crate::Module::<T, I>::inbound_latest_received_nonce(bench_lane_id()), - 21, + 20 + i as MessageNonce, + ); + assert_eq!( + crate::Module::<T, I>::inbound_latest_confirmed_nonce(bench_lane_id()), + 20, ); } @@ -634,7 +712,8 @@ benchmarks_instance! { inbound_lane_data: InboundLaneData { relayers: vec![(1, i as MessageNonce, relayer_id.clone())].into_iter().collect(), last_confirmed_nonce: 0, - } + }, + size: ProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { @@ -679,7 +758,8 @@ benchmarks_instance! { .map(|(j, relayer_id)| (j as MessageNonce + 1, j as MessageNonce + 1, relayer_id.clone())) .collect(), last_confirmed_nonce: 0, - } + }, + size: ProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(confirmation_relayer_id), proof, relayers_state) verify { diff --git a/bridges/modules/message-lane/src/lib.rs b/bridges/modules/message-lane/src/lib.rs index eccebc1fcc9..c14ccdb4554 100644 --- a/bridges/modules/message-lane/src/lib.rs +++ b/bridges/modules/message-lane/src/lib.rs @@ -35,7 +35,10 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub use crate::weights_ext::{ensure_weights_are_correct, WeightInfoExt}; +pub use crate::weights_ext::{ + ensure_able_to_receive_confirmation, ensure_able_to_receive_message, ensure_weights_are_correct, WeightInfoExt, + EXPECTED_DEFAULT_MESSAGE_LENGTH, +}; use crate::inbound_lane::{InboundLane, InboundLaneStorage}; use crate::outbound_lane::{OutboundLane, OutboundLaneStorage}; @@ -267,9 +270,7 @@ decl_module! { } /// Send message over lane. - #[weight = T::WeightInfo::send_message_overhead() - .saturating_add(T::WeightInfo::send_message_size_overhead(Size::size_hint(payload))) - ] + #[weight = T::WeightInfo::send_message_weight(payload)] pub fn send_message( origin, lane_id: LaneId, @@ -351,11 +352,7 @@ decl_module! { /// The weight of the call assumes that the transaction always brings outbound lane /// state update. Because of that, the submitter (relayer) has no benefit of not including /// this data in the transaction, so reward confirmations lags should be minimal. - #[weight = T::WeightInfo::receive_messages_proof_overhead() - .saturating_add(T::WeightInfo::receive_messages_proof_outbound_lane_state_overhead()) - .saturating_add(T::WeightInfo::receive_messages_proof_messages_overhead(MessageNonce::from(*messages_count))) - .saturating_add(*dispatch_weight) - ] + #[weight = T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight)] pub fn receive_messages_proof( origin, relayer_id: T::InboundRelayer, @@ -444,14 +441,7 @@ decl_module! { } /// Receive messages delivery proof from bridged chain. - #[weight = T::WeightInfo::receive_messages_delivery_proof_overhead() - .saturating_add(T::WeightInfo::receive_messages_delivery_proof_messages_overhead( - relayers_state.total_messages - )) - .saturating_add(T::WeightInfo::receive_messages_delivery_proof_relayers_overhead( - relayers_state.unrewarded_relayer_entries - )) - ] + #[weight = T::WeightInfo::receive_messages_delivery_proof_weight(proof, relayers_state)] pub fn receive_messages_delivery_proof( origin, proof: MessagesDeliveryProofOf<T, I>, @@ -774,8 +764,9 @@ fn verify_and_decode_messages_proof<Chain: SourceHeaderChain<Fee>, Fee, Dispatch mod tests { use super::*; use crate::mock::{ - message, run_test, Origin, TestEvent, TestMessageDeliveryAndDispatchPayment, TestMessagesProof, TestPayload, - TestRuntime, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, + message, run_test, Origin, TestEvent, TestMessageDeliveryAndDispatchPayment, TestMessagesDeliveryProof, + TestMessagesProof, TestPayload, TestRuntime, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, + TEST_RELAYER_A, TEST_RELAYER_B, }; use bp_message_lane::UnrewardedRelayersState; use frame_support::{assert_noop, assert_ok}; @@ -814,13 +805,13 @@ mod tests { assert_ok!(Module::<TestRuntime>::receive_messages_delivery_proof( Origin::signed(1), - Ok(( + TestMessagesDeliveryProof(Ok(( TEST_LANE_ID, InboundLaneData { last_confirmed_nonce: 1, ..Default::default() }, - )), + ))), Default::default(), )); @@ -924,13 +915,13 @@ mod tests { assert_noop!( Module::<TestRuntime>::receive_messages_delivery_proof( Origin::signed(1), - Ok(( + TestMessagesDeliveryProof(Ok(( TEST_LANE_ID, InboundLaneData { last_confirmed_nonce: 1, ..Default::default() }, - )), + ))), Default::default(), ), Error::<TestRuntime, DefaultInstance>::Halted, @@ -1140,13 +1131,13 @@ mod tests { // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A assert_ok!(Module::<TestRuntime>::receive_messages_delivery_proof( Origin::signed(1), - Ok(( + TestMessagesDeliveryProof(Ok(( TEST_LANE_ID, InboundLaneData { relayers: vec![(1, 1, TEST_RELAYER_A)].into_iter().collect(), ..Default::default() } - )), + ))), UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, @@ -1165,7 +1156,7 @@ mod tests { // this reports delivery of both message 1 and message 2 => reward is paid only to TEST_RELAYER_B assert_ok!(Module::<TestRuntime>::receive_messages_delivery_proof( Origin::signed(1), - Ok(( + TestMessagesDeliveryProof(Ok(( TEST_LANE_ID, InboundLaneData { relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)] @@ -1173,7 +1164,7 @@ mod tests { .collect(), ..Default::default() } - )), + ))), UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, @@ -1195,7 +1186,11 @@ mod tests { fn receive_messages_delivery_proof_rejects_invalid_proof() { run_test(|| { assert_noop!( - Module::<TestRuntime>::receive_messages_delivery_proof(Origin::signed(1), Err(()), Default::default(),), + Module::<TestRuntime>::receive_messages_delivery_proof( + Origin::signed(1), + TestMessagesDeliveryProof(Err(())), + Default::default(), + ), Error::<TestRuntime, DefaultInstance>::InvalidMessagesDeliveryProof, ); }); @@ -1208,7 +1203,7 @@ mod tests { assert_noop!( Module::<TestRuntime>::receive_messages_delivery_proof( Origin::signed(1), - Ok(( + TestMessagesDeliveryProof(Ok(( TEST_LANE_ID, InboundLaneData { relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)] @@ -1216,7 +1211,7 @@ mod tests { .collect(), ..Default::default() } - )), + ))), UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 2, @@ -1230,7 +1225,7 @@ mod tests { assert_noop!( Module::<TestRuntime>::receive_messages_delivery_proof( Origin::signed(1), - Ok(( + TestMessagesDeliveryProof(Ok(( TEST_LANE_ID, InboundLaneData { relayers: vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B)] @@ -1238,7 +1233,7 @@ mod tests { .collect(), ..Default::default() } - )), + ))), UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 1, diff --git a/bridges/modules/message-lane/src/mock.rs b/bridges/modules/message-lane/src/mock.rs index 94f570a84d3..4d1bb488908 100644 --- a/bridges/modules/message-lane/src/mock.rs +++ b/bridges/modules/message-lane/src/mock.rs @@ -183,6 +183,12 @@ pub struct TestMessagesProof { pub result: Result<MessagesByLaneVec, ()>, } +impl Size for TestMessagesProof { + fn size_hint(&self) -> u32 { + 0 + } +} + impl From<Result<Vec<Message<TestMessageFee>>, ()>> for TestMessagesProof { fn from(result: Result<Vec<Message<TestMessageFee>>, ()>) -> Self { Self { @@ -202,6 +208,16 @@ impl From<Result<Vec<Message<TestMessageFee>>, ()>> for TestMessagesProof { } } +/// Messages delivery proof used in tests. +#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq)] +pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData<TestRelayer>), ()>); + +impl Size for TestMessagesDeliveryProof { + fn size_hint(&self) -> u32 { + 0 + } +} + /// Target header chain that is used in tests. #[derive(Debug, Default)] pub struct TestTargetHeaderChain; @@ -209,7 +225,7 @@ pub struct TestTargetHeaderChain; impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain { type Error = &'static str; - type MessagesDeliveryProof = Result<(LaneId, InboundLaneData<TestRelayer>), ()>; + type MessagesDeliveryProof = TestMessagesDeliveryProof; fn verify_message(payload: &TestPayload) -> Result<(), Self::Error> { if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN { @@ -222,7 +238,7 @@ impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain { fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData<TestRelayer>), Self::Error> { - proof.map_err(|_| TEST_ERROR) + proof.0.map_err(|_| TEST_ERROR) } } diff --git a/bridges/modules/message-lane/src/weights.rs b/bridges/modules/message-lane/src/weights.rs index 33a84ecbafa..44ce61020ee 100644 --- a/bridges/modules/message-lane/src/weights.rs +++ b/bridges/modules/message-lane/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for pallet_message_lane //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.1 -//! DATE: 2021-01-25, STEPS: [50, ], REPEAT: 20 +//! DATE: 2021-02-01, STEPS: [50, ], REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("local"), DB CACHE: 128 @@ -54,14 +54,16 @@ pub trait WeightInfo { fn receive_single_message_proof() -> Weight; fn receive_two_messages_proof() -> Weight; fn receive_single_message_proof_with_outbound_lane_state() -> Weight; + fn receive_single_message_proof_1_kb() -> Weight; + fn receive_single_message_proof_16_kb() -> Weight; fn receive_delivery_proof_for_single_message() -> Weight; fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; fn send_messages_of_various_lengths(i: u32) -> Weight; fn receive_multiple_messages_proof(i: u32) -> Weight; - fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight; fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight; fn receive_message_proofs_with_large_leaf(i: u32) -> Weight; + fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight; fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight; fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight; } @@ -70,90 +72,100 @@ pub trait WeightInfo { pub struct RialtoWeight<T>(PhantomData<T>); impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> { fn send_minimal_message_worst_case() -> Weight { - (123_511_000 as Weight) + (138_421_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight)) } fn send_1_kb_message_worst_case() -> Weight { - (132_218_000 as Weight) + (142_633_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight)) } fn send_16_kb_message_worst_case() -> Weight { - (187_458_000 as Weight) + (194_483_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight)) } fn receive_single_message_proof() -> Weight { - (156_005_000 as Weight) + (154_651_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_two_messages_proof() -> Weight { - (266_292_000 as Weight) + (271_722_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - (171_319_000 as Weight) + (170_821_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn receive_single_message_proof_1_kb() -> Weight { + (189_540_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn receive_single_message_proof_16_kb() -> Weight { + (484_899_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_single_message() -> Weight { - (127_537_000 as Weight) + (145_328_000 as Weight) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - (135_281_000 as Weight) + (150_165_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - (180_862_000 as Weight) + (215_954_000 as Weight) .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn send_messages_of_various_lengths(i: u32) -> Weight { - (98_452_000 as Weight) + (117_961_000 as Weight) .saturating_add((3_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight)) } fn receive_multiple_messages_proof(i: u32) -> Weight { (0 as Weight) - .saturating_add((124_098_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { - (0 as Weight) - .saturating_add((128_267_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((117_783_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight { - (437_756_000 as Weight) + (448_951_000 as Weight) .saturating_add((10_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_message_proofs_with_large_leaf(i: u32) -> Weight { - (164_484_000 as Weight) + (97_174_000 as Weight) .saturating_add((7_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } + fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { + (0 as Weight) + .saturating_add((120_176_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight { - (122_609_000 as Weight) - .saturating_add((7_289_000 as Weight).saturating_mul(i as Weight)) + (132_970_000 as Weight) + .saturating_add((7_243_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight { - (129_622_000 as Weight) - .saturating_add((53_214_000 as Weight).saturating_mul(i as Weight)) + (62_936_000 as Weight) + .saturating_add((67_932_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(i as Weight))) .saturating_add(T::DbWeight::get().writes(3 as Weight)) @@ -164,90 +176,100 @@ impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> { // For backwards compatibility and tests impl WeightInfo for () { fn send_minimal_message_worst_case() -> Weight { - (123_511_000 as Weight) + (138_421_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight)) } fn send_1_kb_message_worst_case() -> Weight { - (132_218_000 as Weight) + (142_633_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight)) } fn send_16_kb_message_worst_case() -> Weight { - (187_458_000 as Weight) + (194_483_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight)) } fn receive_single_message_proof() -> Weight { - (156_005_000 as Weight) + (154_651_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_two_messages_proof() -> Weight { - (266_292_000 as Weight) + (271_722_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - (171_319_000 as Weight) + (170_821_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn receive_single_message_proof_1_kb() -> Weight { + (189_540_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn receive_single_message_proof_16_kb() -> Weight { + (484_899_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_single_message() -> Weight { - (127_537_000 as Weight) + (145_328_000 as Weight) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - (135_281_000 as Weight) + (150_165_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - (180_862_000 as Weight) + (215_954_000 as Weight) .saturating_add(RocksDbWeight::get().reads(8 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn send_messages_of_various_lengths(i: u32) -> Weight { - (98_452_000 as Weight) + (117_961_000 as Weight) .saturating_add((3_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight)) } fn receive_multiple_messages_proof(i: u32) -> Weight { (0 as Weight) - .saturating_add((124_098_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { - (0 as Weight) - .saturating_add((128_267_000 as Weight).saturating_mul(i as Weight)) + .saturating_add((117_783_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight { - (437_756_000 as Weight) + (448_951_000 as Weight) .saturating_add((10_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_message_proofs_with_large_leaf(i: u32) -> Weight { - (164_484_000 as Weight) + (97_174_000 as Weight) .saturating_add((7_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { + (0 as Weight) + .saturating_add((120_176_000 as Weight).saturating_mul(i as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight { - (122_609_000 as Weight) - .saturating_add((7_289_000 as Weight).saturating_mul(i as Weight)) + (132_970_000 as Weight) + .saturating_add((7_243_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight { - (129_622_000 as Weight) - .saturating_add((53_214_000 as Weight).saturating_mul(i as Weight)) + (62_936_000 as Weight) + .saturating_add((67_932_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(i as Weight))) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) diff --git a/bridges/modules/message-lane/src/weights_ext.rs b/bridges/modules/message-lane/src/weights_ext.rs index 95bc8239a94..18d80159a1b 100644 --- a/bridges/modules/message-lane/src/weights_ext.rs +++ b/bridges/modules/message-lane/src/weights_ext.rs @@ -18,52 +18,213 @@ use crate::weights::WeightInfo; -use bp_message_lane::MessageNonce; +use bp_message_lane::{MessageNonce, UnrewardedRelayersState}; +use bp_runtime::{PreComputedSize, Size}; use frame_support::weights::Weight; +/// Size of the message being delivered in benchmarks. +pub const EXPECTED_DEFAULT_MESSAGE_LENGTH: u32 = 128; + +/// We assume that size of signed extensions on all our chains and size of all 'small' arguments of calls +/// we're checking here would fit 1KB. +const SIGNED_EXTENSIONS_SIZE: u32 = 1024; + /// Ensure that weights from `WeightInfoExt` implementation are looking correct. pub fn ensure_weights_are_correct<W: WeightInfoExt>( - expected_max_single_message_delivery_tx_weight: Weight, - expected_max_messages_delivery_tx_weight: Weight, + expected_single_regular_message_delivery_tx_weight: Weight, + expected_messages_delivery_confirmation_tx_weight: Weight, ) { + // verify `send_message` weight components assert_ne!(W::send_message_overhead(), 0); assert_ne!(W::send_message_size_overhead(0), 0); + // verify `receive_messages_proof` weight components assert_ne!(W::receive_messages_proof_overhead(), 0); assert_ne!(W::receive_messages_proof_messages_overhead(1), 0); assert_ne!(W::receive_messages_proof_outbound_lane_state_overhead(), 0); + assert_ne!(W::storage_proof_size_overhead(1), 0); - let actual_max_single_message_delivery_tx_weight = W::receive_messages_proof_overhead() - .checked_add(W::receive_messages_proof_messages_overhead(1)) - .expect("weights are too large") - .checked_add(W::receive_messages_proof_outbound_lane_state_overhead()) - .expect("weights are too large"); + // verify that the hardcoded value covers `receive_messages_proof` weight + let actual_single_regular_message_delivery_tx_weight = W::receive_messages_proof_weight( + &PreComputedSize((EXPECTED_DEFAULT_MESSAGE_LENGTH + W::expected_extra_storage_proof_size()) as usize), + 1, + 0, + ); assert!( - actual_max_single_message_delivery_tx_weight <= expected_max_single_message_delivery_tx_weight, + actual_single_regular_message_delivery_tx_weight <= expected_single_regular_message_delivery_tx_weight, "Single message delivery transaction weight {} is larger than expected weight {}", - actual_max_single_message_delivery_tx_weight, - expected_max_single_message_delivery_tx_weight, + actual_single_regular_message_delivery_tx_weight, + expected_single_regular_message_delivery_tx_weight, ); + // verify `receive_messages_delivery_proof` weight components assert_ne!(W::receive_messages_delivery_proof_overhead(), 0); assert_ne!(W::receive_messages_delivery_proof_messages_overhead(1), 0); assert_ne!(W::receive_messages_delivery_proof_relayers_overhead(1), 0); + assert_ne!(W::storage_proof_size_overhead(1), 0); - let actual_max_messages_delivery_tx_weight = W::receive_messages_delivery_proof_overhead() - .checked_add(W::receive_messages_delivery_proof_messages_overhead(1)) - .expect("weights are too large") - .checked_add(W::receive_messages_delivery_proof_relayers_overhead(1)) - .expect("weights are too large"); + // verify that the hardcoded value covers `receive_messages_delivery_proof` weight + let actual_messages_delivery_confirmation_tx_weight = W::receive_messages_delivery_proof_weight( + &PreComputedSize(W::expected_extra_storage_proof_size() as usize), + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }, + ); assert!( - actual_max_messages_delivery_tx_weight <= expected_max_messages_delivery_tx_weight, + actual_messages_delivery_confirmation_tx_weight <= expected_messages_delivery_confirmation_tx_weight, "Messages delivery confirmation transaction weight {} is larger than expected weight {}", - actual_max_messages_delivery_tx_weight, - expected_max_messages_delivery_tx_weight, + actual_messages_delivery_confirmation_tx_weight, + expected_messages_delivery_confirmation_tx_weight, + ); +} + +/// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain. +pub fn ensure_able_to_receive_message<W: WeightInfoExt>( + max_extrinsic_size: u32, + max_extrinsic_weight: Weight, + max_incoming_message_proof_size: u32, + // This is a base weight (which includes cost of tx itself, per-byte cost, adjusted per-byte cost) of single + // message delivery transaction that brings `max_incoming_message_proof_size` proof. + max_incoming_message_proof_base_weight: Weight, + max_incoming_message_dispatch_weight: Weight, +) { + // verify that we're able to receive proof of maximal-size message + let max_delivery_transaction_size = max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE); + assert!( + max_delivery_transaction_size <= max_extrinsic_size, + "Size of maximal message delivery transaction {} + {} is larger than maximal possible transaction size {}", + max_incoming_message_proof_size, + SIGNED_EXTENSIONS_SIZE, + max_extrinsic_size, + ); + + // verify that we're able to receive proof of maximal-size message with maximal dispatch weight + let max_delivery_transaction_dispatch_weight = W::receive_messages_proof_weight( + &PreComputedSize((max_incoming_message_proof_size + W::expected_extra_storage_proof_size()) as usize), + 1, + max_incoming_message_dispatch_weight, + ); + let max_delivery_transaction_weight = + max_incoming_message_proof_base_weight.saturating_add(max_delivery_transaction_dispatch_weight); + assert!( + max_delivery_transaction_weight <= max_extrinsic_weight, + "Weight of maximal message delivery transaction {} + {} is larger than maximal possible transaction weight {}", + max_delivery_transaction_weight, + max_delivery_transaction_dispatch_weight, + max_extrinsic_weight, + ); +} + +/// Ensure that we're able to receive maximal confirmation from other chain. +pub fn ensure_able_to_receive_confirmation<W: WeightInfoExt>( + max_extrinsic_size: u32, + max_extrinsic_weight: Weight, + max_inbound_lane_data_proof_size_from_peer_chain: u32, + max_unrewarded_relayer_entries_at_peer_inbound_lane: MessageNonce, + max_unconfirmed_messages_at_inbound_lane: MessageNonce, + // This is a base weight (which includes cost of tx itself, per-byte cost, adjusted per-byte cost) of single + // confirmation transaction that brings `max_inbound_lane_data_proof_size_from_peer_chain` proof. + max_incoming_delivery_proof_base_weight: Weight, +) { + // verify that we're able to receive confirmation of maximal-size + let max_confirmation_transaction_size = + max_inbound_lane_data_proof_size_from_peer_chain.saturating_add(SIGNED_EXTENSIONS_SIZE); + assert!( + max_confirmation_transaction_size <= max_extrinsic_size, + "Size of maximal message delivery confirmation transaction {} + {} is larger than maximal possible transaction size {}", + max_inbound_lane_data_proof_size_from_peer_chain, + SIGNED_EXTENSIONS_SIZE, + max_extrinsic_size, + ); + + // verify that we're able to reward maximal number of relayers that have delivered maximal number of messages + let max_confirmation_transaction_dispatch_weight = W::receive_messages_delivery_proof_weight( + &PreComputedSize(max_inbound_lane_data_proof_size_from_peer_chain as usize), + &UnrewardedRelayersState { + unrewarded_relayer_entries: max_unrewarded_relayer_entries_at_peer_inbound_lane, + total_messages: max_unconfirmed_messages_at_inbound_lane, + ..Default::default() + }, + ); + let max_confirmation_transaction_weight = + max_incoming_delivery_proof_base_weight.saturating_add(max_confirmation_transaction_dispatch_weight); + assert!( + max_confirmation_transaction_weight <= max_extrinsic_weight, + "Weight of maximal confirmation transaction {} + {} is larger than maximal possible transaction weight {}", + max_incoming_delivery_proof_base_weight, + max_confirmation_transaction_dispatch_weight, + max_extrinsic_weight, ); } /// Extended weight info. pub trait WeightInfoExt: WeightInfo { + /// Size of proof that is already included in the single message delivery weight. + /// + /// The message submitter (at source chain) has already covered this cost. But there are two + /// factors that may increase proof size: (1) the message size may be larger than predefined + /// and (2) relayer may add extra trie nodes to the proof. So if proof size is larger than + /// this value, we're going to charge relayer for that. + fn expected_extra_storage_proof_size() -> u32; + + // Functions that are directly mapped to extrinsics weights. + + /// Weight of message send extrinsic. + fn send_message_weight(message: &impl Size) -> Weight { + let transaction_overhead = Self::send_message_overhead(); + let message_size_overhead = Self::send_message_size_overhead(message.size_hint()); + + transaction_overhead.saturating_add(message_size_overhead) + } + + /// Weight of message delivery extrinsic. + fn receive_messages_proof_weight(proof: &impl Size, messages_count: u32, dispatch_weight: Weight) -> Weight { + // basic components of extrinsic weight + let transaction_overhead = Self::receive_messages_proof_overhead(); + let outbound_state_delivery_weight = Self::receive_messages_proof_outbound_lane_state_overhead(); + let messages_delivery_weight = + Self::receive_messages_proof_messages_overhead(MessageNonce::from(messages_count)); + let messages_dispatch_weight = dispatch_weight; + + // proof size overhead weight + let expected_proof_size = EXPECTED_DEFAULT_MESSAGE_LENGTH + .saturating_mul(messages_count.saturating_sub(1)) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size_hint(); + let proof_size_overhead = + Self::storage_proof_size_overhead(actual_proof_size.saturating_sub(expected_proof_size)); + + transaction_overhead + .saturating_add(outbound_state_delivery_weight) + .saturating_add(messages_delivery_weight) + .saturating_add(messages_dispatch_weight) + .saturating_add(proof_size_overhead) + } + + /// Weight of confirmation delivery extrinsic. + fn receive_messages_delivery_proof_weight(proof: &impl Size, relayers_state: &UnrewardedRelayersState) -> Weight { + // basic components of extrinsic weight + let transaction_overhead = Self::receive_messages_delivery_proof_overhead(); + let messages_overhead = Self::receive_messages_delivery_proof_messages_overhead(relayers_state.total_messages); + let relayers_overhead = + Self::receive_messages_delivery_proof_relayers_overhead(relayers_state.unrewarded_relayer_entries); + + // proof size overhead weight + let expected_proof_size = Self::expected_extra_storage_proof_size(); + let actual_proof_size = proof.size_hint(); + let proof_size_overhead = + Self::storage_proof_size_overhead(actual_proof_size.saturating_sub(expected_proof_size)); + + transaction_overhead + .saturating_add(messages_overhead) + .saturating_add(relayers_overhead) + .saturating_add(proof_size_overhead) + } + + // Functions that are used by extrinsics weights formulas. + /// Returns weight of message send transaction (`send_message`). fn send_message_overhead() -> Weight { Self::send_minimal_message_worst_case() @@ -130,6 +291,31 @@ pub trait WeightInfoExt: WeightInfo { .saturating_sub(weight_of_two_messages_by_single_relayer) .saturating_mul(relayers as Weight) } + + /// Returns weight that needs to be accounted when storage proof of given size is recieved (either in + /// `receive_messages_proof` or `receive_messages_delivery_proof`). + /// + /// **IMPORTANT**: this overhead is already included in the 'base' transaction cost - e.g. proof + /// size depends on messages count or number of entries in the unrewarded relayers set. So this + /// shouldn't be added to cost of transaction, but instead should act as a minimal cost that the + /// relayer must pay when it relays proof of given size (even if cost based on other parameters + /// is less than that cost). + fn storage_proof_size_overhead(proof_size: u32) -> Weight { + let proof_size_in_bytes = proof_size as Weight; + let byte_weight = + (Self::receive_single_message_proof_16_kb() - Self::receive_single_message_proof_1_kb()) / (15 * 1024); + proof_size_in_bytes * byte_weight + } } -impl<T: WeightInfo> WeightInfoExt for T {} +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + bp_rialto::EXTRA_STORAGE_PROOF_SIZE + } +} + +impl<T: frame_system::Config> WeightInfoExt for crate::weights::RialtoWeight<T> { + fn expected_extra_storage_proof_size() -> u32 { + bp_rialto::EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/bridges/primitives/message-lane/Cargo.toml b/bridges/primitives/message-lane/Cargo.toml index 87088aed64d..e002ccd36a2 100644 --- a/bridges/primitives/message-lane/Cargo.toml +++ b/bridges/primitives/message-lane/Cargo.toml @@ -9,6 +9,10 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } +# Bridge dependencies + +bp-runtime = { path = "../runtime", default-features = false } + # Substrate Dependencies frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "master" , default-features = false } @@ -18,6 +22,7 @@ sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "master [features] default = ["std"] std = [ + "bp-runtime/std", "codec/std", "frame-support/std", "frame-system/std", diff --git a/bridges/primitives/message-lane/src/source_chain.rs b/bridges/primitives/message-lane/src/source_chain.rs index 813188bd791..1c47d8c6deb 100644 --- a/bridges/primitives/message-lane/src/source_chain.rs +++ b/bridges/primitives/message-lane/src/source_chain.rs @@ -18,6 +18,7 @@ use crate::{InboundLaneData, LaneId, MessageNonce}; +use bp_runtime::Size; use frame_support::{Parameter, RuntimeDebug}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug}; @@ -46,7 +47,7 @@ pub trait TargetHeaderChain<Payload, AccountId> { type Error: Debug + Into<&'static str>; /// Proof that messages have been received by target chain. - type MessagesDeliveryProof: Parameter; + type MessagesDeliveryProof: Parameter + Size; /// Verify message payload before we accept it. /// diff --git a/bridges/primitives/message-lane/src/target_chain.rs b/bridges/primitives/message-lane/src/target_chain.rs index c79b534156f..825fe67c549 100644 --- a/bridges/primitives/message-lane/src/target_chain.rs +++ b/bridges/primitives/message-lane/src/target_chain.rs @@ -18,6 +18,7 @@ use crate::{LaneId, Message, MessageData, MessageKey, OutboundLaneData}; +use bp_runtime::Size; use codec::{Decode, Encode, Error as CodecError}; use frame_support::{weights::Weight, Parameter, RuntimeDebug}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*}; @@ -63,7 +64,7 @@ pub trait SourceHeaderChain<Fee> { /// Proof that messages are sent from source chain. This may also include proof /// of corresponding outbound lane states. - type MessagesProof: Parameter; + type MessagesProof: Parameter + Size; /// Verify messages proof and return proved messages. /// diff --git a/bridges/primitives/millau/src/lib.rs b/bridges/primitives/millau/src/lib.rs index b6f32582e9b..017aef6da72 100644 --- a/bridges/primitives/millau/src/lib.rs +++ b/bridges/primitives/millau/src/lib.rs @@ -69,12 +69,15 @@ pub const MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE: MessageNonce = 1024; /// Maximal number of unconfirmed messages at inbound lane. pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 1024; -/// Maximal weight of single message delivery transaction on Millau chain. +/// Maximal weight of single regular message delivery transaction on Millau chain. /// /// This value is a result of `pallet_message_lane::Module::receive_messages_proof` weight formula computation /// for the case when single message is delivered. The result then must be rounded up to account possible future /// runtime upgrades. -pub const MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000; +pub const MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_000_000_000; + +/// Increase of delivery transaction weight on Millau chain with every additional message byte. +pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 3_000; /// Maximal weight of single message delivery confirmation transaction on Millau chain. /// diff --git a/bridges/primitives/rialto/src/lib.rs b/bridges/primitives/rialto/src/lib.rs index 715004efba7..9bec3b1862b 100644 --- a/bridges/primitives/rialto/src/lib.rs +++ b/bridges/primitives/rialto/src/lib.rs @@ -60,12 +60,15 @@ pub const MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE: MessageNonce = 128; /// Maximal number of unconfirmed messages at inbound lane. pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 128; -/// Maximal weight of single message delivery transaction on Rialto chain. +/// Maximal weight of single regular message delivery transaction on Rialto chain. /// /// This value is a result of `pallet_message_lane::Module::receive_messages_proof` weight formula computation /// for the case when single message is delivered. The result then must be rounded up to account possible future /// runtime upgrades. -pub const MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000; +pub const MAX_SINGLE_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_000_000_000; + +/// Increase of delivery transaction weight on Rialto chain with every additional message byte. +pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 3_000; /// Maximal weight of single message delivery confirmation transaction on Rialto chain. /// diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index b9d73d2b9c1..058c510c1b5 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -21,6 +21,7 @@ use codec::Encode; use sp_core::hash::H256; use sp_io::hashing::blake2_256; +use sp_std::convert::TryFrom; pub use chain::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf}; @@ -110,3 +111,12 @@ pub trait Size { /// accurate. fn size_hint(&self) -> u32; } + +/// Pre-computed size. +pub struct PreComputedSize(pub usize); + +impl Size for PreComputedSize { + fn size_hint(&self) -> u32 { + u32::try_from(self.0).unwrap_or(u32::MAX) + } +} diff --git a/bridges/relays/substrate-client/src/client.rs b/bridges/relays/substrate-client/src/client.rs index a5ddf143f1b..767002d6865 100644 --- a/bridges/relays/substrate-client/src/client.rs +++ b/bridges/relays/substrate-client/src/client.rs @@ -245,14 +245,14 @@ impl<C: Chain> Client<C> { instance: InstanceId, lane: LaneId, at_block: C::Hash, - ) -> Result<StorageProof> { + ) -> Result<Vec<Vec<u8>>> { let encoded_trie_nodes = SubstrateMessageLane::<C, _, _>::prove_messages_delivery(&self.client, instance, lane, Some(at_block)) .await .map_err(Error::Request)?; let decoded_trie_nodes: Vec<Vec<u8>> = Decode::decode(&mut &encoded_trie_nodes[..]).map_err(Error::ResponseParseFailed)?; - Ok(StorageProof::new(decoded_trie_nodes)) + Ok(decoded_trie_nodes) } /// Return new justifications stream. diff --git a/bridges/relays/substrate/Cargo.toml b/bridges/relays/substrate/Cargo.toml index e1e4e2ec9a9..137741a1354 100644 --- a/bridges/relays/substrate/Cargo.toml +++ b/bridges/relays/substrate/Cargo.toml @@ -25,6 +25,7 @@ bp-millau = { path = "../../primitives/millau" } bp-polkadot = { path = "../../primitives/polkadot" } bp-runtime = { path = "../../primitives/runtime" } bp-rialto = { path = "../../primitives/rialto" } +bridge-runtime-common = { path = "../../bin/runtime-common" } headers-relay = { path = "../headers-relay" } messages-relay = { path = "../messages-relay" } millau-runtime = { path = "../../bin/millau/runtime" } diff --git a/bridges/relays/substrate/src/messages_source.rs b/bridges/relays/substrate/src/messages_source.rs index 375c469fde1..db894df8c7a 100644 --- a/bridges/relays/substrate/src/messages_source.rs +++ b/bridges/relays/substrate/src/messages_source.rs @@ -23,6 +23,7 @@ use crate::messages_lane::SubstrateMessageLane; use async_trait::async_trait; use bp_message_lane::{LaneId, MessageNonce}; use bp_runtime::InstanceId; +use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use codec::{Decode, Encode}; use frame_support::weights::Weight; use messages_relay::{ @@ -35,13 +36,12 @@ use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf, Hea use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; use sp_core::Bytes; use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; -use sp_trie::StorageProof; use std::ops::RangeInclusive; /// Intermediate message proof returned by the source Substrate node. Includes everything /// required to submit to the target node: cumulative dispatch weight of bundled messages and /// the proof itself. -pub type SubstrateMessagesProof<C> = (Weight, (HashOf<C>, StorageProof, LaneId, MessageNonce, MessageNonce)); +pub type SubstrateMessagesProof<C> = (Weight, FromBridgedChainMessagesProof<HashOf<C>>); /// Substrate client as Substrate messages source. pub struct SubstrateMessagesSource<C: Chain, P> { @@ -179,8 +179,16 @@ where proof_parameters.outbound_state_proof_required, id.1, ) - .await?; - let proof = (id.1, proof, self.lane_id, *nonces.start(), *nonces.end()); + .await? + .iter_nodes() + .collect(); + let proof = FromBridgedChainMessagesProof { + bridged_header_hash: id.1, + storage_proof: proof, + lane: self.lane_id, + nonces_start: *nonces.start(), + nonces_end: *nonces.end(), + }; Ok((id, nonces, (proof_parameters.dispatch_weight, proof))) } diff --git a/bridges/relays/substrate/src/messages_target.rs b/bridges/relays/substrate/src/messages_target.rs index e8610aac953..e5ac8880c84 100644 --- a/bridges/relays/substrate/src/messages_target.rs +++ b/bridges/relays/substrate/src/messages_target.rs @@ -24,6 +24,7 @@ use crate::messages_source::read_client_state; use async_trait::async_trait; use bp_message_lane::{LaneId, MessageNonce, UnrewardedRelayersState}; use bp_runtime::InstanceId; +use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof; use codec::{Decode, Encode}; use messages_relay::{ message_lane::{SourceHeaderIdOf, TargetHeaderIdOf}, @@ -33,11 +34,13 @@ use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf}; use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase}; use sp_core::Bytes; use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; -use sp_trie::StorageProof; use std::ops::RangeInclusive; /// Message receiving proof returned by the target Substrate node. -pub type SubstrateMessagesReceivingProof<C> = (UnrewardedRelayersState, (HashOf<C>, StorageProof, LaneId)); +pub type SubstrateMessagesReceivingProof<C> = ( + UnrewardedRelayersState, + FromBridgedChainMessagesDeliveryProof<HashOf<C>>, +); /// Substrate client as Substrate messages target. pub struct SubstrateMessagesTarget<C: Chain, P> { @@ -166,7 +169,11 @@ where .client .prove_messages_delivery(self.instance, self.lane_id, id.1) .await?; - let proof = (id.1, proof, self.lane_id); + let proof = FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: id.1, + storage_proof: proof, + lane: self.lane_id, + }; Ok((id, (relayers_state, proof))) } diff --git a/bridges/relays/substrate/src/millau_messages_to_rialto.rs b/bridges/relays/substrate/src/millau_messages_to_rialto.rs index 1d9239ff371..c0b5c8a8103 100644 --- a/bridges/relays/substrate/src/millau_messages_to_rialto.rs +++ b/bridges/relays/substrate/src/millau_messages_to_rialto.rs @@ -24,6 +24,7 @@ use crate::{MillauClient, RialtoClient}; use async_trait::async_trait; use bp_message_lane::{LaneId, MessageNonce}; use bp_runtime::{MILLAU_BRIDGE_INSTANCE, RIALTO_BRIDGE_INSTANCE}; +use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use messages_relay::message_lane::MessageLane; use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams}; use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams}; @@ -74,8 +75,12 @@ impl SubstrateMessageLane for MillauMessagesToRialto { proof: <Self as MessageLane>::MessagesProof, ) -> Result<Self::TargetSignedTransaction, SubstrateError> { let (dispatch_weight, proof) = proof; - let (_, _, _, ref nonces_begin, ref nonces_end) = proof; - let messages_count = nonces_end - nonces_begin + 1; + let FromBridgedChainMessagesProof { + ref nonces_start, + ref nonces_end, + .. + } = proof; + let messages_count = nonces_end - nonces_start + 1; let account_id = self.target_sign.signer.public().as_array_ref().clone().into(); let nonce = self.target_client.next_account_index(account_id).await?; let call = rialto_runtime::MessageLaneCall::receive_messages_proof( diff --git a/bridges/relays/substrate/src/rialto_messages_to_millau.rs b/bridges/relays/substrate/src/rialto_messages_to_millau.rs index a336637e4da..2ddf6602706 100644 --- a/bridges/relays/substrate/src/rialto_messages_to_millau.rs +++ b/bridges/relays/substrate/src/rialto_messages_to_millau.rs @@ -24,6 +24,7 @@ use crate::{MillauClient, RialtoClient}; use async_trait::async_trait; use bp_message_lane::{LaneId, MessageNonce}; use bp_runtime::{MILLAU_BRIDGE_INSTANCE, RIALTO_BRIDGE_INSTANCE}; +use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use messages_relay::message_lane::MessageLane; use relay_millau_client::{HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams}; use relay_rialto_client::{HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams}; @@ -74,8 +75,12 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { proof: <Self as MessageLane>::MessagesProof, ) -> Result<Self::TargetSignedTransaction, SubstrateError> { let (dispatch_weight, proof) = proof; - let (_, _, _, ref nonces_begin, ref nonces_end) = proof; - let messages_count = nonces_end - nonces_begin + 1; + let FromBridgedChainMessagesProof { + ref nonces_start, + ref nonces_end, + .. + } = proof; + let messages_count = nonces_end - nonces_start + 1; let account_id = self.target_sign.signer.public().as_array_ref().clone().into(); let nonce = self.target_client.next_account_index(account_id).await?; let call = millau_runtime::MessageLaneCall::receive_messages_proof( -- GitLab