Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
[NFTs] Update attributes with offchain signature (#13390)
Browse files Browse the repository at this point in the history
* Allow to mint with the pre-signed signatures

* Another try

* WIP: test encoder

* Fix the deposits

* Refactoring + tests + benchmarks

* Add sp-core/runtime-benchmarks

* Remove sp-core from dev deps

* Enable full_crypto for benchmarks

* Typo

* Fix

* Update frame/nfts/src/mock.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts

* Add docs

* Add attributes into the pre-signed object & track the deposit owner for attributes

* Update docs

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts

* Add the number of attributes provided to weights

* Support pre-signed attributes

* Update docs

* Fix merge artifacts

* Update docs

* Add more tests

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_nfts

* Update frame/nfts/src/types.rs

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>

* Update types.rs

---------

Co-authored-by: Squirrel <gilescope@gmail.com>
Co-authored-by: command-bot <>
Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
  • Loading branch information
3 people authored and Ank4n committed Feb 28, 2023
1 parent bfd9202 commit 382484f
Show file tree
Hide file tree
Showing 6 changed files with 782 additions and 183 deletions.
50 changes: 50 additions & 0 deletions frame/nfts/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,5 +773,55 @@ benchmarks_instance_pallet! {
assert_last_event::<T, I>(Event::ItemMetadataSet { collection, item, data: metadata }.into());
}

set_attributes_pre_signed {
let n in 0 .. T::MaxAttributesPerCall::get() as u32;
let (collection, _, _) = create_collection::<T, I>();

let item_owner: T::AccountId = account("item_owner", 0, SEED);
let item_owner_lookup = T::Lookup::unlookup(item_owner.clone());

let signer_public = sr25519_generate(0.into(), None);
let signer: T::AccountId = MultiSigner::Sr25519(signer_public).into_account().into();

T::Currency::make_free_balance_be(&item_owner, DepositBalanceOf::<T, I>::max_value());

let item = T::Helper::item(0);
assert_ok!(Nfts::<T, I>::force_mint(
SystemOrigin::Root.into(),
collection,
item,
item_owner_lookup.clone(),
default_item_config(),
));

let mut attributes = vec![];
let attribute_value = vec![0u8; T::ValueLimit::get() as usize];
for i in 0..n {
let attribute_key = make_filled_vec(i as u16, T::KeyLimit::get() as usize);
attributes.push((attribute_key, attribute_value.clone()));
}
let pre_signed_data = PreSignedAttributes {
collection,
item,
attributes,
namespace: AttributeNamespace::Account(signer.clone()),
deadline: One::one(),
};
let message = Encode::encode(&pre_signed_data);
let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &signer_public, &message).unwrap());

frame_system::Pallet::<T>::set_block_number(One::one());
}: _(SystemOrigin::Signed(item_owner.clone()), pre_signed_data, signature.into(), signer.clone())
verify {
assert_last_event::<T, I>(
Event::PreSignedAttributesSet {
collection,
item,
namespace: AttributeNamespace::Account(signer.clone()),
}
.into(),
);
}

impl_benchmark_test_suite!(Nfts, crate::mock::new_test_ext(), crate::mock::Test);
}
53 changes: 53 additions & 0 deletions frame/nfts/src/features/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,59 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
Ok(())
}

pub(crate) fn do_set_attributes_pre_signed(
origin: T::AccountId,
data: PreSignedAttributesOf<T, I>,
signer: T::AccountId,
) -> DispatchResult {
let PreSignedAttributes { collection, item, attributes, namespace, deadline } = data;

ensure!(
attributes.len() <= T::MaxAttributesPerCall::get() as usize,
Error::<T, I>::MaxAttributesLimitReached
);

let now = frame_system::Pallet::<T>::block_number();
ensure!(deadline >= now, Error::<T, I>::DeadlineExpired);

let item_details =
Item::<T, I>::get(&collection, &item).ok_or(Error::<T, I>::UnknownItem)?;
ensure!(item_details.owner == origin, Error::<T, I>::NoPermission);

// Only the CollectionOwner and Account() namespaces could be updated in this way.
// For the Account() namespace we check and set the approval if it wasn't set before.
match &namespace {
AttributeNamespace::CollectionOwner => {},
AttributeNamespace::Account(account) => {
ensure!(account == &signer, Error::<T, I>::NoPermission);
let approvals = ItemAttributesApprovalsOf::<T, I>::get(&collection, &item);
if !approvals.contains(account) {
Self::do_approve_item_attributes(
origin.clone(),
collection,
item,
account.clone(),
)?;
}
},
_ => return Err(Error::<T, I>::WrongNamespace.into()),
}

for (key, value) in attributes {
Self::do_set_attribute(
signer.clone(),
collection,
Some(item),
namespace.clone(),
Self::construct_attribute_key(key)?,
Self::construct_attribute_value(value)?,
origin.clone(),
)?;
}
Self::deposit_event(Event::PreSignedAttributesSet { collection, item, namespace });
Ok(())
}

pub(crate) fn do_clear_attribute(
maybe_check_owner: Option<T::AccountId>,
collection: T::CollectionId,
Expand Down
35 changes: 35 additions & 0 deletions frame/nfts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,12 @@ pub mod pallet {
price: Option<PriceWithDirection<ItemPrice<T, I>>>,
deadline: <T as SystemConfig>::BlockNumber,
},
/// New attributes have been set for an `item` of the `collection`.
PreSignedAttributesSet {
collection: T::CollectionId,
item: T::ItemId,
namespace: AttributeNamespace<T::AccountId>,
},
}

#[pallet::error]
Expand Down Expand Up @@ -614,6 +620,8 @@ pub mod pallet {
IncorrectMetadata,
/// Can't set more attributes per one call.
MaxAttributesLimitReached,
/// The provided namespace isn't supported in this call.
WrongNamespace,
}

#[pallet::call]
Expand Down Expand Up @@ -1824,6 +1832,33 @@ pub mod pallet {
ensure!(signature.verify(&*msg, &signer), Error::<T, I>::WrongSignature);
Self::do_mint_pre_signed(origin, mint_data, signer)
}

/// Set attributes for an item by providing the pre-signed approval.
///
/// Origin must be Signed and must be an owner of the `data.item`.
///
/// - `data`: The pre-signed approval that consists of the information about the item,
/// attributes to update and until what block number.
/// - `signature`: The signature of the `data` object.
/// - `signer`: The `data` object's signer. Should be an owner of the collection for the
/// `CollectionOwner` namespace.
///
/// Emits `AttributeSet` for each provided attribute.
/// Emits `ItemAttributesApprovalAdded` if the approval wasn't set before.
/// Emits `PreSignedAttributesSet` on success.
#[pallet::call_index(38)]
#[pallet::weight(T::WeightInfo::set_attributes_pre_signed(data.attributes.len() as u32))]
pub fn set_attributes_pre_signed(
origin: OriginFor<T>,
data: PreSignedAttributesOf<T, I>,
signature: T::OffchainSignature,
signer: T::AccountId,
) -> DispatchResult {
let origin = ensure_signed(origin)?;
let msg = Encode::encode(&data);
ensure!(signature.verify(&*msg, &signer), Error::<T, I>::WrongSignature);
Self::do_set_attributes_pre_signed(origin, data, signer)
}
}
}

Expand Down
Loading

0 comments on commit 382484f

Please sign in to comment.