Skip to content

Commit

Permalink
delete_token extrinsic and token cleanup (#143)
Browse files Browse the repository at this point in the history
* First pass at delete token feature and cleanup

* Tests for on_idle

* Update readme and weights

* Fix various weight issues

* Update utxo_nft weights

* fix typo
  • Loading branch information
mattdean-digicatapult authored Sep 21, 2023
1 parent 9ab3dfc commit 3aa65df
Show file tree
Hide file tree
Showing 14 changed files with 624 additions and 65 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

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

41 changes: 26 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,25 @@ For `dev` chain, the network only contains a node for `Alice` so other nodes wil

### Calculating weights

Extrinsic calls in `dscp-node` are weighted to ensure blocks are filled appropriately. For production weight calculations please run benchmarks on an AWS `t3a.2xlarge` instance.

To calculate the weights for a pallet you first must ensure the node is built with the benchmarking feature enabled:

```bash
cargo build --release --features runtime-benchmarks
cargo build --profile=production --features runtime-benchmarks
```

Then you can run the benchmark tool with for example

```bash
./target/release/dscp-node benchmark pallet \
./target/production/dscp-node benchmark pallet \
--pallet 'pallet_utxo_nft' \
--extrinsic '*' \
--repeat 1000 \
--output ./weights/
```

The generated weights implementation should then be integrated into the `pallet_utxo_nft` module.
The generated weights implementation should then be integrated into the relevant pallet.

### Upgrading Substrate

Expand All @@ -106,28 +108,37 @@ The node can be interacted with using [`@polkadot/api`](https://www.npmjs.com/pa

The `UtxoNFT` pallet exposes an extrinsic for minting/burning tokens and a storage format that allows their retrieval.

Note: The json object with types, described above, has been upgraded from `"Address": "AccountId", "LookupSource": "AccountId"` to `"Address": "MultiAddress", "LookupSource": "MultiAddress"` and it also needs to be used in conjunction with the new version of _PolkaDot JS_, **v4.7.2** or higher.

Two storage endpoints are then exposed under `UtxoNFT` for the id of the last token issued (`LastToken`) and a mapping of tokens by id (`TokensById`):
Four storage endpoints are then exposed under `UtxoNFT` for: the id of the last token issued (`LastToken`), a mapping of tokens by id (`TokensById`), a map of burnt tokens to be cleaned up (`Graveyard`) and the current status of the graveyard describing where it starts and ends (`CurrentGraveyardState`):

```rust
LastToken get(fn last_token): T::TokenId;
TokensById get(fn tokens_by_id): map T::TokenId => Token<T::AccountId, T::RoleKey, T::TokenId, T::BlockNumber, T::TokenMetadataKey, T::TokenMetadataValue>;
LastToken<T: Config> = StorageValue<_, T::TokenId, ValueQuery>;
TokensById<T: Config> = StorageMap<_, Blake2_128Concat, T::TokenId, Token<T>, OptionQuery>;
Graveyard<T: Config> = StorageMap<_, Blake2_128Concat, u64, T::TokenId, OptionQuery>;
CurrentGraveyardState<T: Config> = StorageValue<_, GraveyardState, ValueQuery>;
```

Tokens can be minted/burnt by calling the following extrinsic under `UtxoNFT`:

```rust
pub fn run_process(
origin: OriginFor<T>,
process: Option<ProcessId<T>>
inputs: Vec<T::TokenId>,
outputs: Vec<
Output<T::AccountId, T::RoleKey, T::TokenMetadataKey, T::TokenMetadataValue>
>,
) -> dispatch::DispatchResult { ... }
origin: OriginFor<T>,
process: ProcessId<T>,
inputs: BoundedVec<T::TokenId, T::MaxInputCount>,
outputs: BoundedVec<Output<T>, T::MaxOutputCount>
) -> DispatchResultWithPostInfo { ... }
```

And tokens that have been burnt from the system a sufficiently long time ago (runtime specifies 7 days) can be permanently deleted with:

```rust
pub fn delete_token(
origin: OriginFor<T>,
token_id: <T as Config>::TokenId
) -> DispatchResultWithPostInfo { ... }
```

Note that deletion of tokens will occur automatically in idle block time after the configured time as well.

All of this functionality can be easily accessed using [https://polkadot.js.org/apps](https://polkadot.js.org/apps) against a running `dev` node. You will need to add a network endpoint of `ws://localhost:9944` under `Settings` and apply the above type configurations in the `Settings/Developer` tab.

Pallet tests can be run with:
Expand Down
2 changes: 1 addition & 1 deletion node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = '2021'
license = 'Apache-2.0'
repository = 'https://github.com/digicatapult/dscp-node/'
name = 'dscp-node'
version = '9.0.0'
version = '9.1.0'

[[bin]]
name = 'dscp-node'
Expand Down
6 changes: 3 additions & 3 deletions pallets/utxo-nft/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = '2021'
license = 'Apache-2.0'
repository = 'https://github.com/digicatapult/dscp-node/'
name = 'pallet-utxo-nft'
version = "6.0.1"
version = "6.1.0"


[package.metadata.docs.rs]
Expand All @@ -25,7 +25,7 @@ frame-benchmarking = { default-features = false, version = "4.0.0-dev", git = "h
sp-runtime = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
sp-io = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }
sp-std = { default-features = false, version = "5.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" }

sp-core = { default-features = false, version = "7.0.0", git = "https://github.com/paritytech/substrate.git", optional = true, branch = "polkadot-v0.9.42" }

[dev-dependencies]
serde = { version = "1.0.159" }
Expand All @@ -42,5 +42,5 @@ std = [
'sp-std/std',
'dscp-pallet-traits/std',
]
runtime-benchmarks = ['frame-benchmarking']
runtime-benchmarks = ['frame-benchmarking', 'sp-core']
try-runtime = ["frame-support/try-runtime"]
33 changes: 30 additions & 3 deletions pallets/utxo-nft/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ Benchmarking setup for pallet-template
*/
use super::*;

use core::convert::TryFrom;
use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite};
use frame_support::{traits::ConstU32, BoundedBTreeMap, BoundedVec};
use frame_system::RawOrigin;
use frame_system::{Pallet as System, RawOrigin};
use sp_runtime::traits::Bounded;
use sp_std::convert::TryFrom;
use sp_std::vec::Vec;

use dscp_pallet_traits::{ProcessFullyQualifiedId, ProcessValidator};
Expand Down Expand Up @@ -146,6 +147,32 @@ benchmarks! {
verify {
assert_eq!(LastToken::<T>::get(), nth_token_id::<T>(i + o)?);
}

delete_token {
let token_id: T::TokenId = 1u32.into();
let caller: T::AccountId = account("owner", 0, SEED);

add_nfts::<T>(1)?;
let inputs = mk_inputs::<T>(1)?;
let outputs = mk_outputs::<T>(0)?;
let default_process = BoundedVec::<u8, ConstU32<32>>::try_from("default".as_bytes().to_vec()).unwrap();
let process = ProcessFullyQualifiedId {
id: default_process.into(),
version: 1u32.into()
};
UtxoNFT::<T>::run_process(
RawOrigin::Signed(caller.clone()).into(),
process,
inputs,
outputs
)?;

System::<T>::set_block_number(T::BlockNumber::max_value());
assert_eq!(TokensById::<T>::get(token_id).is_none(), false);
}: _(RawOrigin::Signed(caller), token_id)
verify {
assert_eq!(TokensById::<T>::get(token_id).is_none(), true);
}
}

impl_benchmark_test_suite!(UtxoNFT, crate::mock::new_test_ext(), crate::mock::Test,);
impl_benchmark_test_suite!(UtxoNFT, crate::tests::mock::new_test_ext(), crate::tests::mock::Test,);
10 changes: 10 additions & 0 deletions pallets/utxo-nft/src/graveyard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use codec::MaxEncodedLen;
use codec::{Decode, Encode};
use frame_support::RuntimeDebug;
use scale_info::TypeInfo;

#[derive(Encode, Decode, Default, RuntimeDebug, MaxEncodedLen, TypeInfo, Clone, PartialEq)]
pub struct GraveyardState {
pub(crate) start_index: u64,
pub(crate) end_index: u64
}
Loading

0 comments on commit 3aa65df

Please sign in to comment.