1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
use {
18
    super::*,
19
    alloc::boxed::Box,
20
    frame_support::{
21
        ensure,
22
        traits::{Defensive, ProcessMessage, ProcessMessageError},
23
        weights::WeightMeter,
24
    },
25
    snowbridge_pallet_outbound_queue::{
26
        CommittedMessage, MessageLeaves, Messages, Nonce, ProcessMessageOriginOf, WeightInfo,
27
    },
28
    sp_runtime::traits::Hash,
29
};
30

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

            
35
impl<T> CustomProcessSnowbridgeMessage<T>
36
where
37
    T: snowbridge_pallet_outbound_queue::Config,
38
{
39
    /// Process a message delivered by the MessageQueue pallet
40
106
    pub(crate) fn do_process_message(
41
106
        _: ProcessMessageOriginOf<T>,
42
106
        mut message: &[u8],
43
106
    ) -> Result<bool, ProcessMessageError> {
44
        use ProcessMessageError::*;
45

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

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

            
57
100
        log::trace!(
58
            "CustomProcessSnowbridgeMessage: {:?}",
59
            versioned_queued_message
60
        );
61

            
62
        // Convert versioned message into latest supported message version
63
100
        let queued_message: QueuedMessage = versioned_queued_message.into();
64

            
65
        // Obtain next nonce
66
100
        let nonce = <Nonce<T>>::try_mutate(
67
100
            queued_message.channel_id,
68
100
            |nonce| -> Result<u64, ProcessMessageError> {
69
100
                *nonce = nonce.checked_add(1).ok_or(Unsupported)?;
70
100
                Ok(*nonce)
71
100
            },
72
100
        )?;
73

            
74
100
        let pricing_params = T::PricingParameters::get();
75
100
        let command = queued_message.command.index();
76
100
        let params = queued_message.command.abi_encode();
77
100
        let max_dispatch_gas =
78
100
            ConstantGasMeter::maximum_dispatch_gas_used_at_most(&queued_message.command);
79
100
        let reward = pricing_params.rewards.remote;
80
100

            
81
100
        // Construct the final committed message
82
100
        let message = CommittedMessage {
83
100
            channel_id: queued_message.channel_id,
84
100
            nonce,
85
100
            command,
86
100
            params,
87
100
            max_dispatch_gas,
88
100
            max_fee_per_gas: pricing_params
89
100
                .fee_per_gas
90
100
                .try_into()
91
100
                .defensive_unwrap_or(u128::MAX),
92
100
            reward: reward.try_into().defensive_unwrap_or(u128::MAX),
93
100
            id: queued_message.id,
94
100
        };
95
100

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

            
101
100
        Messages::<T>::append(Box::new(message));
102
100
        MessageLeaves::<T>::append(message_abi_encoded_hash);
103
100

            
104
100
        snowbridge_pallet_outbound_queue::Pallet::<T>::deposit_event(
105
100
            snowbridge_pallet_outbound_queue::Event::MessageAccepted {
106
100
                id: queued_message.id,
107
100
                nonce,
108
100
            },
109
100
        );
110
100

            
111
100
        Ok(true)
112
106
    }
113
}
114

            
115
impl<T> ProcessMessage for CustomProcessSnowbridgeMessage<T>
116
where
117
    T: snowbridge_pallet_outbound_queue::Config,
118
{
119
    type Origin = T::AggregateMessageOrigin;
120

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

            
134
106
        Self::do_process_message(origin.clone(), message)
135
106
    }
136
}
137

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

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

            
154
1312
    fn maximum_dispatch_gas_used_at_most(command: &Command) -> u64 {
155
1312
        match command {
156
32
            Command::Test { .. } => 60_000,
157
            // TODO: revisit gas cost
158
192
            Command::ReportRewards { .. } => 1_000_000,
159
1088
            Command::ReportSlashes { .. } => 1_000_000,
160
        }
161
1312
    }
162
}