Skip to content

Commit 0a7f9f1

Browse files
authored
pallet-atomic-swap: generialized swap action (#6421)
* pallet-atomic-swap: generialized swap action * Bump spec_version * Fix weight calculation * Remove unnecessary type aliases
1 parent bc337e7 commit 0a7f9f1

File tree

2 files changed

+108
-47
lines changed

2 files changed

+108
-47
lines changed

src/lib.rs

Lines changed: 102 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@
4242

4343
mod tests;
4444

45-
use sp_std::prelude::*;
45+
use sp_std::{prelude::*, marker::PhantomData, ops::{Deref, DerefMut}};
4646
use sp_io::hashing::blake2_256;
4747
use frame_support::{
48-
decl_module, decl_storage, decl_event, decl_error, ensure,
48+
Parameter, decl_module, decl_storage, decl_event, decl_error, ensure,
4949
traits::{Get, Currency, ReservableCurrency, BalanceStatus},
5050
weights::Weight,
5151
dispatch::DispatchResult,
@@ -55,37 +55,98 @@ use codec::{Encode, Decode};
5555
use sp_runtime::RuntimeDebug;
5656

5757
/// Pending atomic swap operation.
58-
#[derive(Clone, RuntimeDebug, Eq, PartialEq, Encode, Decode)]
59-
pub struct PendingSwap<AccountId, Balance, BlockNumber> {
58+
#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode)]
59+
pub struct PendingSwap<T: Trait> {
6060
/// Source of the swap.
61-
pub source: AccountId,
62-
/// Balance value of the swap.
63-
pub balance: Balance,
61+
pub source: T::AccountId,
62+
/// Action of this swap.
63+
pub action: T::SwapAction,
6464
/// End block of the lock.
65-
pub end_block: BlockNumber,
65+
pub end_block: T::BlockNumber,
6666
}
6767

68-
/// Balance type from the pallet's point of view.
69-
pub type BalanceFor<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
68+
/// Hashed proof type.
69+
pub type HashedProof = [u8; 32];
7070

71-
/// AccountId type from the pallet's point of view.
72-
pub type AccountIdFor<T> = <T as frame_system::Trait>::AccountId;
71+
/// Definition of a pending atomic swap action. It contains the following three phrases:
72+
///
73+
/// - **Reserve**: reserve the resources needed for a swap. This is to make sure that **Claim**
74+
/// succeeds with best efforts.
75+
/// - **Claim**: claim any resources reserved in the first phrase.
76+
/// - **Cancel**: cancel any resources reserved in the first phrase.
77+
pub trait SwapAction<T: Trait> {
78+
/// Reserve the resources needed for the swap, from the given `source`. The reservation is
79+
/// allowed to fail. If that is the case, the the full swap creation operation is cancelled.
80+
fn reserve(&self, source: &T::AccountId) -> DispatchResult;
81+
/// Claim the reserved resources, with `source` and `target`. Returns whether the claim
82+
/// succeeds.
83+
fn claim(&self, source: &T::AccountId, target: &T::AccountId) -> bool;
84+
/// Weight for executing the operation.
85+
fn weight(&self) -> Weight;
86+
/// Cancel the resources reserved in `source`.
87+
fn cancel(&self, source: &T::AccountId);
88+
}
7389

74-
/// BlockNumber type from the pallet's point of view.
75-
pub type BlockNumberFor<T> = <T as frame_system::Trait>::BlockNumber;
90+
/// A swap action that only allows transferring balances.
91+
#[derive(Clone, RuntimeDebug, Eq, PartialEq, Encode, Decode)]
92+
pub struct BalanceSwapAction<T: Trait, C: ReservableCurrency<T::AccountId>> {
93+
value: <C as Currency<<T as frame_system::Trait>::AccountId>>::Balance,
94+
_marker: PhantomData<C>,
95+
}
7696

77-
/// PendingSwap type from the pallet's point of view.
78-
pub type PendingSwapFor<T> = PendingSwap<AccountIdFor<T>, BalanceFor<T>, BlockNumberFor<T>>;
97+
impl<T: Trait, C> BalanceSwapAction<T, C> where
98+
C: ReservableCurrency<T::AccountId>,
99+
{
100+
/// Create a new swap action value of balance.
101+
pub fn new(value: <C as Currency<<T as frame_system::Trait>::AccountId>>::Balance) -> Self {
102+
Self { value, _marker: PhantomData }
103+
}
104+
}
79105

80-
/// Hashed proof type.
81-
pub type HashedProof = [u8; 32];
106+
impl<T: Trait, C> Deref for BalanceSwapAction<T, C> where
107+
C: ReservableCurrency<T::AccountId>,
108+
{
109+
type Target = <C as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
110+
111+
fn deref(&self) -> &Self::Target {
112+
&self.value
113+
}
114+
}
115+
116+
impl<T: Trait, C> DerefMut for BalanceSwapAction<T, C> where
117+
C: ReservableCurrency<T::AccountId>,
118+
{
119+
fn deref_mut(&mut self) -> &mut Self::Target {
120+
&mut self.value
121+
}
122+
}
123+
124+
impl<T: Trait, C> SwapAction<T> for BalanceSwapAction<T, C> where
125+
C: ReservableCurrency<T::AccountId>,
126+
{
127+
fn reserve(&self, source: &T::AccountId) -> DispatchResult {
128+
C::reserve(&source, self.value)
129+
}
130+
131+
fn claim(&self, source: &T::AccountId, target: &T::AccountId) -> bool {
132+
C::repatriate_reserved(source, target, self.value, BalanceStatus::Free).is_ok()
133+
}
134+
135+
fn weight(&self) -> Weight {
136+
T::DbWeight::get().reads_writes(1, 1)
137+
}
138+
139+
fn cancel(&self, source: &T::AccountId) {
140+
C::unreserve(source, self.value);
141+
}
142+
}
82143

83144
/// Atomic swap's pallet configuration trait.
84145
pub trait Trait: frame_system::Trait {
85146
/// The overarching event type.
86147
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
87-
/// The currency mechanism.
88-
type Currency: ReservableCurrency<Self::AccountId>;
148+
/// Swap action.
149+
type SwapAction: SwapAction<Self> + Parameter;
89150
/// Limit of proof size.
90151
///
91152
/// Atomic swap is only atomic if once the proof is revealed, both parties can submit the proofs
@@ -103,7 +164,7 @@ decl_storage! {
103164
trait Store for Module<T: Trait> as AtomicSwap {
104165
pub PendingSwaps: double_map
105166
hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) HashedProof
106-
=> Option<PendingSwapFor<T>>;
167+
=> Option<PendingSwap<T>>;
107168
}
108169
}
109170

@@ -121,6 +182,8 @@ decl_error! {
121182
AlreadyClaimed,
122183
/// Swap does not exist.
123184
NotExist,
185+
/// Claim action mismatch.
186+
ClaimActionMismatch,
124187
/// Duration has not yet passed for the swap to be cancelled.
125188
DurationNotPassed,
126189
}
@@ -129,14 +192,13 @@ decl_error! {
129192
decl_event!(
130193
/// Event of atomic swap pallet.
131194
pub enum Event<T> where
132-
Balance = BalanceFor<T>,
133-
AccountId = AccountIdFor<T>,
134-
PendingSwap = PendingSwapFor<T>,
195+
AccountId = <T as system::Trait>::AccountId,
196+
PendingSwap = PendingSwap<T>,
135197
{
136198
/// Swap created.
137199
NewSwap(AccountId, HashedProof, PendingSwap),
138200
/// Swap claimed. The last parameter indicates whether the execution succeeds.
139-
SwapClaimed(AccountId, HashedProof, Balance, bool),
201+
SwapClaimed(AccountId, HashedProof, bool),
140202
/// Swap cancelled.
141203
SwapCancelled(AccountId, HashedProof),
142204
}
@@ -164,22 +226,22 @@ decl_module! {
164226
#[weight = T::DbWeight::get().reads_writes(1, 1).saturating_add(40_000_000)]
165227
fn create_swap(
166228
origin,
167-
target: AccountIdFor<T>,
229+
target: T::AccountId,
168230
hashed_proof: HashedProof,
169-
balance: BalanceFor<T>,
170-
duration: BlockNumberFor<T>,
231+
action: T::SwapAction,
232+
duration: T::BlockNumber,
171233
) {
172234
let source = ensure_signed(origin)?;
173235
ensure!(
174236
!PendingSwaps::<T>::contains_key(&target, hashed_proof),
175237
Error::<T>::AlreadyExist
176238
);
177239

178-
T::Currency::reserve(&source, balance)?;
240+
action.reserve(&source)?;
179241

180242
let swap = PendingSwap {
181243
source,
182-
balance,
244+
action,
183245
end_block: frame_system::Module::<T>::block_number() + duration,
184246
};
185247
PendingSwaps::<T>::insert(target.clone(), hashed_proof.clone(), swap.clone());
@@ -194,13 +256,17 @@ decl_module! {
194256
/// The dispatch origin for this call must be _Signed_.
195257
///
196258
/// - `proof`: Revealed proof of the claim.
197-
#[weight = T::DbWeight::get().reads_writes(2, 2)
259+
/// - `action`: Action defined in the swap, it must match the entry in blockchain. Otherwise
260+
/// the operation fails. This is used for weight calculation.
261+
#[weight = T::DbWeight::get().reads_writes(1, 1)
198262
.saturating_add(40_000_000)
199263
.saturating_add((proof.len() as Weight).saturating_mul(100))
264+
.saturating_add(action.weight())
200265
]
201266
fn claim_swap(
202267
origin,
203268
proof: Vec<u8>,
269+
action: T::SwapAction,
204270
) -> DispatchResult {
205271
ensure!(
206272
proof.len() <= T::ProofLimit::get() as usize,
@@ -212,18 +278,14 @@ decl_module! {
212278

213279
let swap = PendingSwaps::<T>::get(&target, hashed_proof)
214280
.ok_or(Error::<T>::InvalidProof)?;
281+
ensure!(swap.action == action, Error::<T>::ClaimActionMismatch);
215282

216-
let succeeded = T::Currency::repatriate_reserved(
217-
&swap.source,
218-
&target,
219-
swap.balance,
220-
BalanceStatus::Free,
221-
).is_ok();
283+
let succeeded = swap.action.claim(&swap.source, &target);
222284

223285
PendingSwaps::<T>::remove(target.clone(), hashed_proof.clone());
224286

225287
Self::deposit_event(
226-
RawEvent::SwapClaimed(target, hashed_proof, swap.balance, succeeded)
288+
RawEvent::SwapClaimed(target, hashed_proof, succeeded)
227289
);
228290

229291
Ok(())
@@ -238,7 +300,7 @@ decl_module! {
238300
#[weight = T::DbWeight::get().reads_writes(1, 1).saturating_add(40_000_000)]
239301
fn cancel_swap(
240302
origin,
241-
target: AccountIdFor<T>,
303+
target: T::AccountId,
242304
hashed_proof: HashedProof,
243305
) {
244306
let source = ensure_signed(origin)?;
@@ -254,10 +316,7 @@ decl_module! {
254316
Error::<T>::DurationNotPassed,
255317
);
256318

257-
T::Currency::unreserve(
258-
&swap.source,
259-
swap.balance,
260-
);
319+
swap.action.cancel(&swap.source);
261320
PendingSwaps::<T>::remove(&target, hashed_proof.clone());
262321

263322
Self::deposit_event(

src/tests.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ impl_outer_origin! {
2121
// For testing the pallet, we construct most of a mock runtime. This means
2222
// first constructing a configuration type (`Test`) which `impl`s each of the
2323
// configuration traits of pallets we want to use.
24-
#[derive(Clone, Eq, PartialEq)]
24+
#[derive(Clone, Eq, Debug, PartialEq)]
2525
pub struct Test;
2626
parameter_types! {
2727
pub const BlockHashCount: u64 = 250;
@@ -71,7 +71,7 @@ parameter_types! {
7171
}
7272
impl Trait for Test {
7373
type Event = ();
74-
type Currency = Balances;
74+
type SwapAction = BalanceSwapAction<Test, Balances>;
7575
type ProofLimit = ProofLimit;
7676
}
7777
type System = frame_system::Module<Test>;
@@ -109,7 +109,7 @@ fn two_party_successful_swap() {
109109
Origin::signed(A),
110110
B,
111111
hashed_proof.clone(),
112-
50,
112+
BalanceSwapAction::new(50),
113113
1000,
114114
).unwrap();
115115

@@ -123,7 +123,7 @@ fn two_party_successful_swap() {
123123
Origin::signed(B),
124124
A,
125125
hashed_proof.clone(),
126-
75,
126+
BalanceSwapAction::new(75),
127127
1000,
128128
).unwrap();
129129

@@ -136,6 +136,7 @@ fn two_party_successful_swap() {
136136
AtomicSwap::claim_swap(
137137
Origin::signed(A),
138138
proof.to_vec(),
139+
BalanceSwapAction::new(75),
139140
).unwrap();
140141

141142
assert_eq!(Balances::free_balance(A), 100 + 75);
@@ -147,6 +148,7 @@ fn two_party_successful_swap() {
147148
AtomicSwap::claim_swap(
148149
Origin::signed(B),
149150
proof.to_vec(),
151+
BalanceSwapAction::new(50),
150152
).unwrap();
151153

152154
assert_eq!(Balances::free_balance(A), 100 - 50);

0 commit comments

Comments
 (0)