Skip to content
Snippets Groups Projects

Oneshot accounts

Merged Pascal Engélibert requested to merge poc-oneshot-accounts into master
3 files
+ 199
18
Compare changes
  • Side-by-side
  • Inline
Files
3
@@ -148,8 +148,8 @@ pub mod pallet {
},
OneshotAccountConsumed {
account: T::AccountId,
balance: T::Balance,
dest: T::AccountId,
dest1: (T::AccountId, T::Balance),
dest2: Option<(T::AccountId, T::Balance)>,
},
/// Random id assigned
/// [account_id, random_id]
@@ -160,15 +160,19 @@ pub mod pallet {
#[pallet::error]
pub enum Error<T> {
/// DestAccountNotExist
/// Block height is in the future
BlockHeightInFuture,
/// Block height is too old
BlockHeightTooOld,
/// Destination account does not exist
DestAccountNotExist,
/// ExistentialDeposit
/// Destination account has balance less than existential deposit
ExistentialDeposit,
/// InsufficientBalance
/// Source account has insufficient balance
InsufficientBalance,
/// OneshotAccouncAlreadyCreated
/// Destination oneshot account already exists
OneshotAccountAlreadyCreated,
/// OneshotAccountNotExist
/// Source oneshot account does not exist
OneshotAccountNotExist,
}
@@ -254,6 +258,10 @@ pub mod pallet {
// CALLS //
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Create an account that can only be consumed once
///
/// - `dest`: The oneshot account to be created.
/// - `balance`: The balance to be transfered to this oneshot account.
#[pallet::weight(500_000_000)]
pub fn create_oneshot_account(
origin: OriginFor<T>,
@@ -292,9 +300,14 @@ pub mod pallet {
Ok(())
}
/// Consume a oneshot account and transfer its balance to an account
///
/// - `block_height`: must be a recent block number. The limit is `BlockHashCount` in the past. (this is to prevent replay attacks)
/// - `dest`: `dest.1` is the destination account. If `dest.0` is `true`, then a oneshot account is created at `dest.1`. Else, `dest.1` has to be an existing account.
#[pallet::weight(500_000_000)]
pub fn consume_oneshot_account(
origin: OriginFor<T>,
block_height: T::BlockNumber,
dest: (bool, <T::Lookup as StaticLookup>::Source),
) -> DispatchResult {
let transactor = ensure_signed(origin)?;
@@ -304,31 +317,121 @@ pub mod pallet {
let value = OneshotAccounts::<T>::take(&transactor)
.ok_or(Error::<T>::OneshotAccountNotExist)?;
ensure!(
block_height <= frame_system::Pallet::<T>::block_number(),
Error::<T>::BlockHeightInFuture
);
ensure!(
frame_system::pallet::BlockHash::<T>::contains_key(block_height),
Error::<T>::BlockHeightTooOld
);
if dest_is_oneshot {
ensure!(
OneshotAccounts::<T>::get(&dest).is_none(),
Error::<T>::OneshotAccountAlreadyCreated
);
OneshotAccounts::<T>::insert(&dest, value);
Self::deposit_event(Event::OneshotAccountConsumed {
account: transactor.clone(),
balance: value,
dest: dest.clone(),
});
Self::deposit_event(Event::OneshotAccountCreated {
account: dest,
account: dest.clone(),
balance: value,
creator: transactor,
creator: transactor.clone(),
});
} else {
let dest_data = frame_system::Account::<T>::get(&dest).data;
ensure!(dest_data.was_providing(), Error::<T>::DestAccountNotExist);
Self::deposit_event(Event::OneshotAccountConsumed {
account: transactor,
balance: value,
dest,
frame_system::Account::<T>::mutate(&dest, |a| a.data.add_free(value));
}
OneshotAccounts::<T>::remove(&transactor);
Self::deposit_event(Event::OneshotAccountConsumed {
account: transactor,
dest1: (dest, value),
dest2: None,
});
Ok(())
}
/// Consume a oneshot account and transfer its balance to two accounts
///
/// - `block_height`: must be a recent block number. The limit is `BlockHashCount` in the past. (this is to prevent replay attacks)
/// - `dest1`: `dest1.1` is the destination account. If `dest1.0` is `true`, then a oneshot account is created at `dest1.1`. Else, `dest1.1` has to be an existing account.
/// - `dest2`: Idem.
/// - `balance1`: The amount transfered to `dest1`, the leftover being transfered to `dest2`.
#[pallet::weight(500_000_000)]
pub fn consume_oneshot_account_two_dests(
origin: OriginFor<T>,
block_height: T::BlockNumber,
dest1: (bool, <T::Lookup as StaticLookup>::Source),
dest2: (bool, <T::Lookup as StaticLookup>::Source),
#[pallet::compact] balance1: T::Balance,
) -> DispatchResult {
let transactor = ensure_signed(origin)?;
let dest1_is_oneshot = dest1.0;
let dest1 = T::Lookup::lookup(dest1.1)?;
let dest2_is_oneshot = dest2.0;
let dest2 = T::Lookup::lookup(dest2.1)?;
let value = OneshotAccounts::<T>::take(&transactor)
.ok_or(Error::<T>::OneshotAccountNotExist)?;
ensure!(value > balance1, Error::<T>::InsufficientBalance);
let balance2 = value.saturating_sub(balance1);
ensure!(
block_height <= frame_system::Pallet::<T>::block_number(),
Error::<T>::BlockHeightInFuture
);
ensure!(
frame_system::pallet::BlockHash::<T>::contains_key(block_height),
Error::<T>::BlockHeightTooOld
);
if dest1_is_oneshot {
ensure!(
OneshotAccounts::<T>::get(&dest1).is_none(),
Error::<T>::OneshotAccountAlreadyCreated
);
ensure!(
balance1 >= T::ExistentialDeposit::get(),
Error::<T>::ExistentialDeposit
);
} else {
let dest1_data = frame_system::Account::<T>::get(&dest1).data;
ensure!(dest1_data.was_providing(), Error::<T>::DestAccountNotExist);
}
if dest2_is_oneshot {
ensure!(
OneshotAccounts::<T>::get(&dest2).is_none(),
Error::<T>::OneshotAccountAlreadyCreated
);
ensure!(
balance2 >= T::ExistentialDeposit::get(),
Error::<T>::ExistentialDeposit
);
OneshotAccounts::<T>::insert(&dest2, balance2);
Self::deposit_event(Event::OneshotAccountCreated {
account: dest2.clone(),
balance: balance2,
creator: transactor.clone(),
});
} else {
let dest2_data = frame_system::Account::<T>::get(&dest2).data;
ensure!(dest2_data.was_providing(), Error::<T>::DestAccountNotExist);
frame_system::Account::<T>::mutate(&dest2, |a| a.data.add_free(balance2));
}
if dest1_is_oneshot {
OneshotAccounts::<T>::insert(&dest1, balance1);
Self::deposit_event(Event::OneshotAccountCreated {
account: dest1.clone(),
balance: balance1,
creator: transactor.clone(),
});
} else {
frame_system::Account::<T>::mutate(&dest1, |a| a.data.add_free(balance1));
}
OneshotAccounts::<T>::remove(&transactor);
Self::deposit_event(Event::OneshotAccountConsumed {
account: transactor,
dest1: (dest1, balance1),
dest2: Some((dest2, balance2)),
});
Ok(())
}
Loading