Account proof size in weight formula (#679)

* fix broken message lane benchmarks

* proof-size related benchmarks

* impl Size for proof parameters

* include proof weight into weight formula

* left TODO

* fixed proof size

* WeightInfoExt::receive_messages_proof_weight

* charge for extra message bytes delivery in send_message

* removed default impl of WeightsInfoExt

* moved weight formulas to WeightInfoExt

* receive_messages_proof_outbound_lane_state_overhead is included twice in weight

* typo

* typo

* fixed TODO

* more asserts

* started wotk on message-lane documentation

* expected_extra_storage_proof_size() is actually expected in delivery confirmation tx

* update README.md

* ensure_able_to_receive_confirmation

* test rialto message lane weights

* removed TODO

* removed unnecessary trait requirements

* fixed arguments

* fix compilation

* decreased basic delivery tx weight

* fmt

* clippy

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

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

* structs

* Update primitives/millau/src/lib.rs

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

* removed readme.md

* removed obsolete trait bounds

* Revert "removed readme.md"

This reverts commit 50b7376a41687a94c27bf77565434be153f87ca1.

* Update bin/runtime-common/src/messages.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update bin/runtime-common/src/messages.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update bin/runtime-common/src/messages.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update bin/runtime-common/src/messages.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update bin/runtime-common/src/messages.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update bin/runtime-common/src/messages.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update bin/runtime-common/src/messages.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* PreComputedSize

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
Svyatoslav Nikolsky
2021-02-10 20:26:47 +03:00
committed by Bastian Köcher
parent fb7c191234
commit 2f457775bb
25 changed files with 918 additions and 303 deletions
+123 -73
View File
@@ -26,13 +26,13 @@ use bp_message_lane::{
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages},
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
};
use bp_runtime::InstanceId;
use bp_runtime::{InstanceId, Size};
use codec::{Decode, Encode};
use frame_support::{traits::Instance, weights::Weight, RuntimeDebug};
use hash_db::Hasher;
use pallet_substrate_bridge::StorageProofChecker;
use sp_runtime::traits::{CheckedAdd, CheckedDiv, CheckedMul};
use sp_std::{cmp::PartialOrd, marker::PhantomData, ops::RangeInclusive, vec::Vec};
use sp_std::{cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, ops::RangeInclusive, vec::Vec};
use sp_trie::StorageProof;
/// Bidirectional message bridge.
@@ -115,6 +115,9 @@ pub(crate) type BalanceOf<C> = <C as ChainWithMessageLanes>::Balance;
pub(crate) type CallOf<C> = <C as ChainWithMessageLanes>::Call;
pub(crate) type MessageLaneInstanceOf<C> = <C as ChainWithMessageLanes>::MessageLaneInstance;
/// Raw storage proof type (just raw trie nodes).
type RawStorageProof = Vec<Vec<u8>>;
/// Compute weight of transaction at runtime where:
///
/// - transaction payment pallet is being used;
@@ -160,7 +163,26 @@ pub mod source {
/// - hash of finalized header;
/// - storage proof of inbound lane state;
/// - lane id.
pub type FromBridgedChainMessagesDeliveryProof<B> = (HashOf<BridgedChain<B>>, StorageProof, LaneId);
#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug)]
pub struct FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
/// Hash of the bridge header the proof is for.
pub bridged_header_hash: BridgedHeaderHash,
/// Storage trie proof generated for [`Self::bridged_header_hash`].
pub storage_proof: RawStorageProof,
/// Lane id of which messages were delivered and the proof is for.
pub lane: LaneId,
}
impl<BridgedHeaderHash> Size for FromBridgedChainMessagesDeliveryProof<BridgedHeaderHash> {
fn size_hint(&self) -> u32 {
u32::try_from(
self.storage_proof
.iter()
.fold(0usize, |sum, node| sum.saturating_add(node.len())),
)
.unwrap_or(u32::MAX)
}
}
/// 'Parsed' message delivery proof - inbound lane id and its state.
pub type ParsedMessagesDeliveryProofFromBridgedChain<B> = (LaneId, InboundLaneData<AccountIdOf<ThisChain<B>>>);
@@ -204,7 +226,7 @@ pub mod source {
/// Return maximal message size of This -> Bridged chain message.
pub fn maximal_message_size<B: MessageBridge>() -> u32 {
B::maximal_extrinsic_size_on_target_chain() / 3 * 2
super::target::maximal_incoming_message_size(B::maximal_extrinsic_size_on_target_chain())
}
/// Do basic Bridged-chain specific verification of This -> Bridged chain message.
@@ -274,7 +296,7 @@ pub mod source {
/// Verify proof of This -> Bridged chain messages delivery.
pub fn verify_messages_delivery_proof<B: MessageBridge, ThisRuntime>(
proof: FromBridgedChainMessagesDeliveryProof<B>,
proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>,
) -> Result<ParsedMessagesDeliveryProofFromBridgedChain<B>, &'static str>
where
ThisRuntime: pallet_substrate_bridge::Config,
@@ -282,10 +304,14 @@ pub mod source {
HashOf<BridgedChain<B>>:
Into<bp_runtime::HashOf<<ThisRuntime as pallet_substrate_bridge::Config>::BridgedChain>>,
{
let (bridged_header_hash, bridged_storage_proof, lane) = proof;
let FromBridgedChainMessagesDeliveryProof {
bridged_header_hash,
storage_proof,
lane,
} = proof;
pallet_substrate_bridge::Module::<ThisRuntime>::parse_finalized_storage_proof(
bridged_header_hash.into(),
bridged_storage_proof,
StorageProof::new(storage_proof),
|storage| {
// Messages delivery proof is just proof of single storage key read => any error
// is fatal.
@@ -332,13 +358,29 @@ pub mod target {
/// - storage proof of messages and (optionally) outbound lane state;
/// - lane id;
/// - nonces (inclusive range) of messages which are included in this proof.
pub type FromBridgedChainMessagesProof<B> = (
HashOf<BridgedChain<B>>,
StorageProof,
LaneId,
MessageNonce,
MessageNonce,
);
#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug)]
pub struct FromBridgedChainMessagesProof<BridgedHeaderHash> {
/// Hash of the finalized bridged header the proof is for.
pub bridged_header_hash: BridgedHeaderHash,
/// A storage trie proof of messages being delivered.
pub storage_proof: RawStorageProof,
pub lane: LaneId,
/// Nonce of the first message being delivered.
pub nonces_start: MessageNonce,
/// Nonce of the last message being delivered.
pub nonces_end: MessageNonce,
}
impl<BridgedHeaderHash> Size for FromBridgedChainMessagesProof<BridgedHeaderHash> {
fn size_hint(&self) -> u32 {
u32::try_from(
self.storage_proof
.iter()
.fold(0usize, |sum, node| sum.saturating_add(node.len())),
)
.unwrap_or(u32::MAX)
}
}
/// Encoded Call of This chain as it is transferred over bridge.
///
@@ -391,13 +433,23 @@ pub mod target {
}
}
/// Return maximal dispatch weight of the message we're able to receive.
pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight {
maximal_extrinsic_weight / 2
}
/// Return maximal message size given maximal extrinsic size.
pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 {
maximal_extrinsic_size / 3 * 2
}
/// Verify proof of Bridged -> This chain messages.
///
/// The `messages_count` argument verification (sane limits) is supposed to be made
/// outside of this function. This function only verifies that the proof declares exactly
/// `messages_count` messages.
pub fn verify_messages_proof<B: MessageBridge, ThisRuntime>(
proof: FromBridgedChainMessagesProof<B>,
proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>,
messages_count: u32,
) -> Result<ProvedMessages<Message<BalanceOf<BridgedChain<B>>>>, &'static str>
where
@@ -412,7 +464,7 @@ pub mod target {
|bridged_header_hash, bridged_storage_proof| {
pallet_substrate_bridge::Module::<ThisRuntime>::parse_finalized_storage_proof(
bridged_header_hash.into(),
bridged_storage_proof,
StorageProof::new(bridged_storage_proof),
|storage_adapter| storage_adapter,
)
.map(|storage| StorageProofCheckerAdapter::<_, B, ThisRuntime> {
@@ -486,18 +538,24 @@ pub mod target {
/// Verify proof of Bridged -> This chain messages using given message proof parser.
pub(crate) fn verify_messages_proof_with_parser<B: MessageBridge, BuildParser, Parser>(
proof: FromBridgedChainMessagesProof<B>,
proof: FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>,
messages_count: u32,
build_parser: BuildParser,
) -> Result<ProvedMessages<Message<BalanceOf<BridgedChain<B>>>>, MessageProofError>
where
BuildParser: FnOnce(HashOf<BridgedChain<B>>, StorageProof) -> Result<Parser, MessageProofError>,
BuildParser: FnOnce(HashOf<BridgedChain<B>>, RawStorageProof) -> Result<Parser, MessageProofError>,
Parser: MessageProofParser,
{
let (bridged_header_hash, bridged_storage_proof, lane_id, begin, end) = proof;
let FromBridgedChainMessagesProof {
bridged_header_hash,
storage_proof,
lane,
nonces_start,
nonces_end,
} = proof;
// receiving proofs where end < begin is ok (if proof includes outbound lane state)
let messages_in_the_proof = if let Some(nonces_difference) = end.checked_sub(begin) {
let messages_in_the_proof = if let Some(nonces_difference) = nonces_end.checked_sub(nonces_start) {
// let's check that the user (relayer) has passed correct `messages_count`
// (this bounds maximal capacity of messages vec below)
let messages_in_the_proof = nonces_difference.saturating_add(1);
@@ -510,15 +568,15 @@ pub mod target {
0
};
let parser = build_parser(bridged_header_hash, bridged_storage_proof)?;
let parser = build_parser(bridged_header_hash, storage_proof)?;
// Read messages first. All messages that are claimed to be in the proof must
// be in the proof. So any error in `read_value`, or even missing value is fatal.
//
// Mind that we allow proofs with no messages if outbound lane state is proved.
let mut messages = Vec::with_capacity(messages_in_the_proof as _);
for nonce in begin..=end {
let message_key = MessageKey { lane_id, nonce };
for nonce in nonces_start..=nonces_end {
let message_key = MessageKey { lane_id: lane, nonce };
let raw_message_data = parser
.read_raw_message(&message_key)
.ok_or(MessageProofError::MissingRequiredMessage)?;
@@ -536,7 +594,7 @@ pub mod target {
lane_state: None,
messages,
};
let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane_id);
let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane);
if let Some(raw_outbound_lane_data) = raw_outbound_lane_data {
proved_lane_messages.lane_state = Some(
OutboundLaneData::decode(&mut &raw_outbound_lane_data[..])
@@ -551,7 +609,7 @@ pub mod target {
// We only support single lane messages in this schema
let mut proved_messages = ProvedMessages::new();
proved_messages.insert(lane_id, proved_lane_messages);
proved_messages.insert(lane, proved_lane_messages);
Ok(proved_messages)
}
@@ -573,7 +631,7 @@ mod tests {
const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;
/// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from BridgedChain;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
struct OnThisChainBridge;
impl MessageBridge for OnThisChainBridge {
@@ -614,7 +672,7 @@ mod tests {
}
/// Bridge that is deployed on BridgedChain and allows sending/receiving messages to/from ThisChain;
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
struct OnBridgedChainBridge;
impl MessageBridge for OnBridgedChainBridge {
@@ -1011,11 +1069,21 @@ mod tests {
1..=0
}
fn messages_proof(nonces_end: MessageNonce) -> target::FromBridgedChainMessagesProof<()> {
target::FromBridgedChainMessagesProof {
bridged_header_hash: (),
storage_proof: vec![],
lane: Default::default(),
nonces_start: 1,
nonces_end,
}
}
#[test]
fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, TestMessageProofParser>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10),
messages_proof(10),
5,
|_, _| unreachable!(),
),
@@ -1027,7 +1095,7 @@ mod tests {
fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, TestMessageProofParser>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10),
messages_proof(10),
15,
|_, _| unreachable!(),
),
@@ -1039,7 +1107,7 @@ mod tests {
fn message_proof_is_rejected_if_build_parser_fails() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, TestMessageProofParser>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10),
messages_proof(10),
10,
|_, _| Err(target::MessageProofError::Custom("test")),
),
@@ -1050,15 +1118,13 @@ mod tests {
#[test]
fn message_proof_is_rejected_if_required_message_is_missing() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10),
10,
|_, _| Ok(TestMessageProofParser {
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(10), 10, |_, _| Ok(
TestMessageProofParser {
failing: false,
messages: 1..=5,
outbound_lane_data: None,
}),
),
}
),),
Err(target::MessageProofError::MissingRequiredMessage),
);
}
@@ -1066,15 +1132,13 @@ mod tests {
#[test]
fn message_proof_is_rejected_if_message_decode_fails() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 10),
10,
|_, _| Ok(TestMessageProofParser {
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(10), 10, |_, _| Ok(
TestMessageProofParser {
failing: true,
messages: 1..=10,
outbound_lane_data: None,
}),
),
}
),),
Err(target::MessageProofError::FailedToDecodeMessage),
);
}
@@ -1082,10 +1146,8 @@ mod tests {
#[test]
fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 0),
0,
|_, _| Ok(TestMessageProofParser {
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(0), 0, |_, _| Ok(
TestMessageProofParser {
failing: true,
messages: no_messages_range(),
outbound_lane_data: Some(OutboundLaneData {
@@ -1093,8 +1155,8 @@ mod tests {
latest_received_nonce: 1,
latest_generated_nonce: 1,
}),
}),
),
}
),),
Err(target::MessageProofError::FailedToDecodeOutboundLaneState),
);
}
@@ -1102,15 +1164,13 @@ mod tests {
#[test]
fn message_proof_is_rejected_if_it_is_empty() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 0),
0,
|_, _| Ok(TestMessageProofParser {
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(0), 0, |_, _| Ok(
TestMessageProofParser {
failing: false,
messages: no_messages_range(),
outbound_lane_data: None,
}),
),
}
),),
Err(target::MessageProofError::Empty),
);
}
@@ -1118,10 +1178,8 @@ mod tests {
#[test]
fn non_empty_message_proof_without_messages_is_accepted() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 0),
0,
|_, _| Ok(TestMessageProofParser {
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(0), 0, |_, _| Ok(
TestMessageProofParser {
failing: false,
messages: no_messages_range(),
outbound_lane_data: Some(OutboundLaneData {
@@ -1129,8 +1187,8 @@ mod tests {
latest_received_nonce: 1,
latest_generated_nonce: 1,
}),
}),
),
}
),),
Ok(vec![(
Default::default(),
ProvedLaneMessages {
@@ -1150,10 +1208,8 @@ mod tests {
#[test]
fn non_empty_message_proof_is_accepted() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(
(Default::default(), StorageProof::new(vec![]), Default::default(), 1, 1),
1,
|_, _| Ok(TestMessageProofParser {
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(messages_proof(1), 1, |_, _| Ok(
TestMessageProofParser {
failing: false,
messages: 1..=1,
outbound_lane_data: Some(OutboundLaneData {
@@ -1161,8 +1217,8 @@ mod tests {
latest_received_nonce: 1,
latest_generated_nonce: 1,
}),
}),
),
}
),),
Ok(vec![(
Default::default(),
ProvedLaneMessages {
@@ -1192,13 +1248,7 @@ mod tests {
fn verify_messages_proof_with_parser_does_not_panic_if_messages_count_mismatches() {
assert_eq!(
target::verify_messages_proof_with_parser::<OnThisChainBridge, _, _>(
(
Default::default(),
StorageProof::new(vec![]),
Default::default(),
0,
u64::MAX
),
messages_proof(u64::MAX),
0,
|_, _| Ok(TestMessageProofParser {
failing: false,
@@ -32,9 +32,7 @@ use pallet_message_lane::benchmarking::{MessageDeliveryProofParams, MessageProof
use sp_core::Hasher;
use sp_runtime::traits::Header;
use sp_std::prelude::*;
use sp_trie::{
read_trie_value_with, record_all_keys, trie_types::TrieDBMut, Layout, MemoryDB, Recorder, StorageProof, TrieMut,
};
use sp_trie::{record_all_keys, trie_types::TrieDBMut, Layout, MemoryDB, Recorder, TrieMut};
/// Generate ed25519 signature to be used in `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`.
///
@@ -71,7 +69,7 @@ pub fn prepare_message_proof<B, H, R, MM, ML, MH>(
make_bridged_header: MH,
message_dispatch_weight: Weight,
message_payload: MessagePayload,
) -> (FromBridgedChainMessagesProof<B>, Weight)
) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight)
where
B: MessageBridge,
H: Hasher,
@@ -134,13 +132,13 @@ where
pallet_substrate_bridge::initialize_for_benchmarks::<R>(bridged_header);
(
(
bridged_header_hash.into(),
StorageProof::new(storage_proof),
params.lane,
*params.message_nonces.start(),
*params.message_nonces.end(),
),
FromBridgedChainMessagesProof {
bridged_header_hash: bridged_header_hash.into(),
storage_proof,
lane: params.lane,
nonces_start: *params.message_nonces.start(),
nonces_end: *params.message_nonces.end(),
},
message_dispatch_weight
.checked_mul(message_count)
.expect("too many messages requested by benchmark"),
@@ -152,7 +150,7 @@ pub fn prepare_message_delivery_proof<B, H, R, ML, MH>(
params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>,
make_bridged_inbound_lane_data_key: ML,
make_bridged_header: MH,
) -> FromBridgedChainMessagesDeliveryProof<B>
) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>
where
B: MessageBridge,
H: Hasher,
@@ -171,12 +169,13 @@ where
.map_err(|_| "TrieMut::insert has failed")
.expect("TrieMut::insert should not fail in benchmarks");
}
root = grow_trie(root, &mut mdb, params.size);
// generate storage proof to be delivered to This chain
let mut proof_recorder = Recorder::<H::Out>::new();
read_trie_value_with::<Layout<H>, _, _>(&mdb, &root, &storage_key, &mut proof_recorder)
.map_err(|_| "read_trie_value_with has failed")
.expect("read_trie_value_with should not fail in benchmarks");
record_all_keys::<Layout<H>, _>(&mdb, &root, &mut proof_recorder)
.map_err(|_| "record_all_keys has failed")
.expect("record_all_keys should not fail in benchmarks");
let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect();
// prepare Bridged chain header and insert it into the Substrate pallet
@@ -184,17 +183,17 @@ where
let bridged_header_hash = bridged_header.hash();
pallet_substrate_bridge::initialize_for_benchmarks::<R>(bridged_header);
(
bridged_header_hash.into(),
StorageProof::new(storage_proof),
params.lane,
)
FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: bridged_header_hash.into(),
storage_proof,
lane: params.lane,
}
}
/// Populate trie with dummy keys+values until trie has (approximately) at least given size.
/// Populate trie with dummy keys+values until trie has at least given size.
fn grow_trie<H: Hasher>(mut root: H::Out, mdb: &mut MemoryDB<H>, trie_size: ProofSize) -> H::Out {
let (iterations, leaf_size, minimal_trie_size) = match trie_size {
ProofSize::Minimal => return root,
ProofSize::Minimal(_) => return root,
ProofSize::HasLargeLeaf(size) => (1, size, size),
ProofSize::HasExtraNodes(size) => (8, 1, size),
};