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
//! Shared code between relay runtimes.
18

            
19
extern crate alloc;
20

            
21
use alloc::vec::Vec;
22
use core::marker::PhantomData;
23
use frame_support::{
24
    pallet_prelude::Zero,
25
    traits::{
26
        fungible::{Inspect, Mutate},
27
        tokens::{Fortitude, Preservation},
28
    },
29
};
30
use frame_system::pallet_prelude::BlockNumberFor;
31
use parity_scale_codec::Encode;
32
use snowbridge_core::Channel;
33
use snowbridge_inbound_queue_primitives::EventProof as Message;
34
use snowbridge_pallet_inbound_queue::RewardProcessor;
35
use sp_core::Get;
36
use sp_runtime::{traits::Hash as _, DispatchResult};
37
use xcm::latest::{prelude::*, Assets as XcmAssets};
38
use xcm_builder::{deposit_or_burn_fee, HandleFee};
39
use xcm_executor::traits::{FeeReason, TransactAsset};
40

            
41
#[cfg(feature = "relay")]
42
pub mod v1 {
43
    pub use crate::processors::v1::{
44
        EthTokensLocalProcessor, InboundTokenTransferValidator, NativeContainerTokensProcessor,
45
        NativeTokenTransferData, NativeTokenTransferMessageProcessor,
46
    };
47

            
48
    pub use super::RewardThroughFeesAccount;
49
}
50

            
51
#[cfg(feature = "relay")]
52
pub mod v2 {
53
    pub use crate::processors::v2::{
54
        LayerZeroMessageProcessor, RawMessageProcessor, SymbioticMessageProcessor,
55
    };
56
}
57

            
58
/// Rewards the relayer that processed a native token transfer message
59
/// using the FeesAccount configured in pallet_ethereum_token_transfers
60
pub struct RewardThroughFeesAccount<T>(PhantomData<T>);
61

            
62
impl<T> RewardProcessor<T> for RewardThroughFeesAccount<T>
63
where
64
    T: snowbridge_pallet_inbound_queue::Config + pallet_ethereum_token_transfers::Config,
65
    T::AccountId: From<sp_runtime::AccountId32>,
66
    <T::Token as Inspect<T::AccountId>>::Balance: core::fmt::Debug,
67
{
68
56
    fn process_reward(who: T::AccountId, _channel: Channel, message: Message) -> DispatchResult {
69
56
        let reward_amount = snowbridge_pallet_inbound_queue::Pallet::<T>::calculate_delivery_cost(
70
56
            message.encode().len() as u32,
71
        );
72

            
73
56
        let fees_account: T::AccountId = T::FeesAccount::get();
74

            
75
56
        let amount =
76
56
            T::Token::reducible_balance(&fees_account, Preservation::Preserve, Fortitude::Polite)
77
56
                .min(reward_amount);
78

            
79
56
        if amount != reward_amount {
80
16
            log::warn!(
81
                "RewardThroughFeesAccount: fees account running low on funds {:?}: {:?}",
82
                fees_account,
83
                amount
84
            );
85
40
        }
86

            
87
56
        if !amount.is_zero() {
88
40
            T::Token::transfer(&fees_account, &who, amount, Preservation::Preserve)?;
89
16
        }
90

            
91
56
        Ok(())
92
56
    }
93
}
94

            
95
pub struct BabeSlotBeacon<T>(PhantomData<T>);
96
impl<T: pallet_babe::Config> sp_runtime::traits::BlockNumberProvider for BabeSlotBeacon<T> {
97
    type BlockNumber = u32;
98

            
99
24
    fn current_block_number() -> Self::BlockNumber {
100
        // TODO: nimbus_primitives::SlotBeacon requires u32, but this is a u64 in pallet_babe, and
101
        // also it gets converted to u64 in pallet_author_noting, so let's do something to remove
102
        // this intermediate u32 conversion, such as using a different trait
103
24
        u64::from(pallet_babe::CurrentSlot::<T>::get()) as u32
104
24
    }
105
}
106

            
107
/// Combines the vrf output of the previous block with the provided subject.
108
/// This ensures that the randomness will be different on different pallets, as long as the subject is different.
109
5
pub fn mix_randomness<T: frame_system::Config>(vrf_output: [u8; 32], subject: &[u8]) -> T::Hash {
110
5
    let mut digest = Vec::new();
111
5
    digest.extend_from_slice(vrf_output.as_ref());
112
5
    digest.extend_from_slice(subject);
113

            
114
5
    T::Hashing::hash(digest.as_slice())
115
5
}
116

            
117
pub struct BabeAuthorVrfBlockRandomness<T>(PhantomData<T>);
118
impl<T: pallet_babe::Config + frame_system::Config> BabeAuthorVrfBlockRandomness<T> {
119
999
    pub fn get_block_randomness() -> Option<[u8; 32]> {
120
        // In a relay context we get block randomness from Babe's AuthorVrfRandomness
121
999
        pallet_babe::Pallet::<T>::author_vrf_randomness()
122
999
    }
123

            
124
999
    pub fn get_block_randomness_mixed(subject: &[u8]) -> Option<T::Hash> {
125
999
        Self::get_block_randomness().map(|random_hash| mix_randomness::<T>(random_hash, subject))
126
999
    }
127
}
128

            
129
impl<T: pallet_babe::Config + frame_system::Config>
130
    frame_support::traits::Randomness<T::Hash, BlockNumberFor<T>>
