Skip to content

Commit

Permalink
feat(runtime): Add ability to decline given voucher (#3725)
Browse files Browse the repository at this point in the history
  • Loading branch information
breathx committed Feb 12, 2024
1 parent 1f87dab commit 33ee05d
Show file tree
Hide file tree
Showing 13 changed files with 420 additions and 25 deletions.
15 changes: 15 additions & 0 deletions gsdk/src/metadata/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2862,6 +2862,8 @@ pub mod runtime_types {
UploadCode {
code: ::std::vec::Vec<::core::primitive::u8>,
},
#[codec(index = 3)]
DeclineVoucher,
}
#[derive(Debug, crate::gp::Decode, crate::gp::DecodeAsType, crate::gp::Encode)]
pub struct VoucherId(pub [::core::primitive::u8; 32usize]);
Expand Down Expand Up @@ -2927,6 +2929,11 @@ pub mod runtime_types {
::core::primitive::u128,
>,
},
#[codec(index = 5)]
#[doc = "See [`Pallet::decline`]."]
decline {
voucher_id: runtime_types::pallet_gear_voucher::internal::VoucherId,
},
}
#[derive(Debug, crate::gp::Decode, crate::gp::DecodeAsType, crate::gp::Encode)]
#[doc = "The `Error` enum of this pallet."]
Expand Down Expand Up @@ -2990,6 +2997,12 @@ pub mod runtime_types {
voucher_id: runtime_types::pallet_gear_voucher::internal::VoucherId,
new_owner: ::core::option::Option<::subxt::utils::AccountId32>,
},
#[codec(index = 3)]
#[doc = "Voucher has been declined (set to expired state)."]
VoucherDeclined {
spender: ::subxt::utils::AccountId32,
voucher_id: runtime_types::pallet_gear_voucher::internal::VoucherId,
},
}
}
}
Expand Down Expand Up @@ -7909,6 +7922,7 @@ pub mod calls {
Revoke,
Update,
CallDeprecated,
Decline,
}
impl CallInfo for GearVoucherCall {
const PALLET: &'static str = "GearVoucher";
Expand All @@ -7919,6 +7933,7 @@ pub mod calls {
Self::Revoke => "revoke",
Self::Update => "update",
Self::CallDeprecated => "call_deprecated",
Self::Decline => "decline",
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions pallets/gear-voucher/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,35 @@ benchmarks! {
assert_eq!(voucher_info.programs.map(|v| v.len()), Some(<<T as Config>::MaxProgramsAmount as Get<u8>>::get() as usize));
assert_eq!(CurrencyOf::<T>::free_balance(&voucher_id.cast::<T::AccountId>()), balance * 2u128.unique_saturated_into());
}

decline {
// Origin account.
let origin = benchmarking::account::<T::AccountId>("origin", 0, 0);
let _ = CurrencyOf::<T>::deposit_creating(
&origin,
100_000_000_000_000_u128.unique_saturated_into()
);

// Spender account.
let spender = benchmarking::account::<T::AccountId>("spender", 0, 1);

// Voucher balance.
let balance = 10_000_000_000_000_u128.unique_saturated_into();

// Forbid uploading codes.
let code_uploading = false;

// Voucher validity.
let validity = <<T as Config>::MinDuration as Get<BlockNumberFor<T>>>::get();

// Issue voucher.
assert!(Pallet::<T>::issue(RawOrigin::Signed(origin.clone()).into(), spender.clone(), balance, None, code_uploading, validity).is_ok());
let (_, voucher_id, _) = Vouchers::<T>::iter().next().expect("Couldn't find voucher");
}: _(RawOrigin::Signed(spender.clone()), voucher_id)
verify {
let voucher_info = Vouchers::<T>::get(spender, voucher_id).expect("Must be");
assert_eq!(voucher_info.expiry, frame_system::Pallet::<T>::block_number());
}
}

impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
30 changes: 22 additions & 8 deletions pallets/gear-voucher/src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use common::{
storage::{Counter, CounterImpl, Mailbox},
Origin,
};
use frame_system::pallet_prelude::BlockNumberFor;
use gear_core::{declare_id, ids};
use sp_std::collections::btree_set::BTreeSet;

Expand Down Expand Up @@ -67,6 +68,22 @@ where
}

