Skip to content

Commit

Permalink
[feature] hyperledger#2227: check mintability when registering asset
Browse files Browse the repository at this point in the history
Signed-off-by: Artemii Gerasimovich <gerasimovich@soramitsu.co.jp>
  • Loading branch information
Artemii Gerasimovich committed Jun 21, 2022
1 parent 32ec40a commit 5cdcadb
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 6 deletions.
59 changes: 59 additions & 0 deletions client/tests/integration/non_mintable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,62 @@ fn non_mintable_asset_can_be_minted_once_but_not_twice() -> Result<()> {
.is_err());
Ok(())
}

#[test]
fn non_mintable_asset_cannot_be_minted_if_registered_with_non_zero_value() -> Result<()> {
let (_rt, _peer, mut test_client) = <PeerBuilder>::new().start_with_runtime();
wait_for_genesis_committed(&vec![test_client.clone()], 0);

// Given
let account_id = AccountId::from_str("alice@wonderland").expect("Valid");
let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid");
let create_asset =
RegisterBox::new(AssetDefinition::quantity(asset_definition_id.clone()).mintable_once());

let asset_id = AssetId::new(asset_definition_id.clone(), account_id.clone());
let register_asset = RegisterBox::new(Asset::new(asset_id.clone(), 1_u32));

// We can register the non-mintable token
test_client.submit_all([create_asset.into(), register_asset.clone().into()])?;
test_client.poll_request(client::asset::by_account_id(account_id), |result| {
result.iter().any(|asset| {
asset.id().definition_id == asset_definition_id
&& *asset.value() == AssetValue::Quantity(1_u32)
})
})?;

// But only once
assert!(test_client.submit_blocking(register_asset).is_err());

// And can't be minted
let mint = MintBox::new(Value::U32(1_u32), IdBox::AssetId(asset_id));
assert!(test_client.submit_blocking(mint).is_err());

Ok(())
}

#[test]
fn non_mintable_asset_can_be_minted_if_registered_with_zero_value() -> Result<()> {
let (_rt, _peer, mut test_client) = <PeerBuilder>::new().start_with_runtime();
wait_for_genesis_committed(&vec![test_client.clone()], 0);

// Given
let account_id = AccountId::from_str("alice@wonderland").expect("Valid");
let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid");
let create_asset =
RegisterBox::new(AssetDefinition::quantity(asset_definition_id.clone()).mintable_once());

let asset_id = AssetId::new(asset_definition_id.clone(), account_id.clone());
let register_asset = RegisterBox::new(Asset::new(asset_id.clone(), 0_u32));
let mint = MintBox::new(Value::U32(1_u32), IdBox::AssetId(asset_id));

// We can register the non-mintable token wih zero value and then mint it
test_client.submit_all([create_asset.into(), register_asset.into(), mint.into()])?;
test_client.poll_request(client::asset::by_account_id(account_id), |result| {
result.iter().any(|asset| {
asset.id().definition_id == asset_definition_id
&& *asset.value() == AssetValue::Quantity(1_u32)
})
})?;
Ok(())
}
41 changes: 35 additions & 6 deletions core/src/smartcontracts/isi/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub mod isi {

match wsv.asset(asset_id) {
Err(e) if matches!(e, FindError::Asset(_)) => {
assert_can_register(&asset_id.definition_id, wsv, self.object.value())?;

if wsv
.asset_or_insert(asset_id, self.object.value().clone())
.is_err()
Expand Down Expand Up @@ -355,6 +357,19 @@ pub mod isi {
}
}

/// Forbid minting asset with definition [`definition_id`]
fn forbid_minting(
definition_id: &AssetDefinitionId,
wsv: &WorldStateView,
) -> Result<(), Error> {
wsv.modify_asset_definition_entry(definition_id, |entry| {
entry.forbid_minting()?;
Ok(AssetDefinitionEvent::MintabilityChanged(
definition_id.clone(),
))
})
}

/// Assert that this asset is `mintable`.
fn assert_can_mint(
definition_id: &AssetDefinitionId,
Expand All @@ -366,12 +381,26 @@ pub mod isi {
Mintable::Infinitely => Ok(()),
Mintable::Not => Err(Error::Mintability(MintabilityError::MintUnmintable)),
Mintable::Once => {
wsv.modify_asset_definition_entry(definition_id, |entry| {
entry.forbid_minting()?;
Ok(AssetDefinitionEvent::MintabilityChanged(
definition_id.clone(),
))
})?;
forbid_minting(definition_id, wsv)?;
Ok(())
}
}
}

/// Assert that this asset can be registered to an account.
fn assert_can_register(
definition_id: &AssetDefinitionId,
wsv: &WorldStateView,
value: &AssetValue,
) -> Result<(), Error> {
let definition = assert_asset_type(definition_id, wsv, value.value_type())?;
match definition.mintable() {
Mintable::Infinitely => Ok(()),
Mintable::Not => Err(Error::Mintability(MintabilityError::MintUnmintable)),
Mintable::Once => {
if !value.is_zero_value() {
forbid_minting(definition_id, wsv)?;
}
Ok(())
}
}
Expand Down

0 comments on commit 5cdcadb

Please sign in to comment.