131
    for BabeAuthorVrfBlockRandomness<T>
132
{
133
    fn random(subject: &[u8]) -> (T::Hash, BlockNumberFor<T>) {
134
        let block_number = frame_system::Pallet::<T>::block_number();
135
        let randomness = Self::get_block_randomness_mixed(subject).unwrap_or_default();
136

            
137
        (randomness, block_number)
138
    }
139
}
140

            
141
pub struct BabeGetCollatorAssignmentRandomness<T>(PhantomData<T>);
142
impl<T: pallet_babe::Config + frame_system::Config> Get<[u8; 32]>
143
    for BabeGetCollatorAssignmentRandomness<T>
144
{
145
2172
    fn get() -> [u8; 32] {
146
2172
        let block_number = frame_system::Pallet::<T>::block_number();
147
2172
        let random_seed = if !block_number.is_zero() {
148
998
            if let Some(random_hash) = {
149
998
                BabeAuthorVrfBlockRandomness::<T>::get_block_randomness_mixed(b"CollatorAssignment")
150
998
            } {
151
                // Return random_hash as a [u8; 32] instead of a Hash
152
4
                let mut buf = [0u8; 32];
153
4
                let len = core::cmp::min(32, random_hash.as_ref().len());
154
4
                buf[..len].copy_from_slice(&random_hash.as_ref()[..len]);
155

            
156
4
                buf
157
            } else {
158
                // If there is no randomness return [0; 32]
159
994
                [0; 32]
160
            }
161
        } else {
162
            // In block 0 (genesis) there is no randomness
163
1174
            [0; 32]
164
        };
165

            
166
2172
        random_seed
167
2172
    }
168
}
169

            
170
/// Handler for depositing fees to the exporter fees account or a default account based on the reason.
171
pub struct ExporterFeeHandler<AssetTransactor, ExporterFeesAccount, DefaultAccount>(
172
    PhantomData<(AssetTransactor, ExporterFeesAccount, DefaultAccount)>,
173
);
174
impl<AssetTransactor, ExporterFeesAccount, DefaultAccount> HandleFee
175
    for ExporterFeeHandler<AssetTransactor, ExporterFeesAccount, DefaultAccount>
176
where
177
    AssetTransactor: TransactAsset,
178
    ExporterFeesAccount: Get<Location>,
179
    DefaultAccount: Get<Location>,
180
{
181
108
    fn handle_fee(fee: XcmAssets, context: Option<&XcmContext>, reason: FeeReason) -> XcmAssets {
182
108
        match reason {
183
            FeeReason::Export {
184
                network: _,
185
                destination: _,
186
61
            } => {
187
61
                deposit_or_burn_fee::<AssetTransactor>(fee, context, ExporterFeesAccount::get());
188
61
            }
189
47
            _ => {
190
47
                deposit_or_burn_fee::<AssetTransactor>(fee, context, DefaultAccount::get());
191
47
            }
192
        }
193

            
194
108
        XcmAssets::new()
195
108
    }
196
}