Changed delivery and dispatch fee computation methods (#795)

* removed weight <-> fee mess

* updated documentation

Co-authored-by: Hernando Castano <castano.ha@gmail.com>
This commit is contained in:
Svyatoslav Nikolsky
2021-03-08 21:07:20 +03:00
committed by Bastian Köcher
parent f7c3bd4e08
commit 324e083cba
19 changed files with 437 additions and 354 deletions
-10
View File
@@ -667,11 +667,6 @@ mod tests {
bp_millau::max_extrinsic_size(), bp_millau::max_extrinsic_size(),
bp_millau::max_extrinsic_weight(), bp_millau::max_extrinsic_weight(),
max_incoming_message_proof_size, max_incoming_message_proof_size,
bridge_runtime_common::messages::transaction_weight_without_multiplier(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
max_incoming_message_proof_size as _,
0,
),
messages::target::maximal_incoming_message_dispatch_weight(bp_millau::max_extrinsic_weight()), messages::target::maximal_incoming_message_dispatch_weight(bp_millau::max_extrinsic_weight()),
); );
@@ -686,11 +681,6 @@ mod tests {
max_incoming_inbound_lane_data_proof_size, max_incoming_inbound_lane_data_proof_size,
bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
bridge_runtime_common::messages::transaction_weight_without_multiplier(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
max_incoming_inbound_lane_data_proof_size as _,
0,
),
); );
} }
} }
@@ -24,11 +24,11 @@ use bp_message_lane::{
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessageLaneParameter, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessageLaneParameter,
}; };
use bp_runtime::{InstanceId, RIALTO_BRIDGE_INSTANCE}; use bp_runtime::{InstanceId, RIALTO_BRIDGE_INSTANCE};
use bridge_runtime_common::messages::{self, ChainWithMessageLanes, MessageBridge}; use bridge_runtime_common::messages::{self, ChainWithMessageLanes, MessageBridge, MessageLaneTransaction};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::{ use frame_support::{
parameter_types, parameter_types,
weights::{DispatchClass, Weight, WeightToFeePolynomial}, weights::{DispatchClass, Weight},
RuntimeDebug, RuntimeDebug,
}; };
use sp_core::storage::StorageKey; use sp_core::storage::StorageKey;
@@ -99,59 +99,6 @@ impl MessageBridge for WithRialtoMessageBridge {
type ThisChain = Millau; type ThisChain = Millau;
type BridgedChain = Rialto; type BridgedChain = Rialto;
fn maximal_extrinsic_size_on_target_chain() -> u32 {
bp_rialto::max_extrinsic_size()
}
fn weight_limits_of_message_on_bridged_chain(_message_payload: &[u8]) -> RangeInclusive<Weight> {
// we don't want to relay too large messages + keep reserve for future upgrades
let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(bp_rialto::max_extrinsic_weight());
// we're charging for payload bytes in `WithRialtoMessageBridge::weight_of_delivery_transaction` function
//
// this bridge may be used to deliver all kind of messages, so we're not making any assumptions about
// minimal dispatch weight here
0..=upper_limit
}
fn weight_of_delivery_transaction(message_payload: &[u8]) -> Weight {
let message_payload_len = u32::try_from(message_payload.len())
.map(Into::into)
.unwrap_or(Weight::MAX);
let extra_bytes_in_payload =
message_payload_len.saturating_sub(pallet_message_lane::EXPECTED_DEFAULT_MESSAGE_LENGTH.into());
messages::transaction_weight_without_multiplier(
bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
message_payload_len.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE as _),
extra_bytes_in_payload
.saturating_mul(bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT)
.saturating_add(bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT),
)
}
fn weight_of_delivery_confirmation_transaction_on_this_chain() -> Weight {
let inbounded_data_size: Weight =
InboundLaneData::<bp_rialto::AccountId>::encoded_size_hint(bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, 1)
.map(Into::into)
.unwrap_or(Weight::MAX);
messages::transaction_weight_without_multiplier(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
inbounded_data_size.saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE as _),
bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
)
}
fn this_weight_to_this_balance(weight: Weight) -> bp_millau::Balance {
<crate::Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(&weight)
}
fn bridged_weight_to_bridged_balance(weight: Weight) -> bp_rialto::Balance {
// we're using the same weights in both chains now
<crate::Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(&weight) as _
}
fn bridged_balance_to_this_balance(bridged_balance: bp_rialto::Balance) -> bp_millau::Balance { fn bridged_balance_to_this_balance(bridged_balance: bp_rialto::Balance) -> bp_millau::Balance {
bp_millau::Balance::try_from(RialtoToMillauConversionRate::get().saturating_mul_int(bridged_balance)) bp_millau::Balance::try_from(RialtoToMillauConversionRate::get().saturating_mul_int(bridged_balance))
.unwrap_or(bp_millau::Balance::MAX) .unwrap_or(bp_millau::Balance::MAX)
@@ -167,7 +114,6 @@ impl messages::ChainWithMessageLanes for Millau {
type AccountId = bp_millau::AccountId; type AccountId = bp_millau::AccountId;
type Signer = bp_millau::AccountSigner; type Signer = bp_millau::AccountSigner;
type Signature = bp_millau::Signature; type Signature = bp_millau::Signature;
type Call = crate::Call;
type Weight = Weight; type Weight = Weight;
type Balance = bp_millau::Balance; type Balance = bp_millau::Balance;
@@ -175,6 +121,8 @@ impl messages::ChainWithMessageLanes for Millau {
} }
impl messages::ThisChainWithMessageLanes for Millau { impl messages::ThisChainWithMessageLanes for Millau {
type Call = crate::Call;
fn is_outbound_lane_enabled(lane: &LaneId) -> bool { fn is_outbound_lane_enabled(lane: &LaneId) -> bool {
*lane == LaneId::default() *lane == LaneId::default()
} }
@@ -182,6 +130,29 @@ impl messages::ThisChainWithMessageLanes for Millau {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MessageNonce::MAX MessageNonce::MAX
} }
fn estimate_delivery_confirmation_transaction() -> MessageLaneTransaction<Weight> {
let inbound_data_size =
InboundLaneData::<bp_millau::AccountId>::encoded_size_hint(bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, 1)
.unwrap_or(u32::MAX);
MessageLaneTransaction {
dispatch_weight: bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
size: inbound_data_size
.saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_millau::TX_EXTRA_BYTES),
}
}
fn transaction_payment(transaction: MessageLaneTransaction<Weight>) -> bp_millau::Balance {
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment_without_multiplier(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
|weight| weight as _,
transaction,
)
}
} }
/// Rialto chain from message lane point of view. /// Rialto chain from message lane point of view.
@@ -193,13 +164,59 @@ impl messages::ChainWithMessageLanes for Rialto {
type AccountId = bp_rialto::AccountId; type AccountId = bp_rialto::AccountId;
type Signer = bp_rialto::AccountSigner; type Signer = bp_rialto::AccountSigner;
type Signature = bp_rialto::Signature; type Signature = bp_rialto::Signature;
type Call = (); // unknown to us
type Weight = Weight; type Weight = Weight;
type Balance = bp_rialto::Balance; type Balance = bp_rialto::Balance;
type MessageLaneInstance = pallet_message_lane::DefaultInstance; type MessageLaneInstance = pallet_message_lane::DefaultInstance;
} }
impl messages::BridgedChainWithMessageLanes for Rialto {
fn maximal_extrinsic_size() -> u32 {
bp_rialto::max_extrinsic_size()
}
fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Weight> {
// we don't want to relay too large messages + keep reserve for future upgrades
let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(bp_rialto::max_extrinsic_weight());
// we're charging for payload bytes in `WithRialtoMessageBridge::transaction_payment` function
//
// this bridge may be used to deliver all kind of messages, so we're not making any assumptions about
// minimal dispatch weight here
0..=upper_limit
}
fn estimate_delivery_transaction(
message_payload: &[u8],
message_dispatch_weight: Weight,
) -> MessageLaneTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
let extra_bytes_in_payload = Weight::from(message_payload_len)
.saturating_sub(pallet_message_lane::EXPECTED_DEFAULT_MESSAGE_LENGTH.into());
MessageLaneTransaction {
dispatch_weight: extra_bytes_in_payload
.saturating_mul(bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT)
.saturating_add(bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_add(message_dispatch_weight),
size: message_payload_len
.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_rialto::TX_EXTRA_BYTES),
}
}
fn transaction_payment(transaction: MessageLaneTransaction<Weight>) -> bp_rialto::Balance {
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment_without_multiplier(
bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
|weight| weight as _,
transaction,
)
}
}
impl TargetHeaderChain<ToRialtoMessagePayload, bp_rialto::AccountId> for Rialto { impl TargetHeaderChain<ToRialtoMessagePayload, bp_rialto::AccountId> for Rialto {
type Error = &'static str; type Error = &'static str;
// The proof is: // The proof is:
-10
View File
@@ -1096,11 +1096,6 @@ mod tests {
bp_rialto::max_extrinsic_size(), bp_rialto::max_extrinsic_size(),
bp_rialto::max_extrinsic_weight(), bp_rialto::max_extrinsic_weight(),
max_incoming_message_proof_size, max_incoming_message_proof_size,
bridge_runtime_common::messages::transaction_weight_without_multiplier(
bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
max_incoming_message_proof_size as _,
0,
),
messages::target::maximal_incoming_message_dispatch_weight(bp_rialto::max_extrinsic_weight()), messages::target::maximal_incoming_message_dispatch_weight(bp_rialto::max_extrinsic_weight()),
); );
@@ -1115,11 +1110,6 @@ mod tests {
max_incoming_inbound_lane_data_proof_size, max_incoming_inbound_lane_data_proof_size,
bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE,
bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE,
bridge_runtime_common::messages::transaction_weight_without_multiplier(
bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
max_incoming_inbound_lane_data_proof_size as _,
0,
),
); );
} }
@@ -24,11 +24,11 @@ use bp_message_lane::{
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessageLaneParameter, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessageLaneParameter,
}; };
use bp_runtime::{InstanceId, MILLAU_BRIDGE_INSTANCE}; use bp_runtime::{InstanceId, MILLAU_BRIDGE_INSTANCE};
use bridge_runtime_common::messages::{self, ChainWithMessageLanes, MessageBridge}; use bridge_runtime_common::messages::{self, ChainWithMessageLanes, MessageBridge, MessageLaneTransaction};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use frame_support::{ use frame_support::{
parameter_types, parameter_types,
weights::{DispatchClass, Weight, WeightToFeePolynomial}, weights::{DispatchClass, Weight},
RuntimeDebug, RuntimeDebug,
}; };
use sp_core::storage::StorageKey; use sp_core::storage::StorageKey;
@@ -99,59 +99,6 @@ impl MessageBridge for WithMillauMessageBridge {
type ThisChain = Rialto; type ThisChain = Rialto;
type BridgedChain = Millau; type BridgedChain = Millau;
fn maximal_extrinsic_size_on_target_chain() -> u32 {
bp_millau::max_extrinsic_size()
}
fn weight_limits_of_message_on_bridged_chain(_message_payload: &[u8]) -> RangeInclusive<Weight> {
// we don't want to relay too large messages + keep reserve for future upgrades
let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(bp_millau::max_extrinsic_weight());
// we're charging for payload bytes in `WithMillauMessageBridge::weight_of_delivery_transaction` function
//
// this bridge may be used to deliver all kind of messages, so we're not making any assumptions about
// minimal dispatch weight here
0..=upper_limit
}
fn weight_of_delivery_transaction(message_payload: &[u8]) -> Weight {
let message_payload_len = u32::try_from(message_payload.len())
.map(Into::into)
.unwrap_or(Weight::MAX);
let extra_bytes_in_payload =
message_payload_len.saturating_sub(pallet_message_lane::EXPECTED_DEFAULT_MESSAGE_LENGTH.into());
messages::transaction_weight_without_multiplier(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
message_payload_len.saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE as _),
extra_bytes_in_payload
.saturating_mul(bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT)
.saturating_add(bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT),
)
}
fn weight_of_delivery_confirmation_transaction_on_this_chain() -> Weight {
let inbounded_data_size: Weight =
InboundLaneData::<bp_millau::AccountId>::encoded_size_hint(bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, 1)
.map(Into::into)
.unwrap_or(Weight::MAX);
messages::transaction_weight_without_multiplier(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
inbounded_data_size.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE as _),
bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
)
}
fn this_weight_to_this_balance(weight: Weight) -> bp_rialto::Balance {
<crate::Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(&weight)
}
fn bridged_weight_to_bridged_balance(weight: Weight) -> bp_millau::Balance {
// we're using the same weights in both chains now
<crate::Runtime as pallet_transaction_payment::Config>::WeightToFee::calc(&weight) as _
}
fn bridged_balance_to_this_balance(bridged_balance: bp_millau::Balance) -> bp_rialto::Balance { fn bridged_balance_to_this_balance(bridged_balance: bp_millau::Balance) -> bp_rialto::Balance {
bp_rialto::Balance::try_from(MillauToRialtoConversionRate::get().saturating_mul_int(bridged_balance)) bp_rialto::Balance::try_from(MillauToRialtoConversionRate::get().saturating_mul_int(bridged_balance))
.unwrap_or(bp_rialto::Balance::MAX) .unwrap_or(bp_rialto::Balance::MAX)
@@ -167,7 +114,6 @@ impl messages::ChainWithMessageLanes for Rialto {
type AccountId = bp_rialto::AccountId; type AccountId = bp_rialto::AccountId;
type Signer = bp_rialto::AccountSigner; type Signer = bp_rialto::AccountSigner;
type Signature = bp_rialto::Signature; type Signature = bp_rialto::Signature;
type Call = crate::Call;
type Weight = Weight; type Weight = Weight;
type Balance = bp_rialto::Balance; type Balance = bp_rialto::Balance;
@@ -175,6 +121,8 @@ impl messages::ChainWithMessageLanes for Rialto {
} }
impl messages::ThisChainWithMessageLanes for Rialto { impl messages::ThisChainWithMessageLanes for Rialto {
type Call = crate::Call;
fn is_outbound_lane_enabled(lane: &LaneId) -> bool { fn is_outbound_lane_enabled(lane: &LaneId) -> bool {
*lane == LaneId::default() *lane == LaneId::default()
} }
@@ -182,6 +130,29 @@ impl messages::ThisChainWithMessageLanes for Rialto {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MessageNonce::MAX MessageNonce::MAX
} }
fn estimate_delivery_confirmation_transaction() -> MessageLaneTransaction<Weight> {
let inbound_data_size =
InboundLaneData::<bp_rialto::AccountId>::encoded_size_hint(bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, 1)
.unwrap_or(u32::MAX);
MessageLaneTransaction {
dispatch_weight: bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT,
size: inbound_data_size
.saturating_add(bp_millau::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_rialto::TX_EXTRA_BYTES),
}
}
fn transaction_payment(transaction: MessageLaneTransaction<Weight>) -> bp_rialto::Balance {
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment_without_multiplier(
bp_rialto::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
|weight| weight as _,
transaction,
)
}
} }
/// Millau chain from message lane point of view. /// Millau chain from message lane point of view.
@@ -193,13 +164,59 @@ impl messages::ChainWithMessageLanes for Millau {
type AccountId = bp_millau::AccountId; type AccountId = bp_millau::AccountId;
type Signer = bp_millau::AccountSigner; type Signer = bp_millau::AccountSigner;
type Signature = bp_millau::Signature; type Signature = bp_millau::Signature;
type Call = (); // unknown to us
type Weight = Weight; type Weight = Weight;
type Balance = bp_millau::Balance; type Balance = bp_millau::Balance;
type MessageLaneInstance = pallet_message_lane::DefaultInstance; type MessageLaneInstance = pallet_message_lane::DefaultInstance;
} }
impl messages::BridgedChainWithMessageLanes for Millau {
fn maximal_extrinsic_size() -> u32 {
bp_millau::max_extrinsic_size()
}
fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Weight> {
// we don't want to relay too large messages + keep reserve for future upgrades
let upper_limit = messages::target::maximal_incoming_message_dispatch_weight(bp_millau::max_extrinsic_weight());
// we're charging for payload bytes in `WithMillauMessageBridge::transaction_payment` function
//
// this bridge may be used to deliver all kind of messages, so we're not making any assumptions about
// minimal dispatch weight here
0..=upper_limit
}
fn estimate_delivery_transaction(
message_payload: &[u8],
message_dispatch_weight: Weight,
) -> MessageLaneTransaction<Weight> {
let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX);
let extra_bytes_in_payload = Weight::from(message_payload_len)
.saturating_sub(pallet_message_lane::EXPECTED_DEFAULT_MESSAGE_LENGTH.into());
MessageLaneTransaction {
dispatch_weight: extra_bytes_in_payload
.saturating_mul(bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT)
.saturating_add(bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT)
.saturating_add(message_dispatch_weight),
size: message_payload_len
.saturating_add(bp_rialto::EXTRA_STORAGE_PROOF_SIZE)
.saturating_add(bp_millau::TX_EXTRA_BYTES),
}
}
fn transaction_payment(transaction: MessageLaneTransaction<Weight>) -> bp_millau::Balance {
// in our testnets, both per-byte fee and weight-to-fee are 1:1
messages::transaction_payment_without_multiplier(
bp_millau::BlockWeights::get().get(DispatchClass::Normal).base_extrinsic,
1,
|weight| weight as _,
transaction,
)
}
}
impl TargetHeaderChain<ToMillauMessagePayload, bp_millau::AccountId> for Millau { impl TargetHeaderChain<ToMillauMessagePayload, bp_millau::AccountId> for Millau {
type Error = &'static str; type Error = &'static str;
// The proof is: // The proof is:
+61 -68
View File
@@ -33,17 +33,63 @@ message lane module into your runtime. Basic prerequisites of these helpers are:
## `MessageBridge` Trait ## `MessageBridge` Trait
The essence of your integration will be a struct that implements a `MessageBridge` trait. Let's The essence of your integration will be a struct that implements a `MessageBridge` trait. It has
review every method and give some implementation hints here: single method (`MessageBridge::bridged_balance_to_this_balance`), used to convert from bridged chain
tokens into this chain tokens. The bridge also requires two associated types to be specified -
`ThisChain` and `BridgedChain`.
- `MessageBridge::maximal_extrinsic_size_on_target_chain`: you will need to return the maximal Worth to say that if you're going to use hardcoded constant (conversion rate) in the
extrinsic size of the target chain from this function. This may be the constant that is updated `MessageBridge::bridged_balance_to_this_balance` method (or in any other method of
when your runtime is upgraded, or you may use the `ThisChainWithMessageLanes` or `BridgedChainWithMessageLanes` traits), then you should take a
[message lane parameters functionality](../../modules/message-lane/README.md#Non-Essential-Functionality) look at the
to allow the pallet owner to update this value more frequently (you may also want to use this [message lane parameters functionality](../../modules/message-lane/README.md#Non-Essential-Functionality).
functionality for all constants that are used in other methods described below). They allow pallet owner to update constants more frequently than runtime upgrade happens.
- `MessageBridge::weight_limits_of_message_on_bridged_chain`: you'll need to return a range of ## `ChainWithMessageLanes` Trait
The trait is quite simple and can easily be implemented - you just need to specify types used at the
corresponding chain. There is single exception, though (it may be changed in the future):
- `ChainWithMessageLanes::MessageLaneInstance`: this is used to compute runtime storage keys. There
may be several instances of message lane pallet, included in the Runtime. Every instance stores
messages and these messages stored under different keys. When we are verifying storage proofs from
the bridged chain, we should know which instance we're talking to. This is fine, but there's
significant inconvenience with that - this chain runtime must have the same message lane pallet
instance. This does not necessarily mean that we should use the same instance on both chains -
this instance may be used to bridge with another chain/instance, or may not be used at all.
## `ThisChainWithMessageLanes` Trait
This trait represents this chain from bridge point of view. Let's review every method of this trait:
- `ThisChainWithMessageLanes::is_outbound_lane_enabled`: is used to check whether given lane accepts
outbound messages.
- `ThisChainWithMessageLanes::maximal_pending_messages_at_outbound_lane`: you should return maximal
number of pending (undelivered) messages from this function. Returning small values would require
relayers to operate faster and could make message sending logic more complicated. On the other
hand, returning large values could lead to chain state growth.
- `ThisChainWithMessageLanes::estimate_delivery_confirmation_transaction`: you'll need to return
estimated size and dispatch weight of the delivery confirmation transaction (that happens on
this chain) from this function.
- `ThisChainWithMessageLanes::transaction_payment`: you'll need to return fee that the submitter
must pay for given transaction on this chain. Normally, you would use transaction payment pallet
for this. However, if your chain has non-zero fee multiplier set, this would mean that the
payment will be computed using current value of this multiplier. But since this transaction
will be submitted in the future, you may want to choose other value instead. Otherwise,
non-altruistic relayer may choose not to submit this transaction until number of transactions
will decrease.
## `BridgedChainWithMessageLanes` Trait
This trait represents this chain from bridge point of view. Let's review every method of this trait:
- `BridgedChainWithMessageLanes::maximal_extrinsic_size`: you will need to return the maximal
extrinsic size of the target chain from this function.
- `MessageBridge::message_weight_limits`: you'll need to return a range of
dispatch weights that the outbound message may take at the target chain. Please keep in mind that dispatch weights that the outbound message may take at the target chain. Please keep in mind that
our helpers assume that the message is an encoded call of the target chain. But we never decode our helpers assume that the message is an encoded call of the target chain. But we never decode
this call at the source chain. So you can't simply get dispatch weight from pre-dispatch this call at the source chain. So you can't simply get dispatch weight from pre-dispatch
@@ -55,66 +101,13 @@ review every method and give some implementation hints here:
maximal weight of extrinsic at the target chain. In our test chains, we reject all messages that maximal weight of extrinsic at the target chain. In our test chains, we reject all messages that
have declared dispatch weight larger than 50% of the maximal bridged extrinsic weight. have declared dispatch weight larger than 50% of the maximal bridged extrinsic weight.
- `MessageBridge::weight_of_delivery_transaction`: you will need to return the maximal weight of the - `MessageBridge::estimate_delivery_transaction`: you will need to return estimated dispatch weight and
delivery transaction that delivers a given message to the target chain. There are three main size of the delivery transaction that delivers a given message to the target chain.
things to notice:
1. weight, returned from this function is then used to compute the fee that the - `MessageBridge::transaction_payment`: you'll need to return fee that the submitter
message sender needs to pay for the delivery transaction. So it shall not be a simple dispatch must pay for given transaction on bridged chain. The best case is when you have the same conversion
weight of delivery call - it should be the "weight" of the transaction itself, including per-byte formula on both chains - then you may just reuse the `ThisChainWithMessageLanes::transaction_payment`
"weight", "weight" of signed extras and etc. implementation. Otherwise, you'll need to hardcode this formula into your runtime.
1. the delivery transaction brings storage proof of
the message, not the message itself. So your transaction will include extra bytes. We suggest
computing the size of single empty value storage proof at the source chain, increase this value a
bit and hardcode it in the source chain runtime code. This size then must be added to the size of
payload and included in the weight computation;
1. before implementing this function, please take
a look at the
[weight formula of delivery transaction](../../modules/message-lane/README.md#Weight-of-receive_messages_proof-call).
It adds some extra weight for every additional byte of the proof (everything above
`pallet_message_lane::EXPECTED_DEFAULT_MESSAGE_LENGTH`), so it's not trivial. Even better, please
refer to [our implementation](../millau/runtime/src/rialto_messages.rs) for test chains for
details.
- `MessageBridge::weight_of_delivery_confirmation_transaction_on_this_chain`: you'll need to return
the maximal weight of a single message delivery confirmation transaction on this chain. All points
from the previous paragraph are also relevant here.
- `MessageBridge::this_weight_to_this_balance`: this function needs to convert weight units into fee
units on this chain. Most probably this can be done by calling
`pallet_transaction_payment::Config::WeightToFee::calc()` for passed weight.
- `MessageBridge::bridged_weight_to_bridged_balance`: this function needs to convert weight units
into fee units on the target chain. The best case is when you have the same conversion formula on
both chains - then you may just call the same formula from the previous paragraph. Otherwise,
you'll need to hardcode this formula into your runtime.
- `MessageBridge::bridged_balance_to_this_balance`: this may be the easiest method to implement and
the hardest to maintain at the same time. If you don't have any automatic methods to determine
conversion rate, then you'll probably need to maintain it by yourself (by updating conversion
rate, stored in runtime storage). This means that if you're too late with an update, then you risk
to accept messages with lower-than-expected fee. So it may be wise to have some reserve in this
conversion rate, even if that means larger delivery and dispatch fees.
## `ChainWithMessageLanes` Trait
Apart from its methods, `MessageBridge` also has two associated types that are implementing the
`ChainWithMessageLanes` trait. One is for this chain and the other is for the bridged chain. The
trait is quite simple and can easily be implemented - you just need to specify types used at the
corresponding chain. There are two exceptions, though. Both may be changed in the future. Here they
are:
- `ChainWithMessageLanes::Call`: it isn't a good idea to reference bridged chain runtime from your
runtime (cyclic references + maintaining on upgrades). So you can't know the type of bridged chain
call in your runtime. This type isn't actually used at this chain, so you may use `()` instead.
- `ChainWithMessageLanes::MessageLaneInstance`: this is used to compute runtime storage keys. There
may be several instances of message lane pallet, included in the Runtime. Every instance stores
messages and these messages stored under different keys. When we are verifying storage proofs from
the bridged chain, we should know which instance we're talking to. This is fine, but there's
significant inconvenience with that - this chain runtime must have the same message lane pallet
instance. This does not necessarily mean that we should use the same instance on both chains -
this instance may be used to bridge with another chain/instance, or may not be used at all.
## Helpers for the Source Chain ## Helpers for the Source Chain
+144 -113
View File
@@ -31,7 +31,7 @@ use codec::{Decode, Encode};
use frame_support::{traits::Instance, weights::Weight, RuntimeDebug}; use frame_support::{traits::Instance, weights::Weight, RuntimeDebug};
use hash_db::Hasher; use hash_db::Hasher;
use pallet_substrate_bridge::StorageProofChecker; use pallet_substrate_bridge::StorageProofChecker;
use sp_runtime::traits::{CheckedAdd, CheckedDiv, CheckedMul}; use sp_runtime::traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedDiv, CheckedMul};
use sp_std::{cmp::PartialOrd, convert::TryFrom, fmt::Debug, 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; use sp_trie::StorageProof;
@@ -46,37 +46,9 @@ pub trait MessageBridge {
/// This chain in context of message bridge. /// This chain in context of message bridge.
type ThisChain: ThisChainWithMessageLanes; type ThisChain: ThisChainWithMessageLanes;
/// Bridged chain in context of message bridge. /// Bridged chain in context of message bridge.
type BridgedChain: ChainWithMessageLanes; type BridgedChain: BridgedChainWithMessageLanes;
/// Maximal extrinsic size on target chain. /// Convert Bridged chain balance into This chain balance.
fn maximal_extrinsic_size_on_target_chain() -> u32;
/// Returns feasible weights range for given message payload on the target chain.
///
/// If message is being sent with the weight that is out of this range, then it
/// should be rejected.
///
/// Weights returned from this function shall not include transaction overhead
/// (like weight of signature and signed extensions verification), because they're
/// already accounted by the `weight_of_delivery_transaction`. So this function should
/// return pure call dispatch weights range.
fn weight_limits_of_message_on_bridged_chain(
message_payload: &[u8],
) -> RangeInclusive<WeightOf<BridgedChain<Self>>>;
/// Maximal weight of single message delivery transaction on Bridged chain.
fn weight_of_delivery_transaction(message_payload: &[u8]) -> WeightOf<BridgedChain<Self>>;
/// Maximal weight of single message delivery confirmation transaction on This chain.
fn weight_of_delivery_confirmation_transaction_on_this_chain() -> WeightOf<ThisChain<Self>>;
/// Convert weight of This chain to the fee (paid in Balance) of This chain.
fn this_weight_to_this_balance(weight: WeightOf<ThisChain<Self>>) -> BalanceOf<ThisChain<Self>>;
/// Convert weight of the Bridged chain to the fee (paid in Balance) of the Bridged chain.
fn bridged_weight_to_bridged_balance(weight: WeightOf<BridgedChain<Self>>) -> BalanceOf<BridgedChain<Self>>;
/// Convert Bridged chain Balance into This chain Balance.
fn bridged_balance_to_this_balance(bridged_balance: BalanceOf<BridgedChain<Self>>) -> BalanceOf<ThisChain<Self>>; fn bridged_balance_to_this_balance(bridged_balance: BalanceOf<BridgedChain<Self>>) -> BalanceOf<ThisChain<Self>>;
} }
@@ -90,8 +62,6 @@ pub trait ChainWithMessageLanes {
type Signer: Decode; type Signer: Decode;
/// Signature type used on the chain. /// Signature type used on the chain.
type Signature: Decode; type Signature: Decode;
/// Call type on the chain.
type Call: Encode + Decode;
/// Type of weight that is used on the chain. This would almost always be a regular /// Type of weight that is used on the chain. This would almost always be a regular
/// `frame_support::weight::Weight`. But since the meaning of weight on different chains /// `frame_support::weight::Weight`. But since the meaning of weight on different chains
/// may be different, the `WeightOf<>` construct is used to avoid confusion between /// may be different, the `WeightOf<>` construct is used to avoid confusion between
@@ -104,15 +74,59 @@ pub trait ChainWithMessageLanes {
type MessageLaneInstance: Instance; type MessageLaneInstance: Instance;
} }
/// Message-lane related transaction parameters estimation.
#[derive(RuntimeDebug)]
pub struct MessageLaneTransaction<Weight> {
/// The estimated dispatch weight of the transaction.
pub dispatch_weight: Weight,
/// The estimated size of the encoded transaction.
pub size: u32,
}
/// This chain that has `message-lane` and `call-dispatch` modules. /// This chain that has `message-lane` and `call-dispatch` modules.
pub trait ThisChainWithMessageLanes: ChainWithMessageLanes { pub trait ThisChainWithMessageLanes: ChainWithMessageLanes {
/// Call type on the chain.
type Call: Encode + Decode;
/// Are we accepting any messages to the given lane? /// Are we accepting any messages to the given lane?
fn is_outbound_lane_enabled(lane: &LaneId) -> bool; fn is_outbound_lane_enabled(lane: &LaneId) -> bool;
/// Maximal number of pending (not yet delivered) messages at this chain. /// Maximal number of pending (not yet delivered) messages at This chain.
/// ///
/// Any messages over this limit, will be rejected. /// Any messages over this limit, will be rejected.
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce; fn maximal_pending_messages_at_outbound_lane() -> MessageNonce;
/// Estimate size and weight of single message delivery confirmation transaction at This chain.
fn estimate_delivery_confirmation_transaction() -> MessageLaneTransaction<WeightOf<Self>>;
/// Returns minimal transaction fee that must be paid for given transaction at This chain.
fn transaction_payment(transaction: MessageLaneTransaction<WeightOf<Self>>) -> BalanceOf<Self>;
}
/// Bridged chain that has `message-lane` and `call-dispatch` modules.
pub trait BridgedChainWithMessageLanes: ChainWithMessageLanes {
/// Maximal extrinsic size at Bridged chain.
fn maximal_extrinsic_size() -> u32;
/// Returns feasible weights range for given message payload at the Bridged chain.
///
/// If message is being sent with the weight that is out of this range, then it
/// should be rejected.
///
/// Weights returned from this function shall not include transaction overhead
/// (like weight of signature and signed extensions verification), because they're
/// already accounted by the `weight_of_delivery_transaction`. So this function should
/// return pure call dispatch weights range.
fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive<Self::Weight>;
/// Estimate size and weight of single message delivery transaction at the Bridged chain.
fn estimate_delivery_transaction(
message_payload: &[u8],
message_dispatch_weight: WeightOf<Self>,
) -> MessageLaneTransaction<WeightOf<Self>>;
/// Returns minimal transaction fee that must be paid for given transaction at the Bridged chain.
fn transaction_payment(transaction: MessageLaneTransaction<WeightOf<Self>>) -> BalanceOf<Self>;
} }
pub(crate) type ThisChain<B> = <B as MessageBridge>::ThisChain; pub(crate) type ThisChain<B> = <B as MessageBridge>::ThisChain;
@@ -123,35 +137,35 @@ pub(crate) type SignerOf<C> = <C as ChainWithMessageLanes>::Signer;
pub(crate) type SignatureOf<C> = <C as ChainWithMessageLanes>::Signature; pub(crate) type SignatureOf<C> = <C as ChainWithMessageLanes>::Signature;
pub(crate) type WeightOf<C> = <C as ChainWithMessageLanes>::Weight; pub(crate) type WeightOf<C> = <C as ChainWithMessageLanes>::Weight;
pub(crate) type BalanceOf<C> = <C as ChainWithMessageLanes>::Balance; 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; pub(crate) type MessageLaneInstanceOf<C> = <C as ChainWithMessageLanes>::MessageLaneInstance;
pub(crate) type CallOf<C> = <C as ThisChainWithMessageLanes>::Call;
/// Raw storage proof type (just raw trie nodes). /// Raw storage proof type (just raw trie nodes).
type RawStorageProof = Vec<Vec<u8>>; type RawStorageProof = Vec<Vec<u8>>;
/// Compute weight of transaction at runtime where: /// Compute fee of transaction at runtime where:
/// ///
/// - transaction payment pallet is being used; /// - transaction payment pallet is being used;
/// - fee multiplier is zero. /// - fee multiplier is zero.
pub fn transaction_weight_without_multiplier( pub fn transaction_payment_without_multiplier<Balance: AtLeast32BitUnsigned>(
base_weight: Weight, base_extrinsic_weight: Weight,
payload_size: Weight, per_byte_fee: Balance,
dispatch_weight: Weight, weight_to_fee: impl Fn(Weight) -> Balance,
) -> Weight { transaction: MessageLaneTransaction<Weight>,
// non-adjustable per-byte weight is mapped 1:1 to tx weight ) -> Balance {
let per_byte_weight = payload_size; // base fee is charged for every tx
let base_fee = weight_to_fee(base_extrinsic_weight);
// we assume that adjustable per-byte weight is always zero // non-adjustable per-byte fee
let adjusted_per_byte_weight = 0; let len_fee = per_byte_fee.saturating_mul(Balance::from(transaction.size));
// we assume that transaction tip we use is also zero // the adjustable part of the fee
let transaction_tip_weight = 0; //
// here we assume that the fee multiplier is zero, so this part is also always zero
let adjusted_weight_fee = Balance::zero();
base_weight base_fee.saturating_add(len_fee).saturating_add(adjusted_weight_fee)
.saturating_add(per_byte_weight)
.saturating_add(adjusted_per_byte_weight)
.saturating_add(transaction_tip_weight)
.saturating_add(dispatch_weight)
} }
/// Sub-module that is declaring types required for processing This -> Bridged chain messages. /// Sub-module that is declaring types required for processing This -> Bridged chain messages.
@@ -266,7 +280,7 @@ pub mod source {
/// Return maximal message size of This -> Bridged chain message. /// Return maximal message size of This -> Bridged chain message.
pub fn maximal_message_size<B: MessageBridge>() -> u32 { pub fn maximal_message_size<B: MessageBridge>() -> u32 {
super::target::maximal_incoming_message_size(B::maximal_extrinsic_size_on_target_chain()) super::target::maximal_incoming_message_size(BridgedChain::<B>::maximal_extrinsic_size())
} }
/// Do basic Bridged-chain specific verification of This -> Bridged chain message. /// Do basic Bridged-chain specific verification of This -> Bridged chain message.
@@ -277,7 +291,7 @@ pub mod source {
pub fn verify_chain_message<B: MessageBridge>( pub fn verify_chain_message<B: MessageBridge>(
payload: &FromThisChainMessagePayload<B>, payload: &FromThisChainMessagePayload<B>,
) -> Result<(), &'static str> { ) -> Result<(), &'static str> {
let weight_limits = B::weight_limits_of_message_on_bridged_chain(&payload.call); let weight_limits = BridgedChain::<B>::message_weight_limits(&payload.call);
if !weight_limits.contains(&payload.weight.into()) { if !weight_limits.contains(&payload.weight.into()) {
return Err("Incorrect message weight declared"); return Err("Incorrect message weight declared");
} }
@@ -308,18 +322,17 @@ pub mod source {
relayer_fee_percent: u32, relayer_fee_percent: u32,
) -> Result<BalanceOf<ThisChain<B>>, &'static str> { ) -> Result<BalanceOf<ThisChain<B>>, &'static str> {
// the fee (in Bridged tokens) of all transactions that are made on the Bridged chain // the fee (in Bridged tokens) of all transactions that are made on the Bridged chain
let delivery_fee = B::bridged_weight_to_bridged_balance(B::weight_of_delivery_transaction(&payload.call)); let delivery_transaction =
let dispatch_fee = B::bridged_weight_to_bridged_balance(payload.weight.into()); BridgedChain::<B>::estimate_delivery_transaction(&payload.call, payload.weight.into());
let delivery_transaction_fee = BridgedChain::<B>::transaction_payment(delivery_transaction);
// the fee (in This tokens) of all transactions that are made on This chain // the fee (in This tokens) of all transactions that are made on This chain
let delivery_confirmation_fee = let confirmation_transaction = ThisChain::<B>::estimate_delivery_confirmation_transaction();
B::this_weight_to_this_balance(B::weight_of_delivery_confirmation_transaction_on_this_chain()); let confirmation_transaction_fee = ThisChain::<B>::transaction_payment(confirmation_transaction);
// minimal fee (in This tokens) is a sum of all required fees // minimal fee (in This tokens) is a sum of all required fees
let minimal_fee = delivery_fee let minimal_fee =
.checked_add(&dispatch_fee) B::bridged_balance_to_this_balance(delivery_transaction_fee).checked_add(&confirmation_transaction_fee);
.map(B::bridged_balance_to_this_balance)
.and_then(|fee| fee.checked_add(&delivery_confirmation_fee));
// before returning, add extra fee that is paid to the relayer (relayer interest) // before returning, add extra fee that is paid to the relayer (relayer interest)
minimal_fee minimal_fee
@@ -681,31 +694,6 @@ mod tests {
type ThisChain = ThisChain; type ThisChain = ThisChain;
type BridgedChain = BridgedChain; type BridgedChain = BridgedChain;
fn maximal_extrinsic_size_on_target_chain() -> u32 {
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
}
fn weight_limits_of_message_on_bridged_chain(message_payload: &[u8]) -> RangeInclusive<Weight> {
let begin = std::cmp::min(BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, message_payload.len() as Weight);
begin..=BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT
}
fn weight_of_delivery_transaction(_message_payload: &[u8]) -> Weight {
DELIVERY_TRANSACTION_WEIGHT
}
fn weight_of_delivery_confirmation_transaction_on_this_chain() -> Weight {
DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT
}
fn this_weight_to_this_balance(weight: Weight) -> ThisChainBalance {
ThisChainBalance(weight as u32 * THIS_CHAIN_WEIGHT_TO_BALANCE_RATE as u32)
}
fn bridged_weight_to_bridged_balance(weight: Weight) -> BridgedChainBalance {
BridgedChainBalance(weight as u32 * BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE as u32)
}
fn bridged_balance_to_this_balance(bridged_balance: BridgedChainBalance) -> ThisChainBalance { fn bridged_balance_to_this_balance(bridged_balance: BridgedChainBalance) -> ThisChainBalance {
ThisChainBalance(bridged_balance.0 * BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE as u32) ThisChainBalance(bridged_balance.0 * BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE as u32)
} }
@@ -722,30 +710,6 @@ mod tests {
type ThisChain = BridgedChain; type ThisChain = BridgedChain;
type BridgedChain = ThisChain; type BridgedChain = ThisChain;
fn maximal_extrinsic_size_on_target_chain() -> u32 {
unreachable!()
}
fn weight_limits_of_message_on_bridged_chain(_message_payload: &[u8]) -> RangeInclusive<Weight> {
unreachable!()
}
fn weight_of_delivery_transaction(_message_payload: &[u8]) -> Weight {
unreachable!()
}
fn weight_of_delivery_confirmation_transaction_on_this_chain() -> Weight {
unreachable!()
}
fn this_weight_to_this_balance(_weight: Weight) -> BridgedChainBalance {
unreachable!()
}
fn bridged_weight_to_bridged_balance(_weight: Weight) -> ThisChainBalance {
unreachable!()
}
fn bridged_balance_to_this_balance(_this_balance: ThisChainBalance) -> BridgedChainBalance { fn bridged_balance_to_this_balance(_this_balance: ThisChainBalance) -> BridgedChainBalance {
unreachable!() unreachable!()
} }
@@ -845,7 +809,6 @@ mod tests {
type AccountId = ThisChainAccountId; type AccountId = ThisChainAccountId;
type Signer = ThisChainSigner; type Signer = ThisChainSigner;
type Signature = ThisChainSignature; type Signature = ThisChainSignature;
type Call = ThisChainCall;
type Weight = frame_support::weights::Weight; type Weight = frame_support::weights::Weight;
type Balance = ThisChainBalance; type Balance = ThisChainBalance;
@@ -853,6 +816,8 @@ mod tests {
} }
impl ThisChainWithMessageLanes for ThisChain { impl ThisChainWithMessageLanes for ThisChain {
type Call = ThisChainCall;
fn is_outbound_lane_enabled(lane: &LaneId) -> bool { fn is_outbound_lane_enabled(lane: &LaneId) -> bool {
lane == TEST_LANE_ID lane == TEST_LANE_ID
} }
@@ -860,6 +825,38 @@ mod tests {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE
} }
fn estimate_delivery_confirmation_transaction() -> MessageLaneTransaction<WeightOf<Self>> {
MessageLaneTransaction {
dispatch_weight: DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT,
size: 0,
}
}
fn transaction_payment(transaction: MessageLaneTransaction<WeightOf<Self>>) -> BalanceOf<Self> {
ThisChainBalance(transaction.dispatch_weight as u32 * THIS_CHAIN_WEIGHT_TO_BALANCE_RATE as u32)
}
}
impl BridgedChainWithMessageLanes for ThisChain {
fn maximal_extrinsic_size() -> u32 {
unreachable!()
}
fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Self::Weight> {
unreachable!()
}
fn estimate_delivery_transaction(
_message_payload: &[u8],
_message_dispatch_weight: WeightOf<Self>,
) -> MessageLaneTransaction<WeightOf<Self>> {
unreachable!()
}
fn transaction_payment(_transaction: MessageLaneTransaction<WeightOf<Self>>) -> BalanceOf<Self> {
unreachable!()
}
} }
struct BridgedChain; struct BridgedChain;
@@ -869,7 +866,6 @@ mod tests {
type AccountId = BridgedChainAccountId; type AccountId = BridgedChainAccountId;
type Signer = BridgedChainSigner; type Signer = BridgedChainSigner;
type Signature = BridgedChainSignature; type Signature = BridgedChainSignature;
type Call = BridgedChainCall;
type Weight = frame_support::weights::Weight; type Weight = frame_support::weights::Weight;
type Balance = BridgedChainBalance; type Balance = BridgedChainBalance;
@@ -877,6 +873,8 @@ mod tests {
} }
impl ThisChainWithMessageLanes for BridgedChain { impl ThisChainWithMessageLanes for BridgedChain {
type Call = BridgedChainCall;
fn is_outbound_lane_enabled(_lane: &LaneId) -> bool { fn is_outbound_lane_enabled(_lane: &LaneId) -> bool {
unreachable!() unreachable!()
} }
@@ -884,6 +882,39 @@ mod tests {
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
unreachable!() unreachable!()
} }
fn estimate_delivery_confirmation_transaction() -> MessageLaneTransaction<WeightOf<Self>> {
unreachable!()
}
fn transaction_payment(_transaction: MessageLaneTransaction<WeightOf<Self>>) -> BalanceOf<Self> {
unreachable!()
}
}
impl BridgedChainWithMessageLanes for BridgedChain {
fn maximal_extrinsic_size() -> u32 {
BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE
}
fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive<Self::Weight> {
let begin = std::cmp::min(BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, message_payload.len() as Weight);
begin..=BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT
}
fn estimate_delivery_transaction(
_message_payload: &[u8],
message_dispatch_weight: WeightOf<Self>,
) -> MessageLaneTransaction<WeightOf<Self>> {
MessageLaneTransaction {
dispatch_weight: DELIVERY_TRANSACTION_WEIGHT + message_dispatch_weight,
size: 0,
}
}
fn transaction_payment(transaction: MessageLaneTransaction<WeightOf<Self>>) -> BalanceOf<Self> {
BridgedChainBalance(transaction.dispatch_weight as u32 * BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE as u32)
}
} }
fn test_lane_outbound_data() -> OutboundLaneData { fn test_lane_outbound_data() -> OutboundLaneData {
@@ -95,9 +95,6 @@ pub fn ensure_able_to_receive_message<W: WeightInfoExt>(
max_extrinsic_size: u32, max_extrinsic_size: u32,
max_extrinsic_weight: Weight, max_extrinsic_weight: Weight,
max_incoming_message_proof_size: u32, max_incoming_message_proof_size: u32,
// This is a base weight (which includes cost of tx itself, per-byte cost, adjusted per-byte cost) of single
// message delivery transaction that brings `max_incoming_message_proof_size` proof.
max_incoming_message_proof_base_weight: Weight,
max_incoming_message_dispatch_weight: Weight, max_incoming_message_dispatch_weight: Weight,
) { ) {
// verify that we're able to receive proof of maximal-size message // verify that we're able to receive proof of maximal-size message
@@ -116,12 +113,9 @@ pub fn ensure_able_to_receive_message<W: WeightInfoExt>(
1, 1,
max_incoming_message_dispatch_weight, max_incoming_message_dispatch_weight,
); );
let max_delivery_transaction_weight =
max_incoming_message_proof_base_weight.saturating_add(max_delivery_transaction_dispatch_weight);
assert!( assert!(
max_delivery_transaction_weight <= max_extrinsic_weight, max_delivery_transaction_dispatch_weight <= max_extrinsic_weight,
"Weight of maximal message delivery transaction {} + {} is larger than maximal possible transaction weight {}", "Weight of maximal message delivery transaction + {} is larger than maximal possible transaction weight {}",
max_delivery_transaction_weight,
max_delivery_transaction_dispatch_weight, max_delivery_transaction_dispatch_weight,
max_extrinsic_weight, max_extrinsic_weight,
); );
@@ -134,9 +128,6 @@ pub fn ensure_able_to_receive_confirmation<W: WeightInfoExt>(
max_inbound_lane_data_proof_size_from_peer_chain: u32, max_inbound_lane_data_proof_size_from_peer_chain: u32,
max_unrewarded_relayer_entries_at_peer_inbound_lane: MessageNonce, max_unrewarded_relayer_entries_at_peer_inbound_lane: MessageNonce,
max_unconfirmed_messages_at_inbound_lane: MessageNonce, max_unconfirmed_messages_at_inbound_lane: MessageNonce,
// This is a base weight (which includes cost of tx itself, per-byte cost, adjusted per-byte cost) of single
// confirmation transaction that brings `max_inbound_lane_data_proof_size_from_peer_chain` proof.
max_incoming_delivery_proof_base_weight: Weight,
) { ) {
// verify that we're able to receive confirmation of maximal-size // verify that we're able to receive confirmation of maximal-size
let max_confirmation_transaction_size = let max_confirmation_transaction_size =
@@ -158,12 +149,9 @@ pub fn ensure_able_to_receive_confirmation<W: WeightInfoExt>(
..Default::default() ..Default::default()
}, },
); );
let max_confirmation_transaction_weight =
max_incoming_delivery_proof_base_weight.saturating_add(max_confirmation_transaction_dispatch_weight);
assert!( assert!(
max_confirmation_transaction_weight <= max_extrinsic_weight, max_confirmation_transaction_dispatch_weight <= max_extrinsic_weight,
"Weight of maximal confirmation transaction {} + {} is larger than maximal possible transaction weight {}", "Weight of maximal confirmation transaction {} is larger than maximal possible transaction weight {}",
max_incoming_delivery_proof_base_weight,
max_confirmation_transaction_dispatch_weight, max_confirmation_transaction_dispatch_weight,
max_extrinsic_weight, max_extrinsic_weight,
); );
+5
View File
@@ -48,6 +48,11 @@ pub use millau_hash::MillauHash;
/// Some reserve is reserved to account future chain growth. /// Some reserve is reserved to account future chain growth.
pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024;
/// Number of bytes, included in the signed Millau transaction apart from the encoded call itself.
///
/// Can be computed by subtracting encoded call size from raw transaction size.
pub const TX_EXTRA_BYTES: u32 = 103;
/// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id. /// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id.
pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32; pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32;
+5
View File
@@ -39,6 +39,11 @@ use sp_std::prelude::*;
/// Some reserve is reserved to account future chain growth. /// Some reserve is reserved to account future chain growth.
pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024;
/// Number of bytes, included in the signed Rialto transaction apart from the encoded call itself.
///
/// Can be computed by subtracting encoded call size from raw transaction size.
pub const TX_EXTRA_BYTES: u32 = 103;
/// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id. /// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id.
pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32; pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32;
+2 -2
View File
@@ -160,7 +160,7 @@ impl SubmitEthereumHeaders for SubstrateClient<Rialto> {
let nonce = self.next_account_index(account_id).await?; let nonce = self.next_account_index(account_id).await?;
let call = instance.build_signed_header_call(headers); let call = instance.build_signed_header_call(headers);
let transaction = Rialto::sign_transaction(self, &params.signer, nonce, call); let transaction = Rialto::sign_transaction(*self.genesis_hash(), &params.signer, nonce, call);
let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?;
Ok(()) Ok(())
@@ -256,7 +256,7 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateClient<Rialto> {
let nonce = self.next_account_index(account_id).await?; let nonce = self.next_account_index(account_id).await?;
let call = instance.build_currency_exchange_call(proof); let call = instance.build_currency_exchange_call(proof);
let transaction = Rialto::sign_transaction(self, &params.signer, nonce, call); let transaction = Rialto::sign_transaction(*self.genesis_hash(), &params.signer, nonce, call);
let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?;
Ok(()) Ok(())
+4 -4
View File
@@ -17,7 +17,7 @@
//! Types used to connect to the Millau-Substrate chain. //! Types used to connect to the Millau-Substrate chain.
use codec::Encode; use codec::Encode;
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, Client, TransactionSignScheme}; use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
use sp_core::{storage::StorageKey, Pair}; use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
use std::time::Duration; use std::time::Duration;
@@ -65,7 +65,7 @@ impl TransactionSignScheme for Millau {
type SignedTransaction = millau_runtime::UncheckedExtrinsic; type SignedTransaction = millau_runtime::UncheckedExtrinsic;
fn sign_transaction( fn sign_transaction(
client: &Client<Self>, genesis_hash: <Self::Chain as ChainBase>::Hash,
signer: &Self::AccountKeyPair, signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index, signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call, call: <Self::Chain as Chain>::Call,
@@ -84,8 +84,8 @@ impl TransactionSignScheme for Millau {
( (
millau_runtime::VERSION.spec_version, millau_runtime::VERSION.spec_version,
millau_runtime::VERSION.transaction_version, millau_runtime::VERSION.transaction_version,
*client.genesis_hash(), genesis_hash,
*client.genesis_hash(), genesis_hash,
(), (),
(), (),
(), (),
+4 -4
View File
@@ -17,7 +17,7 @@
//! Types used to connect to the Rialto-Substrate chain. //! Types used to connect to the Rialto-Substrate chain.
use codec::Encode; use codec::Encode;
use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, Client, TransactionSignScheme}; use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, TransactionSignScheme};
use sp_core::{storage::StorageKey, Pair}; use sp_core::{storage::StorageKey, Pair};
use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount};
use std::time::Duration; use std::time::Duration;
@@ -65,7 +65,7 @@ impl TransactionSignScheme for Rialto {
type SignedTransaction = rialto_runtime::UncheckedExtrinsic; type SignedTransaction = rialto_runtime::UncheckedExtrinsic;
fn sign_transaction( fn sign_transaction(
client: &Client<Self>, genesis_hash: <Self::Chain as ChainBase>::Hash,
signer: &Self::AccountKeyPair, signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index, signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call, call: <Self::Chain as Chain>::Call,
@@ -84,8 +84,8 @@ impl TransactionSignScheme for Rialto {
( (
rialto_runtime::VERSION.spec_version, rialto_runtime::VERSION.spec_version,
rialto_runtime::VERSION.transaction_version, rialto_runtime::VERSION.transaction_version,
*client.genesis_hash(), genesis_hash,
*client.genesis_hash(), genesis_hash,
(), (),
(), (),
(), (),
+1 -3
View File
@@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License // 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/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::client::Client;
use bp_runtime::Chain as ChainBase; use bp_runtime::Chain as ChainBase;
use frame_support::Parameter; use frame_support::Parameter;
use jsonrpsee_types::jsonrpc::{DeserializeOwned, Serialize}; use jsonrpsee_types::jsonrpc::{DeserializeOwned, Serialize};
@@ -87,7 +85,7 @@ pub trait TransactionSignScheme {
/// Create transaction for given runtime call, signed by given account. /// Create transaction for given runtime call, signed by given account.
fn sign_transaction( fn sign_transaction(
client: &Client<Self::Chain>, genesis_hash: <Self::Chain as ChainBase>::Hash,
signer: &Self::AccountKeyPair, signer: &Self::AccountKeyPair,
signer_nonce: <Self::Chain as Chain>::Index, signer_nonce: <Self::Chain as Chain>::Index,
call: <Self::Chain as Chain>::Call, call: <Self::Chain as Chain>::Call,
+3
View File
@@ -48,3 +48,6 @@ sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "maste
sp-finality-grandpa = { git = "https://github.com/paritytech/substrate.git", branch = "master" } sp-finality-grandpa = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
sp-trie = { git = "https://github.com/paritytech/substrate.git", branch = "master" } sp-trie = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
[dev-dependencies]
sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "master" }
+40 -4
View File
@@ -96,7 +96,7 @@ async fn run_init_bridge(command: cli::InitBridge) -> Result<(), String> {
move |initialization_data| { move |initialization_data| {
Ok(Bytes( Ok(Bytes(
Rialto::sign_transaction( Rialto::sign_transaction(
&rialto_client, *rialto_client.genesis_hash(),
&rialto_sign.signer, &rialto_sign.signer,
rialto_signer_next_index, rialto_signer_next_index,
rialto_runtime::SudoCall::sudo(Box::new( rialto_runtime::SudoCall::sudo(Box::new(
@@ -132,7 +132,7 @@ async fn run_init_bridge(command: cli::InitBridge) -> Result<(), String> {
move |initialization_data| { move |initialization_data| {
Ok(Bytes( Ok(Bytes(
Millau::sign_transaction( Millau::sign_transaction(
&millau_client, *millau_client.genesis_hash(),
&millau_sign.signer, &millau_sign.signer,
millau_signer_next_index, millau_signer_next_index,
millau_runtime::SudoCall::sudo(Box::new( millau_runtime::SudoCall::sudo(Box::new(
@@ -266,7 +266,7 @@ async fn run_send_message(command: cli::SendMessage) -> Result<(), String> {
); );
let signed_millau_call = Millau::sign_transaction( let signed_millau_call = Millau::sign_transaction(
&millau_client, *millau_client.genesis_hash(),
&millau_sign.signer, &millau_sign.signer,
millau_client millau_client
.next_account_index(millau_sign.signer.public().clone().into()) .next_account_index(millau_sign.signer.public().clone().into())
@@ -322,7 +322,7 @@ async fn run_send_message(command: cli::SendMessage) -> Result<(), String> {
); );
let signed_rialto_call = Rialto::sign_transaction( let signed_rialto_call = Rialto::sign_transaction(
&rialto_client, *rialto_client.genesis_hash(),
&rialto_sign.signer, &rialto_sign.signer,
rialto_client rialto_client
.next_account_index(rialto_sign.signer.public().clone().into()) .next_account_index(rialto_sign.signer.public().clone().into())
@@ -933,4 +933,40 @@ mod tests {
); );
assert!(Rialto::verify_message(&payload).is_err()); assert!(Rialto::verify_message(&payload).is_err());
} }
#[test]
fn rialto_tx_extra_bytes_constant_is_correct() {
let rialto_call = rialto_runtime::Call::System(rialto_runtime::SystemCall::remark(vec![]));
let rialto_tx = Rialto::sign_transaction(
Default::default(),
&sp_keyring::AccountKeyring::Alice.pair(),
0,
rialto_call.clone(),
);
let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len();
assert!(
bp_rialto::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction,
"Hardcoded number of extra bytes in Rialto transaction {} is lower than actual value: {}",
bp_rialto::TX_EXTRA_BYTES,
extra_bytes_in_transaction,
);
}
#[test]
fn millau_tx_extra_bytes_constant_is_correct() {
let millau_call = millau_runtime::Call::System(millau_runtime::SystemCall::remark(vec![]));
let millau_tx = Millau::sign_transaction(
Default::default(),
&sp_keyring::AccountKeyring::Alice.pair(),
0,
millau_call.clone(),
);
let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len();
assert!(
bp_millau::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction,
"Hardcoded number of extra bytes in Millau transaction {} is lower than actual value: {}",
bp_millau::TX_EXTRA_BYTES,
extra_bytes_in_transaction,
);
}
} }
@@ -49,7 +49,10 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto {
(), (),
) )
.into(); .into();
let transaction = Rialto::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call);
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Rialto::sign_transaction(genesis_hash, &self.target_sign.signer, nonce, call);
Ok(transaction) Ok(transaction)
} }
} }
@@ -68,7 +68,8 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
let call: millau_runtime::Call = let call: millau_runtime::Call =
millau_runtime::MessageLaneCall::receive_messages_delivery_proof(proof, relayers_state).into(); millau_runtime::MessageLaneCall::receive_messages_delivery_proof(proof, relayers_state).into();
let call_weight = call.get_dispatch_info().weight; let call_weight = call.get_dispatch_info().weight;
let transaction = Millau::sign_transaction(&self.source_client, &self.source_sign.signer, nonce, call); let genesis_hash = *self.source_client.genesis_hash();
let transaction = Millau::sign_transaction(genesis_hash, &self.source_sign.signer, nonce, call);
log::trace!( log::trace!(
target: "bridge", target: "bridge",
"Prepared Rialto -> Millau confirmation transaction. Weight: {}/{}, size: {}/{}", "Prepared Rialto -> Millau confirmation transaction. Weight: {}/{}, size: {}/{}",
@@ -103,7 +104,8 @@ impl SubstrateMessageLane for MillauMessagesToRialto {
) )
.into(); .into();
let call_weight = call.get_dispatch_info().weight; let call_weight = call.get_dispatch_info().weight;
let transaction = Rialto::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call); let genesis_hash = *self.target_client.genesis_hash();
let transaction = Rialto::sign_transaction(genesis_hash, &self.target_sign.signer, nonce, call);
log::trace!( log::trace!(
target: "bridge", target: "bridge",
"Prepared Millau -> Rialto delivery transaction. Weight: {}/{}, size: {}/{}", "Prepared Millau -> Rialto delivery transaction. Weight: {}/{}, size: {}/{}",
@@ -49,7 +49,10 @@ impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau {
(), (),
) )
.into(); .into();
let transaction = Millau::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call);
let genesis_hash = *self.target_client.genesis_hash();
let transaction = Millau::sign_transaction(genesis_hash, &self.target_sign.signer, nonce, call);
Ok(transaction) Ok(transaction)
} }
} }
@@ -68,7 +68,8 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
let call: rialto_runtime::Call = let call: rialto_runtime::Call =
rialto_runtime::MessageLaneCall::receive_messages_delivery_proof(proof, relayers_state).into(); rialto_runtime::MessageLaneCall::receive_messages_delivery_proof(proof, relayers_state).into();
let call_weight = call.get_dispatch_info().weight; let call_weight = call.get_dispatch_info().weight;
let transaction = Rialto::sign_transaction(&self.source_client, &self.source_sign.signer, nonce, call); let genesis_hash = *self.source_client.genesis_hash();
let transaction = Rialto::sign_transaction(genesis_hash, &self.source_sign.signer, nonce, call);
log::trace!( log::trace!(
target: "bridge", target: "bridge",
"Prepared Millau -> Rialto confirmation transaction. Weight: {}/{}, size: {}/{}", "Prepared Millau -> Rialto confirmation transaction. Weight: {}/{}, size: {}/{}",
@@ -103,7 +104,8 @@ impl SubstrateMessageLane for RialtoMessagesToMillau {
) )
.into(); .into();
let call_weight = call.get_dispatch_info().weight; let call_weight = call.get_dispatch_info().weight;
let transaction = Millau::sign_transaction(&self.target_client, &self.target_sign.signer, nonce, call); let genesis_hash = *self.target_client.genesis_hash();
let transaction = Millau::sign_transaction(genesis_hash, &self.target_sign.signer, nonce, call);
log::trace!( log::trace!(
target: "bridge", target: "bridge",
"Prepared Rialto -> Millau delivery transaction. Weight: {}/{}, size: {}/{}", "Prepared Rialto -> Millau delivery transaction. Weight: {}/{}, size: {}/{}",