Skip to content

Commit

Permalink
PR comments
Browse files Browse the repository at this point in the history
  • Loading branch information
tmpolaczyk committed Nov 21, 2024
1 parent 679fb69 commit 54ec73d
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 205 deletions.
4 changes: 4 additions & 0 deletions pallets/external-validator-slashes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ pub mod pallet {

/// Invulnerable provider, used to get the invulnerables to know when not to slash
type InvulnerablesProvider: InvulnerablesProvider<Self::ValidatorId>;

/// Validate a message that will be sent to Ethereum.
type ValidateMessage: ValidateMessage;

/// Send a message to Ethereum. Needs to be validated first.
type OutboundQueue: DeliverMessage<
Ticket = <<Self as pallet::Config>::ValidateMessage as ValidateMessage>::Ticket,
>;
Expand Down
140 changes: 140 additions & 0 deletions primitives/bridge/src/custom_do_process_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use {
super::*,
frame_support::{
ensure,
traits::{Defensive, ProcessMessage, ProcessMessageError},
weights::WeightMeter,
},
snowbridge_pallet_outbound_queue::{
CommittedMessage, MessageLeaves, Messages, Nonce, ProcessMessageOriginOf, WeightInfo,
},
sp_runtime::traits::Hash,
sp_std::boxed::Box,
};

/// Alternative to [snowbridge_pallet_outbound_queue::Pallet::process_message] using a different
/// [Command] enum.
pub struct CustomProcessSnowbridgeMessage<T>(PhantomData<T>);

impl<T> CustomProcessSnowbridgeMessage<T>
where
T: snowbridge_pallet_outbound_queue::Config,
{
/// Process a message delivered by the MessageQueue pallet
pub(crate) fn do_process_message(
_: ProcessMessageOriginOf<T>,
mut message: &[u8],
) -> Result<bool, ProcessMessageError> {
use ProcessMessageError::*;

// Yield if the maximum number of messages has been processed this block.
// This ensures that the weight of `on_finalize` has a known maximum bound.
ensure!(
MessageLeaves::<T>::decode_len().unwrap_or(0) < T::MaxMessagesPerBlock::get() as usize,
Yield
);

// Decode bytes into versioned message
let versioned_queued_message: VersionedQueuedMessage =
VersionedQueuedMessage::decode(&mut message).map_err(|_| Corrupt)?;

// Convert versioned message into latest supported message version
let queued_message: QueuedMessage = versioned_queued_message
.try_into()
.map_err(|_| Unsupported)?;

// Obtain next nonce
let nonce = <Nonce<T>>::try_mutate(
queued_message.channel_id,
|nonce| -> Result<u64, ProcessMessageError> {
*nonce = nonce.checked_add(1).ok_or(Unsupported)?;
Ok(*nonce)
},
)?;

let pricing_params = T::PricingParameters::get();
let command = queued_message.command.index();
let params = queued_message.command.abi_encode();
let max_dispatch_gas =
ConstantGasMeter::maximum_dispatch_gas_used_at_most(&queued_message.command);
let reward = pricing_params.rewards.remote;

// Construct the final committed message
let message = CommittedMessage {
channel_id: queued_message.channel_id,
nonce,
command,
params,
max_dispatch_gas,
max_fee_per_gas: pricing_params
.fee_per_gas
.try_into()
.defensive_unwrap_or(u128::MAX),
reward: reward.try_into().defensive_unwrap_or(u128::MAX),
id: queued_message.id,
};

// ABI-encode and hash the prepared message
let message_abi_encoded = ethabi::encode(&[message.clone().into()]);
let message_abi_encoded_hash =
<T as snowbridge_pallet_outbound_queue::Config>::Hashing::hash(&message_abi_encoded);

Messages::<T>::append(Box::new(message));
MessageLeaves::<T>::append(message_abi_encoded_hash);

snowbridge_pallet_outbound_queue::Pallet::<T>::deposit_event(
snowbridge_pallet_outbound_queue::Event::MessageAccepted {
id: queued_message.id,
nonce,
},
);

Ok(true)
}
}

impl<T> ProcessMessage for CustomProcessSnowbridgeMessage<T>
where
T: snowbridge_pallet_outbound_queue::Config,
{
type Origin = T::AggregateMessageOrigin;

fn process_message(
message: &[u8],
origin: Self::Origin,
meter: &mut WeightMeter,
_id: &mut [u8; 32],
) -> Result<bool, ProcessMessageError> {
// TODO: this weight is from the pallet, should be very similar to the weight of
// Self::do_process_message, but ideally we should benchmark this separately
let weight = T::WeightInfo::do_process_message();
if meter.try_consume(weight).is_err() {
return Err(ProcessMessageError::Overweight(weight));
}

Self::do_process_message(origin.clone(), message)
}
}

/// A meter that assigns a constant amount of gas for the execution of a command
///
/// The gas figures are extracted from this report:
/// > forge test --match-path test/Gateway.t.sol --gas-report
///
/// A healthy buffer is added on top of these figures to account for:
/// * The EIP-150 63/64 rule
/// * Future EVM upgrades that may increase gas cost
pub struct ConstantGasMeter;

impl ConstantGasMeter {
// The base transaction cost, which includes:
// 21_000 transaction cost, roughly worst case 64_000 for calldata, and 100_000
// for message verification
pub const MAXIMUM_BASE_GAS: u64 = 185_000;

fn maximum_dispatch_gas_used_at_most(command: &Command) -> u64 {
match command {
Command::Test { .. } => 60_000,
}
}
}
47 changes: 47 additions & 0 deletions primitives/bridge/src/custom_send_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use {
super::*,
ethabi::H256,
frame_support::traits::EnqueueMessage,
snowbridge_core::{outbound::SendError, PRIMARY_GOVERNANCE_CHANNEL},
sp_std::marker::PhantomData,
};

/// Alternative to [snowbridge_pallet_outbound_queue::Pallet::deliver] using a different
/// origin.
pub struct CustomSendMessage<T, GetAggregateMessageOrigin>(
PhantomData<(T, GetAggregateMessageOrigin)>,
);

impl<T, GetAggregateMessageOrigin> DeliverMessage
for CustomSendMessage<T, GetAggregateMessageOrigin>
where
T: snowbridge_pallet_outbound_queue::Config,
GetAggregateMessageOrigin:
Convert<ChannelId, <T as snowbridge_pallet_outbound_queue::Config>::AggregateMessageOrigin>,
{
type Ticket = Ticket<T>;

fn deliver(ticket: Self::Ticket) -> Result<H256, SendError> {
let origin = GetAggregateMessageOrigin::convert(ticket.channel_id);

if ticket.channel_id != PRIMARY_GOVERNANCE_CHANNEL {
ensure!(
!<snowbridge_pallet_outbound_queue::Pallet<T>>::operating_mode().is_halted(),
SendError::Halted
);
}

let message = ticket.message.as_bounded_slice();

<T as snowbridge_pallet_outbound_queue::Config>::MessageQueue::enqueue_message(
message, origin,
);
snowbridge_pallet_outbound_queue::Pallet::<T>::deposit_event(
snowbridge_pallet_outbound_queue::Event::MessageQueued {
id: ticket.message_id,
},
);

Ok(ticket.message_id)
}
}
Loading

0 comments on commit 54ec73d

Please sign in to comment.