Benchmark for message delivery confirmation transaction (#570)

* receive_delivery_proof benchmarks

* fix compilation

* Update modules/message-lane/src/benchmarking.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update modules/message-lane/src/benchmarking.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update modules/message-lane/src/benchmarking.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update modules/message-lane/src/benchmarking.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

* Update modules/message-lane/src/benchmarking.rs

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
This commit is contained in:
Svyatoslav Nikolsky
2020-12-18 13:59:59 +03:00
committed by Bastian Köcher
parent b45d65f330
commit 9deea5d251
6 changed files with 308 additions and 15 deletions
+33
View File
@@ -802,6 +802,7 @@ impl_runtime_apis! {
use pallet_message_lane::benchmarking::{
Module as MessageLaneBench,
Config as MessageLaneConfig,
MessageDeliveryProofParams as MessageLaneMessageDeliveryProofParams,
MessageParams as MessageLaneMessageParams,
MessageProofParams as MessageLaneMessageProofParams,
};
@@ -811,6 +812,10 @@ impl_runtime_apis! {
Default::default()
}
fn account_balance(account: &Self::AccountId) -> Self::OutboundMessageFee {
pallet_balances::Module::<Runtime>::free_balance(account)
}
fn endow_account(account: &Self::AccountId) {
pallet_balances::Module::<Runtime>::make_free_balance_be(
account,
@@ -913,6 +918,34 @@ impl_runtime_apis! {
}.encode(),
)
}
fn prepare_message_delivery_proof(
params: MessageLaneMessageDeliveryProofParams<Self::AccountId>,
) -> millau_messages::FromMillauMessagesDeliveryProof {
use crate::millau_messages::{Millau, WithMillauMessageBridge};
use bridge_runtime_common::{
messages::ChainWithMessageLanes,
messages_benchmarking::prepare_message_delivery_proof,
};
use sp_runtime::traits::Header;
prepare_message_delivery_proof::<WithMillauMessageBridge, bp_millau::Hasher, Runtime, _, _>(
params,
|lane_id| pallet_message_lane::storage_keys::inbound_lane_data_key::<
Runtime,
<Millau as ChainWithMessageLanes>::MessageLaneInstance,
>(
&lane_id,
).0,
|state_root| bp_millau::Header::new(
0,
Default::default(),
state_root,
Default::default(),
Default::default(),
),
)
}
}
add_benchmark!(params, batches, pallet_bridge_eth_poa, BridgeKovan);
@@ -74,7 +74,8 @@ pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDi
pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof<WithMillauMessageBridge>;
/// Messages delivery proof for Rialto -> Millau messages.
type ToMillauMessagesDeliveryProof = messages::source::FromBridgedChainMessagesDeliveryProof<WithMillauMessageBridge>;
pub type FromMillauMessagesDeliveryProof =
messages::source::FromBridgedChainMessagesDeliveryProof<WithMillauMessageBridge>;
/// Millau <-> Rialto message bridge.
#[derive(RuntimeDebug, Clone, Copy)]
@@ -168,7 +169,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 = ToMillauMessagesDeliveryProof;
type MessagesDeliveryProof = FromMillauMessagesDeliveryProof;
fn verify_message(payload: &ToMillauMessagePayload) -> Result<(), Self::Error> {
messages::source::verify_chain_message::<WithMillauMessageBridge>(payload)
+1 -1
View File
@@ -90,7 +90,7 @@ pub trait ChainWithMessageLanes {
/// Hash used in the chain.
type Hash: Decode;
/// Accound id on the chain.
type AccountId: Decode;
type AccountId: Encode + Decode;
/// Public key of the chain account that may be used to verify signatures.
type Signer: Decode;
/// Signature type used on the chain.
@@ -19,13 +19,16 @@
#![cfg(feature = "runtime-benchmarks")]
use crate::messages::{target::FromBridgedChainMessagesProof, BalanceOf, BridgedChain, HashOf, MessageBridge};
use crate::messages::{
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, AccountIdOf, BalanceOf,
BridgedChain, HashOf, MessageBridge, ThisChain,
};
use bp_message_lane::{LaneId, MessageData, MessageKey, MessagePayload};
use codec::Encode;
use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH};
use frame_support::weights::Weight;
use pallet_message_lane::benchmarking::MessageProofParams;
use pallet_message_lane::benchmarking::{MessageDeliveryProofParams, MessageProofParams};
use sp_core::Hasher;
use sp_runtime::traits::Header;
use sp_std::prelude::*;
@@ -142,3 +145,47 @@ where
.expect("too many messages requested by benchmark"),
)
}
/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call.
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>
where
B: MessageBridge,
H: Hasher,
R: pallet_substrate_bridge::Config,
<R::BridgedChain as bp_runtime::Chain>::Hash: Into<HashOf<BridgedChain<B>>>,
ML: Fn(LaneId) -> Vec<u8>,
MH: Fn(H::Out) -> <R::BridgedChain as bp_runtime::Chain>::Header,
{
// prepare Bridged chain storage with inbound lane state
let storage_key = make_bridged_inbound_lane_data_key(params.lane);
let mut root = Default::default();
let mut mdb = MemoryDB::default();
{
let mut trie = TrieDBMut::<H>::new(&mut mdb, &mut root);
trie.insert(&storage_key, &params.inbound_lane_data.encode())
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
}
// 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");
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
let bridged_header = make_bridged_header(root);
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,
)
}
@@ -16,19 +16,23 @@
//! Message lane pallet benchmarking.
use crate::{inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, Call, Instance};
use crate::{
inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, relayer_fund_account_id, Call, Instance,
};
use bp_message_lane::{
target_chain::SourceHeaderChain, InboundLaneData, LaneId, MessageData, MessageNonce, OutboundLaneData,
source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, InboundLaneData, LaneId, MessageData,
MessageNonce, OutboundLaneData,
};
use frame_benchmarking::{account, benchmarks_instance};
use frame_support::{traits::Get, weights::Weight};
use frame_system::RawOrigin;
use num_traits::Zero;
use sp_std::{ops::RangeInclusive, prelude::*};
use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, ops::RangeInclusive, prelude::*};
/// Message crafted with this size factor should be the largest possible message.
pub const WORST_MESSAGE_SIZE_FACTOR: u32 = 1000;
/// Fee paid by submitter for single message delivery.
const MESSAGE_FEE: u32 = 1_000_000;
const SEED: u32 = 0;
@@ -55,10 +59,20 @@ pub struct MessageProofParams {
pub outbound_lane_data: Option<OutboundLaneData>,
}
/// Benchmark-specific message delivery proof parameters.
pub struct MessageDeliveryProofParams<ThisChainAccountId> {
/// Id of the lane.
pub lane: LaneId,
/// The proof needs to include this inbound lane data.
pub inbound_lane_data: InboundLaneData<ThisChainAccountId>,
}
/// Trait that must be implemented by runtime.
pub trait Config<I: Instance>: crate::Config<I> {
/// Return id of relayer account at the bridged chain.
fn bridged_relayer_id() -> Self::InboundRelayer;
/// Return balance of given account.
fn account_balance(account: &Self::AccountId) -> Self::OutboundMessageFee;
/// Create given account and give it enough balance for test purposes.
fn endow_account(account: &Self::AccountId);
/// Prepare message to send over lane.
@@ -72,6 +86,10 @@ pub trait Config<I: Instance>: crate::Config<I> {
<Self::SourceHeaderChain as SourceHeaderChain<Self::InboundMessageFee>>::MessagesProof,
Weight,
);
/// Prepare messages delivery proof to receive by the module.
fn prepare_message_delivery_proof(
params: MessageDeliveryProofParams<Self::AccountId>,
) -> <Self::TargetHeaderChain as TargetHeaderChain<Self::OutboundPayload, Self::AccountId>>::MessagesDeliveryProof;
}
benchmarks_instance! {
@@ -200,6 +218,111 @@ benchmarks_instance! {
);
}
// Benchmark `receive_messages_delivery_proof` extrinsic with following conditions:
// * single relayer is rewarded for relaying single message;
// * relayer account does not exist (in practice it needs to exist in production environment).
//
// This is base benchmark for all other confirmations delivery benchmarks.
receive_delivery_proof_for_single_message {
let relayers_fund_id = relayer_fund_account_id::<T, I>();
let relayer_id: T::AccountId = account("relayer", 0, SEED);
let relayer_balance = T::account_balance(&relayer_id);
T::endow_account(&relayers_fund_id);
// send message that we're going to confirm
send_regular_message::<T, I>();
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![(1, 1, relayer_id.clone())].into_iter().collect(),
latest_received_nonce: 1,
latest_confirmed_nonce: 0,
}
});
}: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof)
verify {
assert_eq!(
T::account_balance(&relayer_id),
relayer_balance + MESSAGE_FEE.into(),
);
}
// Benchmark `receive_messages_delivery_proof` extrinsic with following conditions:
// * single relayer is rewarded for relaying two messages;
// * relayer account does not exist (in practice it needs to exist in production environment).
//
// Additional weight for paying single-message reward to the same relayer could be computed
// as `weight(receive_delivery_proof_for_two_messages_by_single_relayer)
// - weight(receive_delivery_proof_for_single_message)`.
receive_delivery_proof_for_two_messages_by_single_relayer {
let relayers_fund_id = relayer_fund_account_id::<T, I>();
let relayer_id: T::AccountId = account("relayer", 0, SEED);
let relayer_balance = T::account_balance(&relayer_id);
T::endow_account(&relayers_fund_id);
// send message that we're going to confirm
send_regular_message::<T, I>();
send_regular_message::<T, I>();
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![(1, 2, relayer_id.clone())].into_iter().collect(),
latest_received_nonce: 2,
latest_confirmed_nonce: 0,
}
});
}: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof)
verify {
assert_eq!(
T::account_balance(&relayer_id),
relayer_balance + (MESSAGE_FEE * 2).into(),
);
}
// Benchmark `receive_messages_delivery_proof` extrinsic with following conditions:
// * two relayers are rewarded for relaying single message each;
// * relayer account does not exist (in practice it needs to exist in production environment).
//
// Additional weight for paying reward to the next relayer could be computed
// as `weight(receive_delivery_proof_for_two_messages_by_two_relayers)
// - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`.
receive_delivery_proof_for_two_messages_by_two_relayers {
let relayers_fund_id = relayer_fund_account_id::<T, I>();
let relayer1_id: T::AccountId = account("relayer1", 1, SEED);
let relayer1_balance = T::account_balance(&relayer1_id);
let relayer2_id: T::AccountId = account("relayer2", 2, SEED);
let relayer2_balance = T::account_balance(&relayer2_id);
T::endow_account(&relayers_fund_id);
// send message that we're going to confirm
send_regular_message::<T, I>();
send_regular_message::<T, I>();
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![
(1, 1, relayer1_id.clone()),
(2, 2, relayer2_id.clone()),
].into_iter().collect(),
latest_received_nonce: 2,
latest_confirmed_nonce: 0,
}
});
}: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof)
verify {
assert_eq!(
T::account_balance(&relayer1_id),
relayer1_balance + MESSAGE_FEE.into(),
);
assert_eq!(
T::account_balance(&relayer2_id),
relayer2_balance + MESSAGE_FEE.into(),
);
}
//
// Benchmarks for manual checks.
//
@@ -284,6 +407,86 @@ benchmarks_instance! {
20,
);
}
// Benchmark `receive_messages_delivery_proof` extrinsic where single relayer delivers multiple messages.
receive_delivery_proof_for_multiple_messages_by_single_relayer {
// there actually should be used value of `MaxUnrewardedRelayerEntriesAtInboundLane` from the bridged
// chain, but we're more interested in additional weight/message than in max weight
let i in 1..T::MaxUnrewardedRelayerEntriesAtInboundLane::get()
.try_into()
.expect("Value of MaxUnrewardedRelayerEntriesAtInboundLane is too large");
let relayers_fund_id = relayer_fund_account_id::<T, I>();
let relayer_id: T::AccountId = account("relayer", 0, SEED);
let relayer_balance = T::account_balance(&relayer_id);
T::endow_account(&relayers_fund_id);
// send messages that we're going to confirm
for _ in 1..=i {
send_regular_message::<T, I>();
}
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: vec![(1, i as MessageNonce, relayer_id.clone())].into_iter().collect(),
latest_received_nonce: i as MessageNonce,
latest_confirmed_nonce: 0,
}
});
}: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof)
verify {
assert_eq!(
T::account_balance(&relayer_id),
relayer_balance + (MESSAGE_FEE * i).into(),
);
}
// Benchmark `receive_messages_delivery_proof` extrinsic where every relayer delivers single messages.
receive_delivery_proof_for_multiple_messages_by_multiple_relayers {
// there actually should be used value of `MaxUnconfirmedMessagesAtInboundLane` from the bridged
// chain, but we're more interested in additional weight/message than in max weight
let i in 1..T::MaxUnconfirmedMessagesAtInboundLane::get()
.try_into()
.expect("Value of MaxUnconfirmedMessagesAtInboundLane is too large ");
let relayers_fund_id = relayer_fund_account_id::<T, I>();
let confirmation_relayer_id = account("relayer", 0, SEED);
let relayers: BTreeMap<T::AccountId, T::OutboundMessageFee> = (1..=i)
.map(|j| {
let relayer_id = account("relayer", j + 1, SEED);
let relayer_balance = T::account_balance(&relayer_id);
(relayer_id, relayer_balance)
})
.collect();
T::endow_account(&relayers_fund_id);
// send messages that we're going to confirm
for _ in 1..=i {
send_regular_message::<T, I>();
}
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
lane: bench_lane_id(),
inbound_lane_data: InboundLaneData {
relayers: relayers
.keys()
.enumerate()
.map(|(j, relayer_id)| (j as MessageNonce + 1, j as MessageNonce + 1, relayer_id.clone()))
.collect(),
latest_received_nonce: i as MessageNonce,
latest_confirmed_nonce: 0,
}
});
}: receive_messages_delivery_proof(RawOrigin::Signed(confirmation_relayer_id), proof)
verify {
for (relayer_id, prev_balance) in relayers {
assert_eq!(
T::account_balance(&relayer_id),
prev_balance + MESSAGE_FEE.into(),
);
}
}
}
fn bench_lane_id() -> LaneId {
@@ -294,7 +497,7 @@ fn send_regular_message<T: Config<I>, I: Instance>() {
let mut outbound_lane = outbound_lane::<T, I>(bench_lane_id());
outbound_lane.send_message(MessageData {
payload: vec![],
fee: Zero::zero(),
fee: MESSAGE_FEE.into(),
});
}
+14 -5
View File
@@ -45,7 +45,7 @@ use frame_support::{
Parameter, StorageMap,
};
use frame_system::{ensure_signed, RawOrigin};
use num_traits::Zero;
use num_traits::{SaturatingAdd, Zero};
use sp_runtime::{traits::BadOrigin, DispatchResult};
use sp_std::{cell::RefCell, marker::PhantomData, prelude::*};
@@ -92,12 +92,15 @@ pub trait Config<I = DefaultInstance>: frame_system::Config {
///
/// This constant limits difference between last message from last entry of the
/// `InboundLaneData::relayers` and first message at the first entry.
///
/// There is no point of making this parameter lesser than MaxUnrewardedRelayerEntriesAtInboundLane,
/// because then maximal number of relayer entries will be limited by maximal number of messages.
type MaxUnconfirmedMessagesAtInboundLane: Get<MessageNonce>;
/// Payload type of outbound messages. This payload is dispatched on the bridged chain.
type OutboundPayload: Parameter;
/// Message fee type of outbound messages. This fee is paid on this chain.
type OutboundMessageFee: Parameter + Zero;
type OutboundMessageFee: From<u32> + Parameter + SaturatingAdd + Zero;
/// Payload type of inbound messages. This payload is dispatched on this chain.
type InboundPayload: Decode;
@@ -423,24 +426,30 @@ decl_module! {
let received_range = lane.confirm_delivery(lane_data.latest_received_nonce);
if let Some(received_range) = received_range {
Self::deposit_event(RawEvent::MessagesDelivered(lane_id, received_range.0, received_range.1));
let relayer_fund_account = relayer_fund_account_id::<T, I>();
// reward relayers that have delivered messages
// this loop is bounded by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain
// this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain
let relayer_fund_account = relayer_fund_account_id::<T, I>();
for (nonce_low, nonce_high, relayer) in lane_data.relayers {
let nonce_begin = sp_std::cmp::max(nonce_low, received_range.0);
let nonce_end = sp_std::cmp::min(nonce_high, received_range.1);
// loop won't proceed if current entry is ahead of received range (begin > end).
// this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain
let mut relayer_fee: T::OutboundMessageFee = Zero::zero();
for nonce in nonce_begin..nonce_end + 1 {
let message_data = OutboundMessages::<T, I>::get(MessageKey {
lane_id,
nonce,
}).expect("message was just confirmed; we never prune unconfirmed messages; qed");
relayer_fee = relayer_fee.saturating_add(&message_data.fee);
}
if !relayer_fee.is_zero() {
<T as Config<I>>::MessageDeliveryAndDispatchPayment::pay_relayer_reward(
&confirmation_relayer,
&relayer,
&message_data.fee,
&relayer_fee,
&relayer_fund_account,
);
}