1#![warn(missing_docs)]
20#![cfg_attr(not(feature = "std"), no_std)]
21
22pub use extension::{
23 BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig,
24 RuntimeWithUtilityPallet,
25};
26pub use registration::{ExplicitOrAccountParams, Registration, StakeAndSlash};
27
28use bp_runtime::{ChainId, StorageDoubleMapKeyProvider};
29use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity};
30use scale_info::TypeInfo;
31use sp_runtime::{
32 codec::{Codec, Decode, DecodeWithMemTracking, Encode, EncodeLike, MaxEncodedLen},
33 traits::AccountIdConversion,
34 TypeId,
35};
36use sp_std::{fmt::Debug, marker::PhantomData};
37
38mod extension;
39mod registration;
40
41#[derive(
46 Copy,
47 Clone,
48 Debug,
49 Decode,
50 DecodeWithMemTracking,
51 Encode,
52 Eq,
53 PartialEq,
54 TypeInfo,
55 MaxEncodedLen,
56)]
57pub enum RewardsAccountOwner {
58 ThisChain,
60 BridgedChain,
62}
63
64#[derive(
74 Copy,
75 Clone,
76 Debug,
77 Decode,
78 DecodeWithMemTracking,
79 Encode,
80 Eq,
81 PartialEq,
82 TypeInfo,
83 MaxEncodedLen,
84)]
85pub struct RewardsAccountParams<LaneId> {
86 owner: RewardsAccountOwner,
90 bridged_chain_id: ChainId,
91 lane_id: LaneId,
92}
93
94impl<LaneId: Decode + Encode> RewardsAccountParams<LaneId> {
95 pub const fn new(
97 lane_id: LaneId,
98 bridged_chain_id: ChainId,
99 owner: RewardsAccountOwner,
100 ) -> Self {
101 Self { lane_id, bridged_chain_id, owner }
102 }
103
104 pub const fn lane_id(&self) -> &LaneId {
106 &self.lane_id
107 }
108}
109
110impl<LaneId: Decode + Encode> TypeId for RewardsAccountParams<LaneId> {
111 const TYPE_ID: [u8; 4] = *b"brap";
112}
113
114pub trait PaymentProcedure<Relayer, Reward, RewardBalance> {
116 type Error: Debug;
118
119 type Beneficiary: Clone + Debug + Decode + Encode + Eq + TypeInfo;
121
122 fn pay_reward(
125 relayer: &Relayer,
126 reward: Reward,
127 reward_balance: RewardBalance,
128 beneficiary: Self::Beneficiary,
129 ) -> Result<(), Self::Error>;
130}
131
132impl<Relayer, Reward, RewardBalance> PaymentProcedure<Relayer, Reward, RewardBalance> for () {
133 type Error = &'static str;
134 type Beneficiary = ();
135
136 fn pay_reward(
137 _: &Relayer,
138 _: Reward,
139 _: RewardBalance,
140 _: Self::Beneficiary,
141 ) -> Result<(), Self::Error> {
142 Ok(())
143 }
144}
145
146pub struct PayRewardFromAccount<T, Relayer, LaneId, RewardBalance>(
149 PhantomData<(T, Relayer, LaneId, RewardBalance)>,
150);
151
152impl<T, Relayer, LaneId, RewardBalance> PayRewardFromAccount<T, Relayer, LaneId, RewardBalance>
153where
154 Relayer: Decode + Encode,
155 LaneId: Decode + Encode,
156{
157 pub fn rewards_account(params: RewardsAccountParams<LaneId>) -> Relayer {
159 params.into_sub_account_truncating(b"rewards-account")
160 }
161}
162
163impl<T, Relayer, LaneId, RewardBalance>
164 PaymentProcedure<Relayer, RewardsAccountParams<LaneId>, RewardBalance>
165 for PayRewardFromAccount<T, Relayer, LaneId, RewardBalance>
166where
167 T: frame_support::traits::fungible::Mutate<Relayer>,
168 T::Balance: From<RewardBalance>,
169 Relayer: Clone + Debug + Decode + Encode + Eq + TypeInfo,
170 LaneId: Decode + Encode,
171{
172 type Error = sp_runtime::DispatchError;
173 type Beneficiary = Relayer;
174
175 fn pay_reward(
176 _: &Relayer,
177 reward_kind: RewardsAccountParams<LaneId>,
178 reward: RewardBalance,
179 beneficiary: Self::Beneficiary,
180 ) -> Result<(), Self::Error> {
181 T::transfer(
182 &Self::rewards_account(reward_kind),
183 &beneficiary.into(),
184 reward.into(),
185 Preservation::Expendable,
186 )
187 .map(drop)
188 }
189}
190
191pub struct RelayerRewardsKeyProvider<AccountId, Reward, RewardBalance>(
194 PhantomData<(AccountId, Reward, RewardBalance)>,
195);
196
197impl<AccountId, Reward, RewardBalance> StorageDoubleMapKeyProvider
198 for RelayerRewardsKeyProvider<AccountId, Reward, RewardBalance>
199where
200 AccountId: 'static + Codec + EncodeLike + Send + Sync,
201 Reward: Codec + EncodeLike + Send + Sync,
202 RewardBalance: 'static + Codec + EncodeLike + Send + Sync,
203{
204 const MAP_NAME: &'static str = "RelayerRewards";
205
206 type Hasher1 = Blake2_128Concat;
207 type Key1 = AccountId;
208 type Hasher2 = Identity;
209 type Key2 = Reward;
210 type Value = RewardBalance;
211}
212
213pub trait RewardLedger<Relayer, Reward, RewardBalance> {
218 fn register_reward(relayer: &Relayer, reward: Reward, reward_balance: RewardBalance);
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use bp_messages::{HashedLaneId, LaneIdType, LegacyLaneId};
226 use sp_runtime::{app_crypto::Ss58Codec, testing::H256};
227
228 #[test]
229 fn different_lanes_are_using_different_accounts() {
230 assert_eq!(
231 PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account(
232 RewardsAccountParams::new(
233 HashedLaneId::try_new(1, 2).unwrap(),
234 *b"test",
235 RewardsAccountOwner::ThisChain
236 )
237 ),
238 hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
239 .into(),
240 );
241
242 assert_eq!(
243 PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account(
244 RewardsAccountParams::new(
245 HashedLaneId::try_new(1, 3).unwrap(),
246 *b"test",
247 RewardsAccountOwner::ThisChain
248 )
249 ),
250 hex_literal::hex!("627261700074657374a43e8951aa302c133beb5f85821a21645f07b487270ef3")
251 .into(),
252 );
253 }
254
255 #[test]
256 fn different_directions_are_using_different_accounts() {
257 assert_eq!(
258 PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account(
259 RewardsAccountParams::new(
260 HashedLaneId::try_new(1, 2).unwrap(),
261 *b"test",
262 RewardsAccountOwner::ThisChain
263 )
264 ),
265 hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
266 .into(),
267 );
268
269 assert_eq!(
270 PayRewardFromAccount::<(), H256, HashedLaneId, ()>::rewards_account(
271 RewardsAccountParams::new(
272 HashedLaneId::try_new(1, 2).unwrap(),
273 *b"test",
274 RewardsAccountOwner::BridgedChain
275 )
276 ),
277 hex_literal::hex!("627261700174657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
278 .into(),
279 );
280 }
281
282 #[test]
283 fn pay_reward_from_account_for_legacy_lane_id_works() {
284 let test_data = vec![
285 (
288 LegacyLaneId([0, 0, 0, 1]),
289 b"bhks",
290 RewardsAccountOwner::ThisChain,
291 (0_u16, "13E5fui97x6KTwNnSjaEKZ8s7kJNot5F3aUsy3jUtuoMyUec"),
292 ),
293 (
294 LegacyLaneId([0, 0, 0, 1]),
295 b"bhks",
296 RewardsAccountOwner::BridgedChain,
297 (0_u16, "13E5fui9Ka9Vz4JbGN3xWjmwDNxnxF1N9Hhhbeu3VCqLChuj"),
298 ),
299 (
300 LegacyLaneId([0, 0, 0, 1]),
301 b"bhpd",
302 RewardsAccountOwner::ThisChain,
303 (2_u16, "EoQBtnwtXqnSnr9cgBEJpKU7NjeC9EnR4D1VjgcvHz9ZYmS"),
304 ),
305 (
306 LegacyLaneId([0, 0, 0, 1]),
307 b"bhpd",
308 RewardsAccountOwner::BridgedChain,
309 (2_u16, "EoQBtnx69txxumxSJexVzxYD1Q4LWAuWmRq8LrBWb27nhYN"),
310 ),
311 (
314 LegacyLaneId([0, 0, 0, 2]),
315 b"bhwd",
316 RewardsAccountOwner::ThisChain,
317 (4_u16, "SNihsskf7bFhnHH9HJFMjWD3FJ96ESdAQTFZUAtXudRQbaH"),
318 ),
319 (
320 LegacyLaneId([0, 0, 0, 2]),
321 b"bhwd",
322 RewardsAccountOwner::BridgedChain,
323 (4_u16, "SNihsskrjeSDuD5xumyYv9H8sxZEbNkG7g5C5LT8CfPdaSE"),
324 ),
325 (
326 LegacyLaneId([0, 0, 0, 2]),
327 b"bhro",
328 RewardsAccountOwner::ThisChain,
329 (4_u16, "SNihsskf7bF2vWogkC6uFoiqPhd3dUX6TGzYZ1ocJdo3xHp"),
330 ),
331 (
332 LegacyLaneId([0, 0, 0, 2]),
333 b"bhro",
334 RewardsAccountOwner::BridgedChain,
335 (4_u16, "SNihsskrjeRZ3ScWNfq6SSnw2N3BzQeCAVpBABNCbfmHENB"),
336 ),
337 ];
338
339 for (lane_id, bridged_chain_id, owner, (expected_ss58, expected_account)) in test_data {
340 assert_eq!(
341 expected_account,
342 sp_runtime::AccountId32::new(PayRewardFromAccount::<
343 [u8; 32],
344 [u8; 32],
345 LegacyLaneId,
346 (),
347 >::rewards_account(RewardsAccountParams::new(
348 lane_id,
349 *bridged_chain_id,
350 owner
351 )))
352 .to_ss58check_with_version(expected_ss58.into())
353 );
354 }
355 }
356}