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::{RawMessageProcessor, SymbioticMessageProcessor};
54
}
55

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

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

            
71
56
        let fees_account: T::AccountId = T::FeesAccount::get();
72

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

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

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

            
89
56
        Ok(())
90
56
    }
91
}
92

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

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

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

            
112
5
    T::Hashing::hash(digest.as_slice())
113
5
}
114

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

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

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

            
135
        (randomness, block_number)
136
    }
137
}
138

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

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

            
164
2279
        random_seed
165
2279
    }
166
}
167

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

            
192
114
        XcmAssets::new()
193
114
    }
194
}