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, Encode, EncodeLike, MaxEncodedLen},
33 traits::AccountIdConversion,
34 TypeId,
35};
36use sp_std::{fmt::Debug, marker::PhantomData};
37
38mod extension;
39mod registration;
40
41#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
46pub enum RewardsAccountOwner {
47 ThisChain,
49 BridgedChain,
51}
52
53#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)]
63pub struct RewardsAccountParams<LaneId> {
64 owner: RewardsAccountOwner,
68 bridged_chain_id: ChainId,
69 lane_id: LaneId,
70}
71
72impl<LaneId: Decode + Encode> RewardsAccountParams<LaneId> {
73 pub const fn new(
75 lane_id: LaneId,
76 bridged_chain_id: ChainId,
77 owner: RewardsAccountOwner,
78 ) -> Self {
79 Self { lane_id, bridged_chain_id, owner }
80 }
81
82 pub const fn lane_id(&self) -> &LaneId {
84 &self.lane_id
85 }
86}
87
88impl<LaneId: Decode + Encode> TypeId for RewardsAccountParams<LaneId> {
89 const TYPE_ID: [u8; 4] = *b"brap";
90}
91
92pub trait PaymentProcedure<Relayer, Reward> {
94 type Error: Debug;
96 type LaneId: Decode + Encode;
98
99 fn pay_reward(
101 relayer: &Relayer,
102 rewards_account_params: RewardsAccountParams<Self::LaneId>,
103 reward: Reward,
104 ) -> Result<(), Self::Error>;
105}
106
107impl<Relayer, Reward> PaymentProcedure<Relayer, Reward> for () {
108 type Error = &'static str;
109 type LaneId = ();
110
111 fn pay_reward(
112 _: &Relayer,
113 _: RewardsAccountParams<Self::LaneId>,
114 _: Reward,
115 ) -> Result<(), Self::Error> {
116 Ok(())
117 }
118}
119
120pub struct PayRewardFromAccount<T, Relayer, LaneId>(PhantomData<(T, Relayer, LaneId)>);
123
124impl<T, Relayer, LaneId> PayRewardFromAccount<T, Relayer, LaneId>
125where
126 Relayer: Decode + Encode,
127 LaneId: Decode + Encode,
128{
129 pub fn rewards_account(params: RewardsAccountParams<LaneId>) -> Relayer {
131 params.into_sub_account_truncating(b"rewards-account")
132 }
133}
134
135impl<T, Relayer, LaneId> PaymentProcedure<Relayer, T::Balance>
136 for PayRewardFromAccount<T, Relayer, LaneId>
137where
138 T: frame_support::traits::fungible::Mutate<Relayer>,
139 Relayer: Decode + Encode + Eq,
140 LaneId: Decode + Encode,
141{
142 type Error = sp_runtime::DispatchError;
143 type LaneId = LaneId;
144
145 fn pay_reward(
146 relayer: &Relayer,
147 rewards_account_params: RewardsAccountParams<Self::LaneId>,
148 reward: T::Balance,
149 ) -> Result<(), Self::Error> {
150 T::transfer(
151 &Self::rewards_account(rewards_account_params),
152 relayer,
153 reward,
154 Preservation::Expendable,
155 )
156 .map(drop)
157 }
158}
159
160pub struct RelayerRewardsKeyProvider<AccountId, Reward, LaneId>(
163 PhantomData<(AccountId, Reward, LaneId)>,
164);
165
166impl<AccountId, Reward, LaneId> StorageDoubleMapKeyProvider
167 for RelayerRewardsKeyProvider<AccountId, Reward, LaneId>
168where
169 AccountId: 'static + Codec + EncodeLike + Send + Sync,
170 Reward: 'static + Codec + EncodeLike + Send + Sync,
171 LaneId: Codec + EncodeLike + Send + Sync,
172{
173 const MAP_NAME: &'static str = "RelayerRewards";
174
175 type Hasher1 = Blake2_128Concat;
176 type Key1 = AccountId;
177 type Hasher2 = Identity;
178 type Key2 = RewardsAccountParams<LaneId>;
179 type Value = Reward;
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use bp_messages::{HashedLaneId, LaneIdType, LegacyLaneId};
186 use sp_runtime::{app_crypto::Ss58Codec, testing::H256};
187
188 #[test]
189 fn different_lanes_are_using_different_accounts() {
190 assert_eq!(
191 PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
192 RewardsAccountParams::new(
193 HashedLaneId::try_new(1, 2).unwrap(),
194 *b"test",
195 RewardsAccountOwner::ThisChain
196 )
197 ),
198 hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
199 .into(),
200 );
201
202 assert_eq!(
203 PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
204 RewardsAccountParams::new(
205 HashedLaneId::try_new(1, 3).unwrap(),
206 *b"test",
207 RewardsAccountOwner::ThisChain
208 )
209 ),
210 hex_literal::hex!("627261700074657374a43e8951aa302c133beb5f85821a21645f07b487270ef3")
211 .into(),
212 );
213 }
214
215 #[test]
216 fn different_directions_are_using_different_accounts() {
217 assert_eq!(
218 PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
219 RewardsAccountParams::new(
220 HashedLaneId::try_new(1, 2).unwrap(),
221 *b"test",
222 RewardsAccountOwner::ThisChain
223 )
224 ),
225 hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
226 .into(),
227 );
228
229 assert_eq!(
230 PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account(
231 RewardsAccountParams::new(
232 HashedLaneId::try_new(1, 2).unwrap(),
233 *b"test",
234 RewardsAccountOwner::BridgedChain
235 )
236 ),
237 hex_literal::hex!("627261700174657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc")
238 .into(),
239 );
240 }
241
242 #[test]
243 fn pay_reward_from_account_for_legacy_lane_id_works() {
244 let test_data = vec![
245 (
248 LegacyLaneId([0, 0, 0, 1]),
249 b"bhks",
250 RewardsAccountOwner::ThisChain,
251 (0_u16, "13E5fui97x6KTwNnSjaEKZ8s7kJNot5F3aUsy3jUtuoMyUec"),
252 ),
253 (
254 LegacyLaneId([0, 0, 0, 1]),
255 b"bhks",
256 RewardsAccountOwner::BridgedChain,
257 (0_u16, "13E5fui9Ka9Vz4JbGN3xWjmwDNxnxF1N9Hhhbeu3VCqLChuj"),
258 ),
259 (
260 LegacyLaneId([0, 0, 0, 1]),
261 b"bhpd",
262 RewardsAccountOwner::ThisChain,
263 (2_u16, "EoQBtnwtXqnSnr9cgBEJpKU7NjeC9EnR4D1VjgcvHz9ZYmS"),
264 ),
265 (
266 LegacyLaneId([0, 0, 0, 1]),
267 b"bhpd",
268 RewardsAccountOwner::BridgedChain,
269 (2_u16, "EoQBtnx69txxumxSJexVzxYD1Q4LWAuWmRq8LrBWb27nhYN"),
270 ),
271 (
274 LegacyLaneId([0, 0, 0, 2]),
275 b"bhwd",
276 RewardsAccountOwner::ThisChain,
277 (4_u16, "SNihsskf7bFhnHH9HJFMjWD3FJ96ESdAQTFZUAtXudRQbaH"),
278 ),
279 (
280 LegacyLaneId([0, 0, 0, 2]),
281 b"bhwd",
282 RewardsAccountOwner::BridgedChain,
283 (4_u16, "SNihsskrjeSDuD5xumyYv9H8sxZEbNkG7g5C5LT8CfPdaSE"),
284 ),
285 (
286 LegacyLaneId([0, 0, 0, 2]),
287 b"bhro",
288 RewardsAccountOwner::ThisChain,
289 (4_u16, "SNihsskf7bF2vWogkC6uFoiqPhd3dUX6TGzYZ1ocJdo3xHp"),
290 ),
291 (
292 LegacyLaneId([0, 0, 0, 2]),
293 b"bhro",
294 RewardsAccountOwner::BridgedChain,
295 (4_u16, "SNihsskrjeRZ3ScWNfq6SSnw2N3BzQeCAVpBABNCbfmHENB"),
296 ),
297 ];
298
299 for (lane_id, bridged_chain_id, owner, (expected_ss58, expected_account)) in test_data {
300 assert_eq!(
301 expected_account,
302 sp_runtime::AccountId32::new(PayRewardFromAccount::<
303 [u8; 32],
304 [u8; 32],
305 LegacyLaneId,
306 >::rewards_account(RewardsAccountParams::new(
307 lane_id,
308 *bridged_chain_id,
309 owner
310 )))
311 .to_ss58check_with_version(expected_ss58.into())
312 );
313 }
314 }
315}