mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 04:01:02 +00:00
Merge commit 'e5bed7ac380b6adb54b60a2a72a2a8f07f50d6c1' as 'bridges'
This commit is contained in:
@@ -0,0 +1,830 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Messages pallet benchmarking.
|
||||
|
||||
use crate::weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH;
|
||||
use crate::{inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, Call, Instance};
|
||||
|
||||
use bp_messages::{
|
||||
source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, InboundLaneData, LaneId, MessageData,
|
||||
MessageNonce, OutboundLaneData, UnrewardedRelayersState,
|
||||
};
|
||||
use frame_benchmarking::{account, benchmarks_instance};
|
||||
use frame_support::{traits::Get, weights::Weight};
|
||||
use frame_system::RawOrigin;
|
||||
use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, ops::RangeInclusive, prelude::*};
|
||||
|
||||
/// Fee paid by submitter for single message delivery.
|
||||
pub const MESSAGE_FEE: u64 = 10_000_000_000;
|
||||
|
||||
const SEED: u32 = 0;
|
||||
|
||||
/// Pallet we're benchmarking here.
|
||||
pub struct Pallet<T: Config<I>, I: crate::Instance>(crate::Pallet<T, I>);
|
||||
|
||||
/// Proof size requirements.
|
||||
pub enum ProofSize {
|
||||
/// The proof is expected to be minimal. If value size may be changed, then it is expected to
|
||||
/// have given size.
|
||||
Minimal(u32),
|
||||
/// The proof is expected to have at least given size and grow by increasing number of trie nodes
|
||||
/// included in the proof.
|
||||
HasExtraNodes(u32),
|
||||
/// The proof is expected to have at least given size and grow by increasing value that is stored
|
||||
/// in the trie.
|
||||
HasLargeLeaf(u32),
|
||||
}
|
||||
|
||||
/// Benchmark-specific message parameters.
|
||||
pub struct MessageParams<ThisAccountId> {
|
||||
/// Size of the message payload.
|
||||
pub size: u32,
|
||||
/// Message sender account.
|
||||
pub sender_account: ThisAccountId,
|
||||
}
|
||||
|
||||
/// Benchmark-specific message proof parameters.
|
||||
pub struct MessageProofParams {
|
||||
/// Id of the lane.
|
||||
pub lane: LaneId,
|
||||
/// Range of messages to include in the proof.
|
||||
pub message_nonces: RangeInclusive<MessageNonce>,
|
||||
/// If `Some`, the proof needs to include this outbound lane data.
|
||||
pub outbound_lane_data: Option<OutboundLaneData>,
|
||||
/// Proof size requirements.
|
||||
pub size: ProofSize,
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
/// Proof size requirements.
|
||||
pub size: ProofSize,
|
||||
}
|
||||
|
||||
/// Trait that must be implemented by runtime.
|
||||
pub trait Config<I: Instance>: crate::Config<I> {
|
||||
/// Lane id to use in benchmarks.
|
||||
fn bench_lane_id() -> LaneId {
|
||||
Default::default()
|
||||
}
|
||||
/// Get maximal size of the message payload.
|
||||
fn maximal_message_size() -> u32;
|
||||
/// 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.
|
||||
fn prepare_outbound_message(
|
||||
params: MessageParams<Self::AccountId>,
|
||||
) -> (Self::OutboundPayload, Self::OutboundMessageFee);
|
||||
/// Prepare messages proof to receive by the module.
|
||||
fn prepare_message_proof(
|
||||
params: MessageProofParams,
|
||||
) -> (
|
||||
<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! {
|
||||
//
|
||||
// Benchmarks that are used directly by the runtime.
|
||||
//
|
||||
|
||||
// Benchmark `send_message` extrinsic with the worst possible conditions:
|
||||
// * outbound lane already has state, so it needs to be read and decoded;
|
||||
// * relayers fund account does not exists (in practice it needs to exist in production environment);
|
||||
// * maximal number of messages is being pruned during the call;
|
||||
// * message size is minimal for the target chain.
|
||||
//
|
||||
// Result of this benchmark is used as a base weight for `send_message` call. Then the 'message weight'
|
||||
// (estimated using `send_half_maximal_message_worst_case` and `send_maximal_message_worst_case`) is
|
||||
// added.
|
||||
send_minimal_message_worst_case {
|
||||
let lane_id = T::bench_lane_id();
|
||||
let sender = account("sender", 0, SEED);
|
||||
T::endow_account(&sender);
|
||||
|
||||
// 'send' messages that are to be pruned when our message is sent
|
||||
for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() {
|
||||
send_regular_message::<T, I>();
|
||||
}
|
||||
confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get());
|
||||
|
||||
let (payload, fee) = T::prepare_outbound_message(MessageParams {
|
||||
size: 0,
|
||||
sender_account: sender.clone(),
|
||||
});
|
||||
}: send_message(RawOrigin::Signed(sender), lane_id, payload, fee)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()),
|
||||
T::MaxMessagesToPruneAtOnce::get() + 1,
|
||||
);
|
||||
}
|
||||
|
||||
// Benchmark `send_message` extrinsic with the worst possible conditions:
|
||||
// * outbound lane already has state, so it needs to be read and decoded;
|
||||
// * relayers fund account does not exists (in practice it needs to exist in production environment);
|
||||
// * maximal number of messages is being pruned during the call;
|
||||
// * message size is 1KB.
|
||||
//
|
||||
// With single KB of message size, the weight of the call is increased (roughly) by
|
||||
// `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`.
|
||||
send_1_kb_message_worst_case {
|
||||
let lane_id = T::bench_lane_id();
|
||||
let sender = account("sender", 0, SEED);
|
||||
T::endow_account(&sender);
|
||||
|
||||
// 'send' messages that are to be pruned when our message is sent
|
||||
for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() {
|
||||
send_regular_message::<T, I>();
|
||||
}
|
||||
confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get());
|
||||
|
||||
let size = 1024;
|
||||
assert!(
|
||||
T::maximal_message_size() > size,
|
||||
"This benchmark can only be used with runtime that accepts 1KB messages",
|
||||
);
|
||||
|
||||
let (payload, fee) = T::prepare_outbound_message(MessageParams {
|
||||
size,
|
||||
sender_account: sender.clone(),
|
||||
});
|
||||
}: send_message(RawOrigin::Signed(sender), lane_id, payload, fee)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()),
|
||||
T::MaxMessagesToPruneAtOnce::get() + 1,
|
||||
);
|
||||
}
|
||||
|
||||
// Benchmark `send_message` extrinsic with the worst possible conditions:
|
||||
// * outbound lane already has state, so it needs to be read and decoded;
|
||||
// * relayers fund account does not exists (in practice it needs to exist in production environment);
|
||||
// * maximal number of messages is being pruned during the call;
|
||||
// * message size is 16KB.
|
||||
//
|
||||
// With single KB of message size, the weight of the call is increased (roughly) by
|
||||
// `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`.
|
||||
send_16_kb_message_worst_case {
|
||||
let lane_id = T::bench_lane_id();
|
||||
let sender = account("sender", 0, SEED);
|
||||
T::endow_account(&sender);
|
||||
|
||||
// 'send' messages that are to be pruned when our message is sent
|
||||
for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() {
|
||||
send_regular_message::<T, I>();
|
||||
}
|
||||
confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get());
|
||||
|
||||
let size = 16 * 1024;
|
||||
assert!(
|
||||
T::maximal_message_size() > size,
|
||||
"This benchmark can only be used with runtime that accepts 16KB messages",
|
||||
);
|
||||
|
||||
let (payload, fee) = T::prepare_outbound_message(MessageParams {
|
||||
size,
|
||||
sender_account: sender.clone(),
|
||||
});
|
||||
}: send_message(RawOrigin::Signed(sender), lane_id, payload, fee)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()),
|
||||
T::MaxMessagesToPruneAtOnce::get() + 1,
|
||||
);
|
||||
}
|
||||
|
||||
// Benchmark `increase_message_fee` with following conditions:
|
||||
// * message has maximal message;
|
||||
// * submitter account is killed because its balance is less than ED after payment.
|
||||
increase_message_fee {
|
||||
let sender = account("sender", 42, SEED);
|
||||
T::endow_account(&sender);
|
||||
|
||||
let additional_fee = T::account_balance(&sender);
|
||||
let lane_id = T::bench_lane_id();
|
||||
let nonce = 1;
|
||||
|
||||
send_regular_message_with_payload::<T, I>(vec![42u8; T::maximal_message_size() as _]);
|
||||
}: increase_message_fee(RawOrigin::Signed(sender.clone()), lane_id, nonce, additional_fee)
|
||||
verify {
|
||||
assert_eq!(T::account_balance(&sender), 0.into());
|
||||
}
|
||||
|
||||
// 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 is base benchmark for all other message delivery benchmarks.
|
||||
receive_single_message_proof {
|
||||
let relayer_id_on_source = T::bridged_relayer_id();
|
||||
let relayer_id_on_target = account("relayer", 0, SEED);
|
||||
|
||||
// mark messages 1..=20 as delivered
|
||||
receive_messages::<T, I>(20);
|
||||
|
||||
let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
message_nonces: 21..=21,
|
||||
outbound_lane_data: None,
|
||||
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::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
|
||||
21,
|
||||
);
|
||||
}
|
||||
|
||||
// Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions:
|
||||
// * proof does not include outbound lane state proof;
|
||||
// * inbound lane already has state, so it needs to be read and decoded;
|
||||
// * message is successfully dispatched;
|
||||
// * message requires all heavy checks done by dispatcher.
|
||||
//
|
||||
// The weight of single message delivery could be approximated as
|
||||
// `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`.
|
||||
// This won't be super-accurate if message has non-zero dispatch weight, but estimation should
|
||||
// be close enough to real weight.
|
||||
receive_two_messages_proof {
|
||||
let relayer_id_on_source = T::bridged_relayer_id();
|
||||
let relayer_id_on_target = account("relayer", 0, SEED);
|
||||
|
||||
// mark messages 1..=20 as delivered
|
||||
receive_messages::<T, I>(20);
|
||||
|
||||
let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
message_nonces: 21..=22,
|
||||
outbound_lane_data: None,
|
||||
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::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
|
||||
22,
|
||||
);
|
||||
}
|
||||
|
||||
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
|
||||
// * proof includes outbound lane state proof;
|
||||
// * inbound lane already has state, so it needs to be read and decoded;
|
||||
// * message is successfully dispatched;
|
||||
// * message requires all heavy checks done by dispatcher.
|
||||
//
|
||||
// The weight of outbound lane state delivery would be
|
||||
// `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`.
|
||||
// This won't be super-accurate if message has non-zero dispatch weight, but estimation should
|
||||
// be close enough to real weight.
|
||||
receive_single_message_proof_with_outbound_lane_state {
|
||||
let relayer_id_on_source = T::bridged_relayer_id();
|
||||
let relayer_id_on_target = account("relayer", 0, SEED);
|
||||
|
||||
// mark messages 1..=20 as delivered
|
||||
receive_messages::<T, I>(20);
|
||||
|
||||
let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
message_nonces: 21..=21,
|
||||
outbound_lane_data: Some(OutboundLaneData {
|
||||
oldest_unpruned_nonce: 21,
|
||||
latest_received_nonce: 20,
|
||||
latest_generated_nonce: 21,
|
||||
}),
|
||||
size: 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::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
|
||||
21,
|
||||
);
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::inbound_latest_confirmed_nonce(T::bench_lane_id()),
|
||||
20,
|
||||
);
|
||||
}
|
||||
|
||||
// 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: T::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::Pallet::<T, I>::inbound_latest_received_nonce(T::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: T::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::Pallet::<T, I>::inbound_latest_received_nonce(T::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).
|
||||
//
|
||||
// This is base benchmark for all other confirmations delivery benchmarks.
|
||||
receive_delivery_proof_for_single_message {
|
||||
let relayers_fund_id = crate::Pallet::<T, I>::relayer_fund_account_id();
|
||||
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 relayers_state = UnrewardedRelayersState {
|
||||
unrewarded_relayer_entries: 1,
|
||||
messages_in_oldest_entry: 1,
|
||||
total_messages: 1,
|
||||
};
|
||||
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
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 {
|
||||
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 = crate::Pallet::<T, I>::relayer_fund_account_id();
|
||||
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 relayers_state = UnrewardedRelayersState {
|
||||
unrewarded_relayer_entries: 1,
|
||||
messages_in_oldest_entry: 2,
|
||||
total_messages: 2,
|
||||
};
|
||||
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
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 {
|
||||
ensure_relayer_rewarded::<T, I>(&relayer_id, &relayer_balance);
|
||||
}
|
||||
|
||||
// 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 = crate::Pallet::<T, I>::relayer_fund_account_id();
|
||||
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 relayers_state = UnrewardedRelayersState {
|
||||
unrewarded_relayer_entries: 2,
|
||||
messages_in_oldest_entry: 1,
|
||||
total_messages: 2,
|
||||
};
|
||||
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
inbound_lane_data: InboundLaneData {
|
||||
relayers: vec![
|
||||
(1, 1, relayer1_id.clone()),
|
||||
(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 {
|
||||
ensure_relayer_rewarded::<T, I>(&relayer1_id, &relayer1_balance);
|
||||
ensure_relayer_rewarded::<T, I>(&relayer2_id, &relayer2_balance);
|
||||
}
|
||||
|
||||
//
|
||||
// Benchmarks for manual checks.
|
||||
//
|
||||
|
||||
// Benchmark `send_message` extrinsic with following conditions:
|
||||
// * outbound lane already has state, so it needs to be read and decoded;
|
||||
// * relayers fund account does not exists (in practice it needs to exist in production environment);
|
||||
// * maximal number of messages is being pruned during the call;
|
||||
// * message size varies from minimal to maximal for the target chain.
|
||||
//
|
||||
// Results of this benchmark may be used to check how message size affects `send_message` performance.
|
||||
send_messages_of_various_lengths {
|
||||
let i in 0..T::maximal_message_size().try_into().unwrap_or_default();
|
||||
|
||||
let lane_id = T::bench_lane_id();
|
||||
let sender = account("sender", 0, SEED);
|
||||
T::endow_account(&sender);
|
||||
|
||||
// 'send' messages that are to be pruned when our message is sent
|
||||
for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() {
|
||||
send_regular_message::<T, I>();
|
||||
}
|
||||
confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get());
|
||||
|
||||
let (payload, fee) = T::prepare_outbound_message(MessageParams {
|
||||
size: i as _,
|
||||
sender_account: sender.clone(),
|
||||
});
|
||||
}: send_message(RawOrigin::Signed(sender), lane_id, payload, fee)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()),
|
||||
T::MaxMessagesToPruneAtOnce::get() + 1,
|
||||
);
|
||||
}
|
||||
|
||||
// Benchmark `receive_messages_proof` extrinsic with multiple minimal-weight messages and following conditions:
|
||||
// * proof does not include outbound lane state proof;
|
||||
// * inbound lane already has state, so it needs to be read and decoded;
|
||||
// * message is successfully dispatched;
|
||||
// * message requires all heavy checks done by dispatcher.
|
||||
//
|
||||
// This benchmarks gives us an approximation of single message delivery weight. It is similar to the
|
||||
// `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..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: T::bench_lane_id(),
|
||||
message_nonces: 21..=(20 + i as MessageNonce),
|
||||
outbound_lane_data: None,
|
||||
size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH),
|
||||
});
|
||||
}: receive_messages_proof(
|
||||
RawOrigin::Signed(relayer_id_on_target),
|
||||
relayer_id_on_source,
|
||||
proof,
|
||||
messages_count,
|
||||
dispatch_weight
|
||||
)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
|
||||
20 + i as MessageNonce,
|
||||
);
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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 = 1u32;
|
||||
|
||||
// mark messages 1..=20 as delivered
|
||||
receive_messages::<T, I>(20);
|
||||
|
||||
let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
message_nonces: 21..=21,
|
||||
outbound_lane_data: None,
|
||||
size: ProofSize::HasExtraNodes(i as _),
|
||||
});
|
||||
}: receive_messages_proof(
|
||||
RawOrigin::Signed(relayer_id_on_target),
|
||||
relayer_id_on_source,
|
||||
proof,
|
||||
messages_count,
|
||||
dispatch_weight
|
||||
)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
|
||||
21,
|
||||
);
|
||||
}
|
||||
|
||||
// Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions:
|
||||
// * proof does not include outbound lane state proof;
|
||||
// * inbound lane already has state, so it needs to be read and decoded;
|
||||
// * message is successfully dispatched;
|
||||
// * message requires all heavy checks done by dispatcher.
|
||||
//
|
||||
// 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();
|
||||
let relayer_id_on_target = account("relayer", 0, SEED);
|
||||
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: T::bench_lane_id(),
|
||||
message_nonces: 21..=21,
|
||||
outbound_lane_data: None,
|
||||
size: ProofSize::HasLargeLeaf(i as _),
|
||||
});
|
||||
}: receive_messages_proof(
|
||||
RawOrigin::Signed(relayer_id_on_target),
|
||||
relayer_id_on_source,
|
||||
proof,
|
||||
messages_count,
|
||||
dispatch_weight
|
||||
)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
|
||||
21,
|
||||
);
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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 = i as _;
|
||||
|
||||
// mark messages 1..=20 as delivered
|
||||
receive_messages::<T, I>(20);
|
||||
|
||||
let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams {
|
||||
lane: T::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(0),
|
||||
});
|
||||
}: receive_messages_proof(
|
||||
RawOrigin::Signed(relayer_id_on_target),
|
||||
relayer_id_on_source,
|
||||
proof,
|
||||
messages_count,
|
||||
dispatch_weight
|
||||
)
|
||||
verify {
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()),
|
||||
20 + i as MessageNonce,
|
||||
);
|
||||
assert_eq!(
|
||||
crate::Pallet::<T, I>::inbound_latest_confirmed_nonce(T::bench_lane_id()),
|
||||
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 = crate::Pallet::<T, I>::relayer_fund_account_id();
|
||||
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 relayers_state = UnrewardedRelayersState {
|
||||
unrewarded_relayer_entries: 1,
|
||||
messages_in_oldest_entry: 1,
|
||||
total_messages: i as MessageNonce,
|
||||
};
|
||||
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
|
||||
lane: T::bench_lane_id(),
|
||||
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 {
|
||||
ensure_relayer_rewarded::<T, I>(&relayer_id, &relayer_balance);
|
||||
}
|
||||
|
||||
// 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 = crate::Pallet::<T, I>::relayer_fund_account_id();
|
||||
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 relayers_state = UnrewardedRelayersState {
|
||||
unrewarded_relayer_entries: i as MessageNonce,
|
||||
messages_in_oldest_entry: 1,
|
||||
total_messages: i as MessageNonce,
|
||||
};
|
||||
let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams {
|
||||
lane: T::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(),
|
||||
last_confirmed_nonce: 0,
|
||||
},
|
||||
size: ProofSize::Minimal(0),
|
||||
});
|
||||
}: receive_messages_delivery_proof(RawOrigin::Signed(confirmation_relayer_id), proof, relayers_state)
|
||||
verify {
|
||||
for (relayer_id, prev_balance) in relayers {
|
||||
ensure_relayer_rewarded::<T, I>(&relayer_id, &prev_balance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_regular_message<T: Config<I>, I: Instance>() {
|
||||
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
|
||||
outbound_lane.send_message(MessageData {
|
||||
payload: vec![],
|
||||
fee: MESSAGE_FEE.into(),
|
||||
});
|
||||
}
|
||||
|
||||
fn send_regular_message_with_payload<T: Config<I>, I: Instance>(payload: Vec<u8>) {
|
||||
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
|
||||
outbound_lane.send_message(MessageData {
|
||||
payload,
|
||||
fee: MESSAGE_FEE.into(),
|
||||
});
|
||||
}
|
||||
|
||||
fn confirm_message_delivery<T: Config<I>, I: Instance>(nonce: MessageNonce) {
|
||||
let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id());
|
||||
assert!(outbound_lane.confirm_delivery(nonce).is_some());
|
||||
}
|
||||
|
||||
fn receive_messages<T: Config<I>, I: Instance>(nonce: MessageNonce) {
|
||||
let mut inbound_lane_storage = inbound_lane_storage::<T, I>(T::bench_lane_id());
|
||||
inbound_lane_storage.set_data(InboundLaneData {
|
||||
relayers: vec![(1, nonce, T::bridged_relayer_id())].into_iter().collect(),
|
||||
last_confirmed_nonce: 0,
|
||||
});
|
||||
}
|
||||
|
||||
fn ensure_relayer_rewarded<T: Config<I>, I: Instance>(relayer_id: &T::AccountId, old_balance: &T::OutboundMessageFee) {
|
||||
let new_balance = T::account_balance(relayer_id);
|
||||
assert!(
|
||||
new_balance > *old_balance,
|
||||
"Relayer haven't received reward for relaying message: old balance = {:?}, new balance = {:?}",
|
||||
old_balance,
|
||||
new_balance,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Everything about incoming messages receival.
|
||||
|
||||
use bp_messages::{
|
||||
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
|
||||
InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData,
|
||||
};
|
||||
use sp_std::prelude::PartialEq;
|
||||
|
||||
/// Inbound lane storage.
|
||||
pub trait InboundLaneStorage {
|
||||
/// Delivery and dispatch fee type on source chain.
|
||||
type MessageFee;
|
||||
/// Id of relayer on source chain.
|
||||
type Relayer: PartialEq;
|
||||
|
||||
/// Lane id.
|
||||
fn id(&self) -> LaneId;
|
||||
/// Return maximal number of unrewarded relayer entries in inbound lane.
|
||||
fn max_unrewarded_relayer_entries(&self) -> MessageNonce;
|
||||
/// Return maximal number of unconfirmed messages in inbound lane.
|
||||
fn max_unconfirmed_messages(&self) -> MessageNonce;
|
||||
/// Get lane data from the storage.
|
||||
fn data(&self) -> InboundLaneData<Self::Relayer>;
|
||||
/// Update lane data in the storage.
|
||||
fn set_data(&mut self, data: InboundLaneData<Self::Relayer>);
|
||||
}
|
||||
|
||||
/// Inbound messages lane.
|
||||
pub struct InboundLane<S> {
|
||||
storage: S,
|
||||
}
|
||||
|
||||
impl<S: InboundLaneStorage> InboundLane<S> {
|
||||
/// Create new inbound lane backed by given storage.
|
||||
pub fn new(storage: S) -> Self {
|
||||
InboundLane { storage }
|
||||
}
|
||||
|
||||
/// Receive state of the corresponding outbound lane.
|
||||
pub fn receive_state_update(&mut self, outbound_lane_data: OutboundLaneData) -> Option<MessageNonce> {
|
||||
let mut data = self.storage.data();
|
||||
let last_delivered_nonce = data.last_delivered_nonce();
|
||||
|
||||
if outbound_lane_data.latest_received_nonce > last_delivered_nonce {
|
||||
// this is something that should never happen if proofs are correct
|
||||
return None;
|
||||
}
|
||||
if outbound_lane_data.latest_received_nonce <= data.last_confirmed_nonce {
|
||||
return None;
|
||||
}
|
||||
|
||||
let new_confirmed_nonce = outbound_lane_data.latest_received_nonce;
|
||||
data.last_confirmed_nonce = new_confirmed_nonce;
|
||||
// Firstly, remove all of the records where higher nonce <= new confirmed nonce
|
||||
while data
|
||||
.relayers
|
||||
.front()
|
||||
.map(|(_, nonce_high, _)| *nonce_high <= new_confirmed_nonce)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
data.relayers.pop_front();
|
||||
}
|
||||
// Secondly, update the next record with lower nonce equal to new confirmed nonce if needed.
|
||||
// Note: There will be max. 1 record to update as we don't allow messages from relayers to overlap.
|
||||
match data.relayers.front_mut() {
|
||||
Some((nonce_low, _, _)) if *nonce_low < new_confirmed_nonce => {
|
||||
*nonce_low = new_confirmed_nonce + 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.storage.set_data(data);
|
||||
Some(outbound_lane_data.latest_received_nonce)
|
||||
}
|
||||
|
||||
/// Receive new message.
|
||||
pub fn receive_message<P: MessageDispatch<S::MessageFee>>(
|
||||
&mut self,
|
||||
relayer: S::Relayer,
|
||||
nonce: MessageNonce,
|
||||
message_data: DispatchMessageData<P::DispatchPayload, S::MessageFee>,
|
||||
) -> bool {
|
||||
let mut data = self.storage.data();
|
||||
let is_correct_message = nonce == data.last_delivered_nonce() + 1;
|
||||
if !is_correct_message {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there are more unrewarded relayer entries than we may accept, reject this message
|
||||
if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there are more unconfirmed messages than we may accept, reject this message
|
||||
let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce);
|
||||
if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let push_new = match data.relayers.back_mut() {
|
||||
Some((_, nonce_high, last_relayer)) if last_relayer == &relayer => {
|
||||
*nonce_high = nonce;
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if push_new {
|
||||
data.relayers.push_back((nonce, nonce, relayer));
|
||||
}
|
||||
|
||||
self.storage.set_data(data);
|
||||
|
||||
P::dispatch(DispatchMessage {
|
||||
key: MessageKey {
|
||||
lane_id: self.storage.id(),
|
||||
nonce,
|
||||
},
|
||||
data: message_data,
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
inbound_lane,
|
||||
mock::{
|
||||
message_data, run_test, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A,
|
||||
TEST_RELAYER_B, TEST_RELAYER_C,
|
||||
},
|
||||
DefaultInstance, RuntimeInboundLaneStorage,
|
||||
};
|
||||
|
||||
fn receive_regular_message(
|
||||
lane: &mut InboundLane<RuntimeInboundLaneStorage<TestRuntime, DefaultInstance>>,
|
||||
nonce: MessageNonce,
|
||||
) {
|
||||
assert!(lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A,
|
||||
nonce,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receive_status_update_ignores_status_from_the_future() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
receive_regular_message(&mut lane, 1);
|
||||
assert_eq!(
|
||||
lane.receive_state_update(OutboundLaneData {
|
||||
latest_received_nonce: 10,
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(lane.storage.data().last_confirmed_nonce, 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receive_status_update_ignores_obsolete_status() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
receive_regular_message(&mut lane, 1);
|
||||
receive_regular_message(&mut lane, 2);
|
||||
receive_regular_message(&mut lane, 3);
|
||||
assert_eq!(
|
||||
lane.receive_state_update(OutboundLaneData {
|
||||
latest_received_nonce: 3,
|
||||
..Default::default()
|
||||
}),
|
||||
Some(3),
|
||||
);
|
||||
assert_eq!(lane.storage.data().last_confirmed_nonce, 3);
|
||||
|
||||
assert_eq!(
|
||||
lane.receive_state_update(OutboundLaneData {
|
||||
latest_received_nonce: 3,
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
assert_eq!(lane.storage.data().last_confirmed_nonce, 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receive_status_update_works() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
receive_regular_message(&mut lane, 1);
|
||||
receive_regular_message(&mut lane, 2);
|
||||
receive_regular_message(&mut lane, 3);
|
||||
assert_eq!(lane.storage.data().last_confirmed_nonce, 0);
|
||||
assert_eq!(lane.storage.data().relayers, vec![(1, 3, TEST_RELAYER_A)]);
|
||||
|
||||
assert_eq!(
|
||||
lane.receive_state_update(OutboundLaneData {
|
||||
latest_received_nonce: 2,
|
||||
..Default::default()
|
||||
}),
|
||||
Some(2),
|
||||
);
|
||||
assert_eq!(lane.storage.data().last_confirmed_nonce, 2);
|
||||
assert_eq!(lane.storage.data().relayers, vec![(3, 3, TEST_RELAYER_A)]);
|
||||
|
||||
assert_eq!(
|
||||
lane.receive_state_update(OutboundLaneData {
|
||||
latest_received_nonce: 3,
|
||||
..Default::default()
|
||||
}),
|
||||
Some(3),
|
||||
);
|
||||
assert_eq!(lane.storage.data().last_confirmed_nonce, 3);
|
||||
assert_eq!(lane.storage.data().relayers, vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receive_status_update_works_with_batches_from_relayers() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
let mut seed_storage_data = lane.storage.data();
|
||||
// Prepare data
|
||||
seed_storage_data.last_confirmed_nonce = 0;
|
||||
seed_storage_data.relayers.push_back((1, 1, TEST_RELAYER_A));
|
||||
// Simulate messages batch (2, 3, 4) from relayer #2
|
||||
seed_storage_data.relayers.push_back((2, 4, TEST_RELAYER_B));
|
||||
seed_storage_data.relayers.push_back((5, 5, TEST_RELAYER_C));
|
||||
lane.storage.set_data(seed_storage_data);
|
||||
// Check
|
||||
assert_eq!(
|
||||
lane.receive_state_update(OutboundLaneData {
|
||||
latest_received_nonce: 3,
|
||||
..Default::default()
|
||||
}),
|
||||
Some(3),
|
||||
);
|
||||
assert_eq!(lane.storage.data().last_confirmed_nonce, 3);
|
||||
assert_eq!(
|
||||
lane.storage.data().relayers,
|
||||
vec![(4, 4, TEST_RELAYER_B), (5, 5, TEST_RELAYER_C)]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_to_receive_message_with_incorrect_nonce() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
assert!(!lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A,
|
||||
10,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
assert_eq!(lane.storage.data().last_delivered_nonce(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
let max_nonce = <TestRuntime as crate::Config>::MaxUnrewardedRelayerEntriesAtInboundLane::get();
|
||||
for current_nonce in 1..max_nonce + 1 {
|
||||
assert!(lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A + current_nonce,
|
||||
current_nonce,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
}
|
||||
// Fails to dispatch new message from different than latest relayer.
|
||||
assert_eq!(
|
||||
false,
|
||||
lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A + max_nonce + 1,
|
||||
max_nonce + 1,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
)
|
||||
);
|
||||
// Fails to dispatch new messages from latest relayer. Prevents griefing attacks.
|
||||
assert_eq!(
|
||||
false,
|
||||
lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A + max_nonce,
|
||||
max_nonce + 1,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
let max_nonce = <TestRuntime as crate::Config>::MaxUnconfirmedMessagesAtInboundLane::get();
|
||||
for current_nonce in 1..=max_nonce {
|
||||
assert!(lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A,
|
||||
current_nonce,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
}
|
||||
// Fails to dispatch new message from different than latest relayer.
|
||||
assert_eq!(
|
||||
false,
|
||||
lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_B,
|
||||
max_nonce + 1,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
)
|
||||
);
|
||||
// Fails to dispatch new messages from latest relayer.
|
||||
assert_eq!(
|
||||
false,
|
||||
lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A,
|
||||
max_nonce + 1,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn correctly_receives_following_messages_from_two_relayers_alternately() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
assert!(lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A,
|
||||
1,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
assert!(lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_B,
|
||||
2,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
assert!(lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A,
|
||||
3,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
assert_eq!(
|
||||
lane.storage.data().relayers,
|
||||
vec![(1, 1, TEST_RELAYER_A), (2, 2, TEST_RELAYER_B), (3, 3, TEST_RELAYER_A)]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_same_message_from_two_different_relayers() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
assert!(lane.receive_message::<TestMessageDispatch>(
|
||||
TEST_RELAYER_A,
|
||||
1,
|
||||
message_data(REGULAR_PAYLOAD).into()
|
||||
));
|
||||
assert_eq!(
|
||||
false,
|
||||
lane.receive_message::<TestMessageDispatch>(TEST_RELAYER_B, 1, message_data(REGULAR_PAYLOAD).into())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn correct_message_is_processed_instantly() {
|
||||
run_test(|| {
|
||||
let mut lane = inbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
receive_regular_message(&mut lane, 1);
|
||||
assert_eq!(lane.storage.data().last_delivered_nonce(), 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implementation of `MessageDeliveryAndDispatchPayment` trait on top of `Currency` trait.
|
||||
//!
|
||||
//! The payment is first transferred to a special `relayers-fund` account and only transferred
|
||||
//! to the actual relayer in case confirmation is received.
|
||||
|
||||
use bp_messages::{
|
||||
source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards, Sender},
|
||||
MessageNonce,
|
||||
};
|
||||
use codec::Encode;
|
||||
use frame_support::traits::{Currency as CurrencyT, ExistenceRequirement, Get};
|
||||
use num_traits::Zero;
|
||||
use sp_runtime::traits::Saturating;
|
||||
use sp_std::fmt::Debug;
|
||||
|
||||
/// Instant message payments made in given currency.
|
||||
///
|
||||
/// The balance is initally reserved in a special `relayers-fund` account, and transferred
|
||||
/// to the relayer when message delivery is confirmed.
|
||||
///
|
||||
/// Additionaly, confirmation transaction submitter (`confirmation_relayer`) is reimbursed
|
||||
/// with the confirmation rewards (part of message fee, reserved to pay for delivery confirmation).
|
||||
///
|
||||
/// NOTE The `relayers-fund` account must always exist i.e. be over Existential Deposit (ED; the
|
||||
/// pallet enforces that) to make sure that even if the message cost is below ED it is still payed
|
||||
/// to the relayer account.
|
||||
/// NOTE It's within relayer's interest to keep their balance above ED as well, to make sure they
|
||||
/// can receive the payment.
|
||||
pub struct InstantCurrencyPayments<T, Currency, GetConfirmationFee, RootAccount> {
|
||||
_phantom: sp_std::marker::PhantomData<(T, Currency, GetConfirmationFee, RootAccount)>,
|
||||
}
|
||||
|
||||
impl<T, Currency, GetConfirmationFee, RootAccount> MessageDeliveryAndDispatchPayment<T::AccountId, Currency::Balance>
|
||||
for InstantCurrencyPayments<T, Currency, GetConfirmationFee, RootAccount>
|
||||
where
|
||||
T: frame_system::Config,
|
||||
Currency: CurrencyT<T::AccountId>,
|
||||
Currency::Balance: From<MessageNonce>,
|
||||
GetConfirmationFee: Get<Currency::Balance>,
|
||||
RootAccount: Get<Option<T::AccountId>>,
|
||||
{
|
||||
type Error = &'static str;
|
||||
|
||||
fn initialize(relayer_fund_account: &T::AccountId) -> usize {
|
||||
assert!(
|
||||
frame_system::Pallet::<T>::account_exists(relayer_fund_account),
|
||||
"The relayer fund account ({:?}) must exist for the message lanes pallet to work correctly.",
|
||||
relayer_fund_account,
|
||||
);
|
||||
1
|
||||
}
|
||||
|
||||
fn pay_delivery_and_dispatch_fee(
|
||||
submitter: &Sender<T::AccountId>,
|
||||
fee: &Currency::Balance,
|
||||
relayer_fund_account: &T::AccountId,
|
||||
) -> Result<(), Self::Error> {
|
||||
let root_account = RootAccount::get();
|
||||
let account = match submitter {
|
||||
Sender::Signed(submitter) => submitter,
|
||||
Sender::Root | Sender::None => root_account
|
||||
.as_ref()
|
||||
.ok_or("Sending messages using Root or None origin is disallowed.")?,
|
||||
};
|
||||
|
||||
Currency::transfer(
|
||||
account,
|
||||
relayer_fund_account,
|
||||
*fee,
|
||||
// it's fine for the submitter to go below Existential Deposit and die.
|
||||
ExistenceRequirement::AllowDeath,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn pay_relayers_rewards(
|
||||
confirmation_relayer: &T::AccountId,
|
||||
relayers_rewards: RelayersRewards<T::AccountId, Currency::Balance>,
|
||||
relayer_fund_account: &T::AccountId,
|
||||
) {
|
||||
pay_relayers_rewards::<Currency, _>(
|
||||
confirmation_relayer,
|
||||
relayers_rewards,
|
||||
relayer_fund_account,
|
||||
GetConfirmationFee::get(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Pay rewards to given relayers, optionally rewarding confirmation relayer.
|
||||
fn pay_relayers_rewards<Currency, AccountId>(
|
||||
confirmation_relayer: &AccountId,
|
||||
relayers_rewards: RelayersRewards<AccountId, Currency::Balance>,
|
||||
relayer_fund_account: &AccountId,
|
||||
confirmation_fee: Currency::Balance,
|
||||
) where
|
||||
AccountId: Debug + Default + Encode + PartialEq,
|
||||
Currency: CurrencyT<AccountId>,
|
||||
Currency::Balance: From<u64>,
|
||||
{
|
||||
// reward every relayer except `confirmation_relayer`
|
||||
let mut confirmation_relayer_reward = Currency::Balance::zero();
|
||||
for (relayer, reward) in relayers_rewards {
|
||||
let mut relayer_reward = reward.reward;
|
||||
|
||||
if relayer != *confirmation_relayer {
|
||||
// If delivery confirmation is submitted by other relayer, let's deduct confirmation fee
|
||||
// from relayer reward.
|
||||
//
|
||||
// If confirmation fee has been increased (or if it was the only component of message fee),
|
||||
// then messages relayer may receive zero reward.
|
||||
let mut confirmation_reward = confirmation_fee.saturating_mul(reward.messages.into());
|
||||
if confirmation_reward > relayer_reward {
|
||||
confirmation_reward = relayer_reward;
|
||||
}
|
||||
relayer_reward = relayer_reward.saturating_sub(confirmation_reward);
|
||||
confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(confirmation_reward);
|
||||
} else {
|
||||
// If delivery confirmation is submitted by this relayer, let's add confirmation fee
|
||||
// from other relayers to this relayer reward.
|
||||
confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(reward.reward);
|
||||
continue;
|
||||
}
|
||||
|
||||
pay_relayer_reward::<Currency, _>(relayer_fund_account, &relayer, relayer_reward);
|
||||
}
|
||||
|
||||
// finally - pay reward to confirmation relayer
|
||||
pay_relayer_reward::<Currency, _>(relayer_fund_account, confirmation_relayer, confirmation_relayer_reward);
|
||||
}
|
||||
|
||||
/// Transfer funds from relayers fund account to given relayer.
|
||||
fn pay_relayer_reward<Currency, AccountId>(
|
||||
relayer_fund_account: &AccountId,
|
||||
relayer_account: &AccountId,
|
||||
reward: Currency::Balance,
|
||||
) where
|
||||
AccountId: Debug,
|
||||
Currency: CurrencyT<AccountId>,
|
||||
{
|
||||
if reward.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
let pay_result = Currency::transfer(
|
||||
relayer_fund_account,
|
||||
relayer_account,
|
||||
reward,
|
||||
// the relayer fund account must stay above ED (needs to be pre-funded)
|
||||
ExistenceRequirement::KeepAlive,
|
||||
);
|
||||
|
||||
match pay_result {
|
||||
Ok(_) => log::trace!(
|
||||
target: "runtime::bridge-messages",
|
||||
"Rewarded relayer {:?} with {:?}",
|
||||
relayer_account,
|
||||
reward,
|
||||
),
|
||||
Err(error) => log::trace!(
|
||||
target: "runtime::bridge-messages",
|
||||
"Failed to pay relayer {:?} reward {:?}: {:?}",
|
||||
relayer_account,
|
||||
reward,
|
||||
error,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mock::{run_test, AccountId as TestAccountId, Balance as TestBalance, TestRuntime};
|
||||
use bp_messages::source_chain::RelayerRewards;
|
||||
|
||||
type Balances = pallet_balances::Pallet<TestRuntime>;
|
||||
|
||||
const RELAYER_1: TestAccountId = 1;
|
||||
const RELAYER_2: TestAccountId = 2;
|
||||
const RELAYER_3: TestAccountId = 3;
|
||||
const RELAYERS_FUND_ACCOUNT: TestAccountId = crate::mock::ENDOWED_ACCOUNT;
|
||||
|
||||
fn relayers_rewards() -> RelayersRewards<TestAccountId, TestBalance> {
|
||||
vec![
|
||||
(
|
||||
RELAYER_1,
|
||||
RelayerRewards {
|
||||
reward: 100,
|
||||
messages: 2,
|
||||
},
|
||||
),
|
||||
(
|
||||
RELAYER_2,
|
||||
RelayerRewards {
|
||||
reward: 100,
|
||||
messages: 3,
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() {
|
||||
run_test(|| {
|
||||
pay_relayers_rewards::<Balances, _>(&RELAYER_2, relayers_rewards(), &RELAYERS_FUND_ACCOUNT, 10);
|
||||
|
||||
assert_eq!(Balances::free_balance(&RELAYER_1), 80);
|
||||
assert_eq!(Balances::free_balance(&RELAYER_2), 120);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirmation_relayer_is_rewarded_if_it_has_not_delivered_any_delivered_messages() {
|
||||
run_test(|| {
|
||||
pay_relayers_rewards::<Balances, _>(&RELAYER_3, relayers_rewards(), &RELAYERS_FUND_ACCOUNT, 10);
|
||||
|
||||
assert_eq!(Balances::free_balance(&RELAYER_1), 80);
|
||||
assert_eq!(Balances::free_balance(&RELAYER_2), 70);
|
||||
assert_eq!(Balances::free_balance(&RELAYER_3), 50);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_confirmation_relayer_is_rewarded_if_confirmation_fee_has_significantly_increased() {
|
||||
run_test(|| {
|
||||
pay_relayers_rewards::<Balances, _>(&RELAYER_3, relayers_rewards(), &RELAYERS_FUND_ACCOUNT, 1000);
|
||||
|
||||
assert_eq!(Balances::free_balance(&RELAYER_1), 0);
|
||||
assert_eq!(Balances::free_balance(&RELAYER_2), 0);
|
||||
assert_eq!(Balances::free_balance(&RELAYER_3), 200);
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,404 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// From construct_runtime macro
|
||||
#![allow(clippy::from_over_into)]
|
||||
|
||||
use crate::Config;
|
||||
|
||||
use bp_messages::{
|
||||
source_chain::{
|
||||
LaneMessageVerifier, MessageDeliveryAndDispatchPayment, RelayersRewards, Sender, TargetHeaderChain,
|
||||
},
|
||||
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
|
||||
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
|
||||
Parameter as MessagesParameter,
|
||||
};
|
||||
use bp_runtime::Size;
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{parameter_types, weights::Weight};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header as SubstrateHeader,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
FixedU128, Perbill,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub type AccountId = u64;
|
||||
pub type Balance = u64;
|
||||
#[derive(Decode, Encode, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TestPayload(pub u64, pub Weight);
|
||||
pub type TestMessageFee = u64;
|
||||
pub type TestRelayer = u64;
|
||||
|
||||
pub struct AccountIdConverter;
|
||||
|
||||
impl sp_runtime::traits::Convert<H256, AccountId> for AccountIdConverter {
|
||||
fn convert(hash: H256) -> AccountId {
|
||||
hash.to_low_u64_ne()
|
||||
}
|
||||
}
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<TestRuntime>;
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;
|
||||
|
||||
use crate as pallet_bridge_messages;
|
||||
|
||||
frame_support::construct_runtime! {
|
||||
pub enum TestRuntime where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic,
|
||||
{
|
||||
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
|
||||
Balances: pallet_balances::{Pallet, Call, Event<T>},
|
||||
Messages: pallet_bridge_messages::{Pallet, Call, Event<T>},
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const MaximumBlockWeight: Weight = 1024;
|
||||
pub const MaximumBlockLength: u32 = 2 * 1024;
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
}
|
||||
|
||||
impl frame_system::Config for TestRuntime {
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type Call = Call;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = SubstrateHeader;
|
||||
type Event = Event;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type BaseCallFilter = ();
|
||||
type SystemWeightInfo = ();
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type SS58Prefix = ();
|
||||
type OnSetCode = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for TestRuntime {
|
||||
type MaxLocks = ();
|
||||
type Balance = Balance;
|
||||
type DustRemoval = ();
|
||||
type Event = Event;
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = frame_system::Pallet<TestRuntime>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const MaxMessagesToPruneAtOnce: u64 = 10;
|
||||
pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16;
|
||||
pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32;
|
||||
pub storage TokenConversionRate: FixedU128 = 1.into();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
|
||||
pub enum TestMessagesParameter {
|
||||
TokenConversionRate(FixedU128),
|
||||
}
|
||||
|
||||
impl MessagesParameter for TestMessagesParameter {
|
||||
fn save(&self) {
|
||||
match *self {
|
||||
TestMessagesParameter::TokenConversionRate(conversion_rate) => TokenConversionRate::set(&conversion_rate),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config for TestRuntime {
|
||||
type Event = Event;
|
||||
type WeightInfo = ();
|
||||
type Parameter = TestMessagesParameter;
|
||||
type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce;
|
||||
type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane;
|
||||
type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane;
|
||||
|
||||
type OutboundPayload = TestPayload;
|
||||
type OutboundMessageFee = TestMessageFee;
|
||||
|
||||
type InboundPayload = TestPayload;
|
||||
type InboundMessageFee = TestMessageFee;
|
||||
type InboundRelayer = TestRelayer;
|
||||
|
||||
type AccountIdConverter = AccountIdConverter;
|
||||
|
||||
type TargetHeaderChain = TestTargetHeaderChain;
|
||||
type LaneMessageVerifier = TestLaneMessageVerifier;
|
||||
type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment;
|
||||
|
||||
type SourceHeaderChain = TestSourceHeaderChain;
|
||||
type MessageDispatch = TestMessageDispatch;
|
||||
}
|
||||
|
||||
impl Size for TestPayload {
|
||||
fn size_hint(&self) -> u32 {
|
||||
16
|
||||
}
|
||||
}
|
||||
|
||||
/// Account that has balance to use in tests.
|
||||
pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD;
|
||||
|
||||
/// Account id of test relayer.
|
||||
pub const TEST_RELAYER_A: AccountId = 100;
|
||||
|
||||
/// Account id of additional test relayer - B.
|
||||
pub const TEST_RELAYER_B: AccountId = 101;
|
||||
|
||||
/// Account id of additional test relayer - C.
|
||||
pub const TEST_RELAYER_C: AccountId = 102;
|
||||
|
||||
/// Error that is returned by all test implementations.
|
||||
pub const TEST_ERROR: &str = "Test error";
|
||||
|
||||
/// Lane that we're using in tests.
|
||||
pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1];
|
||||
|
||||
/// Regular message payload.
|
||||
pub const REGULAR_PAYLOAD: TestPayload = TestPayload(0, 50);
|
||||
|
||||
/// Payload that is rejected by `TestTargetHeaderChain`.
|
||||
pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = TestPayload(1, 50);
|
||||
|
||||
/// Vec of proved messages, grouped by lane.
|
||||
pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages<Message<TestMessageFee>>)>;
|
||||
|
||||
/// Test messages proof.
|
||||
#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq)]
|
||||
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 {
|
||||
result: result.map(|messages| {
|
||||
let mut messages_by_lane: BTreeMap<LaneId, ProvedLaneMessages<Message<TestMessageFee>>> =
|
||||
BTreeMap::new();
|
||||
for message in messages {
|
||||
messages_by_lane
|
||||
.entry(message.key.lane_id)
|
||||
.or_default()
|
||||
.messages
|
||||
.push(message);
|
||||
}
|
||||
messages_by_lane.into_iter().collect()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages delivery proof used in tests.
|
||||
#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq)]
|
||||
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;
|
||||
|
||||
impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
|
||||
type Error = &'static str;
|
||||
|
||||
type MessagesDeliveryProof = TestMessagesDeliveryProof;
|
||||
|
||||
fn verify_message(payload: &TestPayload) -> Result<(), Self::Error> {
|
||||
if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN {
|
||||
Err(TEST_ERROR)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_messages_delivery_proof(
|
||||
proof: Self::MessagesDeliveryProof,
|
||||
) -> Result<(LaneId, InboundLaneData<TestRelayer>), Self::Error> {
|
||||
proof.0.map_err(|_| TEST_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
/// Lane message verifier that is used in tests.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TestLaneMessageVerifier;
|
||||
|
||||
impl LaneMessageVerifier<AccountId, TestPayload, TestMessageFee> for TestLaneMessageVerifier {
|
||||
type Error = &'static str;
|
||||
|
||||
fn verify_message(
|
||||
_submitter: &Sender<AccountId>,
|
||||
delivery_and_dispatch_fee: &TestMessageFee,
|
||||
_lane: &LaneId,
|
||||
_lane_outbound_data: &OutboundLaneData,
|
||||
_payload: &TestPayload,
|
||||
) -> Result<(), Self::Error> {
|
||||
if *delivery_and_dispatch_fee != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(TEST_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Message fee payment system that is used in tests.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TestMessageDeliveryAndDispatchPayment;
|
||||
|
||||
impl TestMessageDeliveryAndDispatchPayment {
|
||||
/// Reject all payments.
|
||||
pub fn reject_payments() {
|
||||
frame_support::storage::unhashed::put(b":reject-message-fee:", &true);
|
||||
}
|
||||
|
||||
/// Returns true if given fee has been paid by given submitter.
|
||||
pub fn is_fee_paid(submitter: AccountId, fee: TestMessageFee) -> bool {
|
||||
frame_support::storage::unhashed::get(b":message-fee:") == Some((Sender::Signed(submitter), fee))
|
||||
}
|
||||
|
||||
/// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is
|
||||
/// cleared after the call.
|
||||
pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool {
|
||||
let key = (b":relayer-reward:", relayer, fee).encode();
|
||||
frame_support::storage::unhashed::take::<bool>(&key).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageDeliveryAndDispatchPayment<AccountId, TestMessageFee> for TestMessageDeliveryAndDispatchPayment {
|
||||
type Error = &'static str;
|
||||
|
||||
fn pay_delivery_and_dispatch_fee(
|
||||
submitter: &Sender<AccountId>,
|
||||
fee: &TestMessageFee,
|
||||
_relayer_fund_account: &AccountId,
|
||||
) -> Result<(), Self::Error> {
|
||||
if frame_support::storage::unhashed::get(b":reject-message-fee:") == Some(true) {
|
||||
return Err(TEST_ERROR);
|
||||
}
|
||||
|
||||
frame_support::storage::unhashed::put(b":message-fee:", &(submitter, fee));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pay_relayers_rewards(
|
||||
_confirmation_relayer: &AccountId,
|
||||
relayers_rewards: RelayersRewards<AccountId, TestMessageFee>,
|
||||
_relayer_fund_account: &AccountId,
|
||||
) {
|
||||
for (relayer, reward) in relayers_rewards {
|
||||
let key = (b":relayer-reward:", relayer, reward.reward).encode();
|
||||
frame_support::storage::unhashed::put(&key, &true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Source header chain that is used in tests.
|
||||
#[derive(Debug)]
|
||||
pub struct TestSourceHeaderChain;
|
||||
|
||||
impl SourceHeaderChain<TestMessageFee> for TestSourceHeaderChain {
|
||||
type Error = &'static str;
|
||||
|
||||
type MessagesProof = TestMessagesProof;
|
||||
|
||||
fn verify_messages_proof(
|
||||
proof: Self::MessagesProof,
|
||||
_messages_count: u32,
|
||||
) -> Result<ProvedMessages<Message<TestMessageFee>>, Self::Error> {
|
||||
proof
|
||||
.result
|
||||
.map(|proof| proof.into_iter().collect())
|
||||
.map_err(|_| TEST_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
/// Source header chain that is used in tests.
|
||||
#[derive(Debug)]
|
||||
pub struct TestMessageDispatch;
|
||||
|
||||
impl MessageDispatch<TestMessageFee> for TestMessageDispatch {
|
||||
type DispatchPayload = TestPayload;
|
||||
|
||||
fn dispatch_weight(message: &DispatchMessage<TestPayload, TestMessageFee>) -> Weight {
|
||||
match message.data.payload.as_ref() {
|
||||
Ok(payload) => payload.1,
|
||||
Err(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch(_message: DispatchMessage<TestPayload, TestMessageFee>) {}
|
||||
}
|
||||
|
||||
/// Return test lane message with given nonce and payload.
|
||||
pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message<TestMessageFee> {
|
||||
Message {
|
||||
key: MessageKey {
|
||||
lane_id: TEST_LANE_ID,
|
||||
nonce,
|
||||
},
|
||||
data: message_data(payload),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return message data with valid fee for given payload.
|
||||
pub fn message_data(payload: TestPayload) -> MessageData<TestMessageFee> {
|
||||
MessageData {
|
||||
payload: payload.encode(),
|
||||
fee: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Run pallet test.
|
||||
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
|
||||
let mut t = frame_system::GenesisConfig::default()
|
||||
.build_storage::<TestRuntime>()
|
||||
.unwrap();
|
||||
pallet_balances::GenesisConfig::<TestRuntime> {
|
||||
balances: vec![(ENDOWED_ACCOUNT, 1_000_000)],
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
ext.execute_with(test)
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Everything about outgoing messages sending.
|
||||
|
||||
use bp_messages::{LaneId, MessageData, MessageNonce, OutboundLaneData};
|
||||
|
||||
/// Outbound lane storage.
|
||||
pub trait OutboundLaneStorage {
|
||||
/// Delivery and dispatch fee type on source chain.
|
||||
type MessageFee;
|
||||
|
||||
/// Lane id.
|
||||
fn id(&self) -> LaneId;
|
||||
/// Get lane data from the storage.
|
||||
fn data(&self) -> OutboundLaneData;
|
||||
/// Update lane data in the storage.
|
||||
fn set_data(&mut self, data: OutboundLaneData);
|
||||
/// Returns saved outbound message payload.
|
||||
#[cfg(test)]
|
||||
fn message(&self, nonce: &MessageNonce) -> Option<MessageData<Self::MessageFee>>;
|
||||
/// Save outbound message in the storage.
|
||||
fn save_message(&mut self, nonce: MessageNonce, message_data: MessageData<Self::MessageFee>);
|
||||
/// Remove outbound message from the storage.
|
||||
fn remove_message(&mut self, nonce: &MessageNonce);
|
||||
}
|
||||
|
||||
/// Outbound messages lane.
|
||||
pub struct OutboundLane<S> {
|
||||
storage: S,
|
||||
}
|
||||
|
||||
impl<S: OutboundLaneStorage> OutboundLane<S> {
|
||||
/// Create new inbound lane backed by given storage.
|
||||
pub fn new(storage: S) -> Self {
|
||||
OutboundLane { storage }
|
||||
}
|
||||
|
||||
/// Get this lane data.
|
||||
pub fn data(&self) -> OutboundLaneData {
|
||||
self.storage.data()
|
||||
}
|
||||
|
||||
/// Send message over lane.
|
||||
///
|
||||
/// Returns new message nonce.
|
||||
pub fn send_message(&mut self, message_data: MessageData<S::MessageFee>) -> MessageNonce {
|
||||
let mut data = self.storage.data();
|
||||
let nonce = data.latest_generated_nonce + 1;
|
||||
data.latest_generated_nonce = nonce;
|
||||
|
||||
self.storage.save_message(nonce, message_data);
|
||||
self.storage.set_data(data);
|
||||
|
||||
nonce
|
||||
}
|
||||
|
||||
/// Confirm messages delivery.
|
||||
///
|
||||
/// Returns `None` if confirmation is wrong/duplicate.
|
||||
/// Returns `Some` with inclusive ranges of message nonces that have been received.
|
||||
pub fn confirm_delivery(&mut self, latest_received_nonce: MessageNonce) -> Option<(MessageNonce, MessageNonce)> {
|
||||
let mut data = self.storage.data();
|
||||
if latest_received_nonce <= data.latest_received_nonce || latest_received_nonce > data.latest_generated_nonce {
|
||||
return None;
|
||||
}
|
||||
|
||||
let prev_latest_received_nonce = data.latest_received_nonce;
|
||||
data.latest_received_nonce = latest_received_nonce;
|
||||
self.storage.set_data(data);
|
||||
|
||||
Some((prev_latest_received_nonce + 1, latest_received_nonce))
|
||||
}
|
||||
|
||||
/// Prune at most `max_messages_to_prune` already received messages.
|
||||
///
|
||||
/// Returns number of pruned messages.
|
||||
pub fn prune_messages(&mut self, max_messages_to_prune: MessageNonce) -> MessageNonce {
|
||||
let mut pruned_messages = 0;
|
||||
let mut anything_changed = false;
|
||||
let mut data = self.storage.data();
|
||||
while pruned_messages < max_messages_to_prune && data.oldest_unpruned_nonce <= data.latest_received_nonce {
|
||||
self.storage.remove_message(&data.oldest_unpruned_nonce);
|
||||
|
||||
anything_changed = true;
|
||||
pruned_messages += 1;
|
||||
data.oldest_unpruned_nonce += 1;
|
||||
}
|
||||
|
||||
if anything_changed {
|
||||
self.storage.set_data(data);
|
||||
}
|
||||
|
||||
pruned_messages
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
mock::{message_data, run_test, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID},
|
||||
outbound_lane,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn send_message_works() {
|
||||
run_test(|| {
|
||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 0);
|
||||
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
|
||||
assert!(lane.storage.message(&1).is_some());
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirm_delivery_works() {
|
||||
run_test(|| {
|
||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 1);
|
||||
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 2);
|
||||
assert_eq!(lane.send_message(message_data(REGULAR_PAYLOAD)), 3);
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirm_delivery_rejects_nonce_lesser_than_latest_received() {
|
||||
run_test(|| {
|
||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||
assert_eq!(lane.confirm_delivery(3), Some((1, 3)));
|
||||
assert_eq!(lane.confirm_delivery(3), None);
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
||||
|
||||
assert_eq!(lane.confirm_delivery(2), None);
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||
assert_eq!(lane.storage.data().latest_received_nonce, 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirm_delivery_rejects_nonce_larger_than_last_generated() {
|
||||
run_test(|| {
|
||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||
assert_eq!(lane.confirm_delivery(10), None);
|
||||
assert_eq!(lane.storage.data().latest_generated_nonce, 3);
|
||||
assert_eq!(lane.storage.data().latest_received_nonce, 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prune_messages_works() {
|
||||
run_test(|| {
|
||||
let mut lane = outbound_lane::<TestRuntime, _>(TEST_LANE_ID);
|
||||
// when lane is empty, nothing is pruned
|
||||
assert_eq!(lane.prune_messages(100), 0);
|
||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
|
||||
// when nothing is confirmed, nothing is pruned
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
lane.send_message(message_data(REGULAR_PAYLOAD));
|
||||
assert_eq!(lane.prune_messages(100), 0);
|
||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1);
|
||||
// after confirmation, some messages are received
|
||||
assert_eq!(lane.confirm_delivery(2), Some((1, 2)));
|
||||
assert_eq!(lane.prune_messages(100), 2);
|
||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3);
|
||||
// after last message is confirmed, everything is pruned
|
||||
assert_eq!(lane.confirm_delivery(3), Some((3, 3)));
|
||||
assert_eq!(lane.prune_messages(100), 1);
|
||||
assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Autogenerated weights for pallet_bridge_messages
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
|
||||
//! DATE: 2021-04-14, STEPS: [50, ], REPEAT: 20
|
||||
//! LOW RANGE: [], HIGH RANGE: []
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
|
||||
//! CHAIN: Some("dev"), DB CACHE: 128
|
||||
|
||||
// Executed Command:
|
||||
// target/release/rialto-bridge-node
|
||||
// benchmark
|
||||
// --chain=dev
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --pallet=pallet_bridge_messages
|
||||
// --extrinsic=*
|
||||
// --execution=wasm
|
||||
// --wasm-execution=Compiled
|
||||
// --heap-pages=4096
|
||||
// --output=./modules/messages/src/weights.rs
|
||||
// --template=./.maintain/rialto-weight-template.hbs
|
||||
|
||||
#![allow(clippy::all)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame_support::{
|
||||
traits::Get,
|
||||
weights::{constants::RocksDbWeight, Weight},
|
||||
};
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_bridge_messages.
|
||||
pub trait WeightInfo {
|
||||
fn send_minimal_message_worst_case() -> Weight;
|
||||
fn send_1_kb_message_worst_case() -> Weight;
|
||||
fn send_16_kb_message_worst_case() -> Weight;
|
||||
fn increase_message_fee() -> Weight;
|
||||
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_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;
|
||||
}
|
||||
|
||||
/// Weights for pallet_bridge_messages using the Rialto node and recommended hardware.
|
||||
pub struct RialtoWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> {
|
||||
fn send_minimal_message_worst_case() -> Weight {
|
||||
(149_497_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn send_1_kb_message_worst_case() -> Weight {
|
||||
(154_339_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn send_16_kb_message_worst_case() -> Weight {
|
||||
(200_066_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn increase_message_fee() -> Weight {
|
||||
(6_432_637_000 as Weight)
|
||||
.saturating_add(T::DbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn receive_single_message_proof() -> Weight {
|
||||
(141_671_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 {
|
||||
(247_393_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 {
|
||||
(159_312_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 {
|
||||
(167_935_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 {
|
||||
(449_846_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_322_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 {
|
||||
(134_120_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 {
|
||||
(191_193_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 {
|
||||
(115_699_000 as Weight)
|
||||
.saturating_add((3_000 as Weight).saturating_mul(i as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn receive_multiple_messages_proof(i: u32) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((113_551_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 {
|
||||
(458_731_000 as Weight)
|
||||
.saturating_add((9_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 {
|
||||
(82_314_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 {
|
||||
(16_766_000 as Weight)
|
||||
.saturating_add((115_533_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_146_000 as Weight)
|
||||
.saturating_add((6_789_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 {
|
||||
(155_671_000 as Weight)
|
||||
.saturating_add((63_020_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))
|
||||
.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight)))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
fn send_minimal_message_worst_case() -> Weight {
|
||||
(149_497_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn send_1_kb_message_worst_case() -> Weight {
|
||||
(154_339_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn send_16_kb_message_worst_case() -> Weight {
|
||||
(200_066_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn increase_message_fee() -> Weight {
|
||||
(6_432_637_000 as Weight)
|
||||
.saturating_add(RocksDbWeight::get().reads(4 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn receive_single_message_proof() -> Weight {
|
||||
(141_671_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 {
|
||||
(247_393_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 {
|
||||
(159_312_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 {
|
||||
(167_935_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 {
|
||||
(449_846_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_322_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 {
|
||||
(134_120_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 {
|
||||
(191_193_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 {
|
||||
(115_699_000 as Weight)
|
||||
.saturating_add((3_000 as Weight).saturating_mul(i as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(12 as Weight))
|
||||
}
|
||||
fn receive_multiple_messages_proof(i: u32) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((113_551_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 {
|
||||
(458_731_000 as Weight)
|
||||
.saturating_add((9_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 {
|
||||
(82_314_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 {
|
||||
(16_766_000 as Weight)
|
||||
.saturating_add((115_533_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_146_000 as Weight)
|
||||
.saturating_add((6_789_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 {
|
||||
(155_671_000 as Weight)
|
||||
.saturating_add((63_020_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))
|
||||
.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(i as Weight)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Bridges Common.
|
||||
|
||||
// Parity Bridges Common is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Bridges Common is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Weight-related utilities.
|
||||
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
use bp_messages::{MessageNonce, UnrewardedRelayersState};
|
||||
use bp_runtime::{PreComputedSize, Size};
|
||||
use frame_support::weights::Weight;
|
||||
|
||||
/// Size of the message being delivered in benchmarks.
|
||||
pub const EXPECTED_DEFAULT_MESSAGE_LENGTH: u32 = 128;
|
||||
|
||||
/// We assume that size of signed extensions on all our chains and size of all 'small' arguments of calls
|
||||
/// we're checking here would fit 1KB.
|
||||
const SIGNED_EXTENSIONS_SIZE: u32 = 1024;
|
||||
|
||||
/// Ensure that weights from `WeightInfoExt` implementation are looking correct.
|
||||
pub fn ensure_weights_are_correct<W: WeightInfoExt>(
|
||||
expected_default_message_delivery_tx_weight: Weight,
|
||||
expected_additional_byte_delivery_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);
|
||||
|
||||
// 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_single_regular_message_delivery_tx_weight <= expected_default_message_delivery_tx_weight,
|
||||
"Default message delivery transaction weight {} is larger than expected weight {}",
|
||||
actual_single_regular_message_delivery_tx_weight,
|
||||
expected_default_message_delivery_tx_weight,
|
||||
);
|
||||
|
||||
// verify that hardcoded value covers additional byte length of `receive_messages_proof` weight
|
||||
let actual_additional_byte_delivery_weight = W::storage_proof_size_overhead(1);
|
||||
assert!(
|
||||
actual_additional_byte_delivery_weight <= expected_additional_byte_delivery_weight,
|
||||
"Single additional byte delivery weight {} is larger than expected weight {}",
|
||||
actual_additional_byte_delivery_weight,
|
||||
expected_additional_byte_delivery_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);
|
||||
|
||||
// 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_messages_delivery_confirmation_tx_weight <= expected_messages_delivery_confirmation_tx_weight,
|
||||
"Messages delivery confirmation transaction weight {} is larger than expected 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,
|
||||
max_incoming_message_dispatch_weight: Weight,
|
||||
) {
|
||||
// verify that we're able to receive proof of maximal-size message
|
||||
let max_delivery_transaction_size = max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE);
|
||||
assert!(
|
||||
max_delivery_transaction_size <= max_extrinsic_size,
|
||||
"Size of maximal message delivery transaction {} + {} is larger than maximal possible transaction size {}",
|
||||
max_incoming_message_proof_size,
|
||||
SIGNED_EXTENSIONS_SIZE,
|
||||
max_extrinsic_size,
|
||||
);
|
||||
|
||||
// verify that we're able to receive proof of maximal-size message with maximal dispatch weight
|
||||
let max_delivery_transaction_dispatch_weight = W::receive_messages_proof_weight(
|
||||
&PreComputedSize((max_incoming_message_proof_size + W::expected_extra_storage_proof_size()) as usize),
|
||||
1,
|
||||
max_incoming_message_dispatch_weight,
|
||||
);
|
||||
assert!(
|
||||
max_delivery_transaction_dispatch_weight <= max_extrinsic_weight,
|
||||
"Weight of maximal message delivery transaction + {} is larger than maximal possible transaction weight {}",
|
||||
max_delivery_transaction_dispatch_weight,
|
||||
max_extrinsic_weight,
|
||||
);
|
||||
}
|
||||
|
||||
/// Ensure that we're able to receive maximal confirmation from other chain.
|
||||
pub fn ensure_able_to_receive_confirmation<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,
|
||||
) {
|
||||
// verify that we're able to receive confirmation of maximal-size
|
||||
let max_confirmation_transaction_size =
|
||||
max_inbound_lane_data_proof_size_from_peer_chain.saturating_add(SIGNED_EXTENSIONS_SIZE);
|
||||
assert!(
|
||||
max_confirmation_transaction_size <= max_extrinsic_size,
|
||||
"Size of maximal message delivery confirmation transaction {} + {} is larger than maximal possible transaction size {}",
|
||||
max_inbound_lane_data_proof_size_from_peer_chain,
|
||||
SIGNED_EXTENSIONS_SIZE,
|
||||
max_extrinsic_size,
|
||||
);
|
||||
|
||||
// verify that we're able to reward maximal number of relayers that have delivered maximal number of messages
|
||||
let max_confirmation_transaction_dispatch_weight = W::receive_messages_delivery_proof_weight(
|
||||
&PreComputedSize(max_inbound_lane_data_proof_size_from_peer_chain as usize),
|
||||
&UnrewardedRelayersState {
|
||||
unrewarded_relayer_entries: max_unrewarded_relayer_entries_at_peer_inbound_lane,
|
||||
total_messages: max_unconfirmed_messages_at_inbound_lane,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
assert!(
|
||||
max_confirmation_transaction_dispatch_weight <= max_extrinsic_weight,
|
||||
"Weight of maximal confirmation transaction {} is larger than maximal possible transaction weight {}",
|
||||
max_confirmation_transaction_dispatch_weight,
|
||||
max_extrinsic_weight,
|
||||
);
|
||||
}
|
||||
|
||||
/// Extended weight info.
|
||||
pub trait WeightInfoExt: WeightInfo {
|
||||
/// Size of proof that is already included in the single message delivery weight.
|
||||
///
|
||||
/// The message submitter (at source chain) has already covered this cost. But there are two
|
||||
/// factors that may increase proof size: (1) the message size may be larger than predefined
|
||||
/// and (2) relayer may add extra trie nodes to the proof. So if proof size is larger than
|
||||
/// this value, we're going to charge relayer for that.
|
||||
fn expected_extra_storage_proof_size() -> u32;
|
||||
|
||||
// Functions that are directly mapped to extrinsics weights.
|
||||
|
||||
/// Weight of message 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()
|
||||
}
|
||||
|
||||
/// Returns weight that needs to be accounted when message of given size is sent (`send_message`).
|
||||
fn send_message_size_overhead(message_size: u32) -> Weight {
|
||||
let message_size_in_kb = (1024u64 + message_size as u64) / 1024;
|
||||
let single_kb_weight = (Self::send_16_kb_message_worst_case() - Self::send_1_kb_message_worst_case()) / 15;
|
||||
message_size_in_kb * single_kb_weight
|
||||
}
|
||||
|
||||
/// Returns weight overhead of message delivery transaction (`receive_messages_proof`).
|
||||
fn receive_messages_proof_overhead() -> Weight {
|
||||
let weight_of_two_messages_and_two_tx_overheads = Self::receive_single_message_proof().saturating_mul(2);
|
||||
let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof();
|
||||
weight_of_two_messages_and_two_tx_overheads.saturating_sub(weight_of_two_messages_and_single_tx_overhead)
|
||||
}
|
||||
|
||||
/// Returns weight that needs to be accounted when receiving given number of messages with message
|
||||
/// delivery transaction (`receive_messages_proof`).
|
||||
fn receive_messages_proof_messages_overhead(messages: MessageNonce) -> Weight {
|
||||
let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof();
|
||||
let weight_of_single_message_and_single_tx_overhead = Self::receive_single_message_proof();
|
||||
weight_of_two_messages_and_single_tx_overhead
|
||||
.saturating_sub(weight_of_single_message_and_single_tx_overhead)
|
||||
.saturating_mul(messages as Weight)
|
||||
}
|
||||
|
||||
/// Returns weight that needs to be accounted when message delivery transaction (`receive_messages_proof`)
|
||||
/// is carrying outbound lane state proof.
|
||||
fn receive_messages_proof_outbound_lane_state_overhead() -> Weight {
|
||||
let weight_of_single_message_and_lane_state = Self::receive_single_message_proof_with_outbound_lane_state();
|
||||
let weight_of_single_message = Self::receive_single_message_proof();
|
||||
weight_of_single_message_and_lane_state.saturating_sub(weight_of_single_message)
|
||||
}
|
||||
|
||||
/// Returns weight overhead of delivery confirmation transaction (`receive_messages_delivery_proof`).
|
||||
fn receive_messages_delivery_proof_overhead() -> Weight {
|
||||
let weight_of_two_messages_and_two_tx_overheads =
|
||||
Self::receive_delivery_proof_for_single_message().saturating_mul(2);
|
||||
let weight_of_two_messages_and_single_tx_overhead =
|
||||
Self::receive_delivery_proof_for_two_messages_by_single_relayer();
|
||||
weight_of_two_messages_and_two_tx_overheads.saturating_sub(weight_of_two_messages_and_single_tx_overhead)
|
||||
}
|
||||
|
||||
/// Returns weight that needs to be accounted when receiving confirmations for given number of
|
||||
/// messages with delivery confirmation transaction (`receive_messages_delivery_proof`).
|
||||
fn receive_messages_delivery_proof_messages_overhead(messages: MessageNonce) -> Weight {
|
||||
let weight_of_two_messages = Self::receive_delivery_proof_for_two_messages_by_single_relayer();
|
||||
let weight_of_single_message = Self::receive_delivery_proof_for_single_message();
|
||||
weight_of_two_messages
|
||||
.saturating_sub(weight_of_single_message)
|
||||
.saturating_mul(messages as Weight)
|
||||
}
|
||||
|
||||
/// Returns weight that needs to be accounted when receiving confirmations for given number of
|
||||
/// relayers entries with delivery confirmation transaction (`receive_messages_delivery_proof`).
|
||||
fn receive_messages_delivery_proof_relayers_overhead(relayers: MessageNonce) -> Weight {
|
||||
let weight_of_two_messages_by_two_relayers = Self::receive_delivery_proof_for_two_messages_by_two_relayers();
|
||||
let weight_of_two_messages_by_single_relayer =
|
||||
Self::receive_delivery_proof_for_two_messages_by_single_relayer();
|
||||
weight_of_two_messages_by_two_relayers
|
||||
.saturating_sub(weight_of_two_messages_by_single_relayer)
|
||||
.saturating_mul(relayers as 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 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user