impl<T: Config> Pallet<T> {
/// Queries a voucher and asserts its validity.
pub fn get_active_voucher(
origin: AccountIdOf<T>,
voucher_id: VoucherId,
) -> Result<VoucherInfo<AccountIdOf<T>, BlockNumberFor<T>>, Error<T>> {
let voucher =
Vouchers::<T>::get(origin.clone(), voucher_id).ok_or(Error::<T>::InexistentVoucher)?;

ensure!(
<frame_system::Pallet<T>>::block_number() < voucher.expiry,
Error::<T>::VoucherExpired
);

Ok(voucher)
}

/// Return the account id of a synthetical account used to sponsor gas
/// and transaction fee for legacy vouchers implementation.
#[deprecated = "Legacy voucher issuing logic is deprecated, and this and \
Expand All @@ -89,15 +106,10 @@ impl<T: Config> Pallet<T> {
voucher_id: VoucherId,
call: &PrepaidCall<BalanceOf<T>>,
) -> Result<(), Error<T>> {
let voucher =
Vouchers::<T>::get(origin.clone(), voucher_id).ok_or(Error::<T>::InexistentVoucher)?;

ensure!(
<frame_system::Pallet<T>>::block_number() < voucher.expiry,
Error::<T>::VoucherExpired
);
let voucher = Self::get_active_voucher(origin.clone(), voucher_id)?;

match call {
PrepaidCall::DeclineVoucher => (),
PrepaidCall::UploadCode { .. } => {
ensure!(voucher.code_uploading, Error::<T>::CodeUploadingDisabled)
}
Expand Down Expand Up @@ -127,7 +139,7 @@ impl<T: Config> Pallet<T> {
PrepaidCall::SendReply { reply_to_id, .. } => {
T::Mailbox::peek(who, reply_to_id).map(|stored_message| stored_message.source())
}
PrepaidCall::UploadCode { .. } => None,
PrepaidCall::UploadCode { .. } | PrepaidCall::DeclineVoucher => None,
}
}
}
Expand All @@ -144,6 +156,7 @@ pub trait PrepaidCallsDispatcher {
fn dispatch(
account_id: Self::AccountId,
sponsor_id: Self::AccountId,
voucher_id: VoucherId,
call: PrepaidCall<Self::Balance>,
) -> DispatchResultWithPostInfo;
}
Expand Down Expand Up @@ -217,4 +230,5 @@ pub enum PrepaidCall<Balance> {
UploadCode {
code: Vec<u8>,
},
DeclineVoucher,
}
55 changes: 49 additions & 6 deletions pallets/gear-voucher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,14 @@ pub mod pallet {
/// Optional field defining was the owner changed during update.
new_owner: Option<AccountIdOf<T>>,
},

/// Voucher has been declined (set to expired state).
VoucherDeclined {
/// Account id of user who declined its own voucher.
spender: AccountIdOf<T>,
/// Voucher identifier.
voucher_id: VoucherId,
},
}

// Pallet Gear Voucher error.
Expand Down Expand Up @@ -322,7 +330,7 @@ pub mod pallet {
Self::validate_prepaid(origin.clone(), voucher_id, &call)?;

// Dispatching of the call.
T::CallsDispatcher::dispatch(origin, voucher_id.cast(), call)
T::CallsDispatcher::dispatch(origin, voucher_id.cast(), voucher_id, call)
}

/// Revoke existing voucher.
Expand Down Expand Up @@ -559,18 +567,53 @@ pub mod pallet {
let origin = ensure_signed(origin)?;

// Validating the call for legacy implementation.
ensure!(
!matches!(call, PrepaidCall::UploadCode { .. }),
Error::<T>::CodeUploadingDisabled
);
match call {
PrepaidCall::UploadCode { .. } => {
return Err(Error::<T>::CodeUploadingDisabled.into())
}
PrepaidCall::DeclineVoucher => return Err(Error::<T>::InexistentVoucher.into()),
PrepaidCall::SendMessage { .. } | PrepaidCall::SendReply { .. } => (),
};

// Looking for sponsor synthetic account.
#[allow(deprecated)]
let sponsor = Self::call_deprecated_sponsor(&origin, &call)
.ok_or(Error::<T>::UnknownDestination)?;

// Dispatching call.
T::CallsDispatcher::dispatch(origin, sponsor, call)
T::CallsDispatcher::dispatch(origin, sponsor.clone(), sponsor.cast(), call)
}

/// Decline existing and not expired voucher.
///
/// This extrinsic expires voucher of the caller, if it's still active,
/// allowing it to be revoked.
///
/// Arguments:
/// * voucher_id: voucher id to be declined.
#[pallet::call_index(5)]
#[pallet::weight(T::WeightInfo::decline())]
pub fn decline(origin: OriginFor<T>, voucher_id: VoucherId) -> DispatchResultWithPostInfo {
// Ensuring origin.
let origin = ensure_signed(origin)?;

// Querying voucher if its not expired.
let mut voucher = Self::get_active_voucher(origin.clone(), voucher_id)?;

// Set voucher into expired state.
voucher.expiry = <frame_system::Pallet<T>>::block_number();

// Updating voucher in storage.
// TODO: consider revoke here once gas counting implemented (#3726).
Vouchers::<T>::insert(origin.clone(), voucher_id, voucher);

// Depositing event.
Self::deposit_event(Event::VoucherDeclined {
spender: origin,
voucher_id,
});

Ok(().into())
}
}
}
3 changes: 2 additions & 1 deletion pallets/gear-voucher/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

#![allow(unused)]

use crate as pallet_gear_voucher;
use crate::{self as pallet_gear_voucher, VoucherId};
use common::{
storage::{Interval, Mailbox},
Origin,
Expand Down Expand Up @@ -83,6 +83,7 @@ impl crate::PrepaidCallsDispatcher for () {
fn dispatch(
_account_id: Self::AccountId,
_sponsor_id: Self::AccountId,
_voucher_id: VoucherId,
_call: pallet_gear_voucher::PrepaidCall<Balance>,
) -> frame_support::pallet_prelude::DispatchResultWithPostInfo {
Ok(().into())
Expand Down
62 changes: 62 additions & 0 deletions pallets/gear-voucher/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,68 @@ fn voucher_update_err_cases() {
});
}

#[test]
fn voucher_decline_works() {
new_test_ext().execute_with(|| {
assert_ok!(Voucher::issue(
RuntimeOrigin::signed(ALICE),
BOB,
DEFAULT_BALANCE,
None,
false,
DEFAULT_VALIDITY,
));

let voucher_id = utils::get_last_voucher_id();

assert_ok!(Voucher::decline(RuntimeOrigin::signed(BOB), voucher_id));

System::assert_last_event(
Event::VoucherDeclined {
spender: BOB,
voucher_id,
}
.into(),
);

let new_expiry = Vouchers::<Test>::get(BOB, voucher_id)
.expect("Couldn't find voucher")
.expiry;

assert_eq!(new_expiry, System::block_number());
});
}

#[test]
fn voucher_decline_err_cases() {
new_test_ext().execute_with(|| {
// Voucher doesn't exist.
assert_noop!(
Voucher::decline(RuntimeOrigin::signed(BOB), H256::random().cast()),
Error::<Test>::InexistentVoucher
);

// Voucher has already expired.
assert_ok!(Voucher::issue(
RuntimeOrigin::signed(ALICE),
BOB,
DEFAULT_BALANCE,
None,
false,
DEFAULT_VALIDITY,
));

let voucher_id = utils::get_last_voucher_id();

System::set_block_number(System::block_number() + 10 * DEFAULT_VALIDITY);

assert_noop!(
Voucher::decline(RuntimeOrigin::signed(BOB), voucher_id),
Error::<Test>::VoucherExpired
);
});
}

mod utils {
use super::*;
use frame_support::dispatch::DispatchErrorWithPostInfo;
Expand Down
19 changes: 19 additions & 0 deletions pallets/gear-voucher/src/weights.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 33ee05d

Please sign in to comment.