Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to change token's metadata URI #1805

Merged
merged 13 commits into from
Sep 2, 2024
5 changes: 5 additions & 0 deletions api-server/api-server-common/src/storage/storage_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,11 @@ impl FungibleTokenData {
self.authority = authority;
self
}

pub fn change_metadata_uri(mut self, metadata_uri: Vec<u8>) -> Self {
self.metadata_uri = metadata_uri;
self
}
}

#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
Expand Down
25 changes: 25 additions & 0 deletions api-server/scanner-lib/src/blockchain_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ async fn calculate_fees<T: ApiServerStorageWrite>(
| AccountCommand::UnmintTokens(token_id)
| AccountCommand::UnfreezeToken(token_id)
| AccountCommand::LockTokenSupply(token_id)
| AccountCommand::ChangeTokenMetadataUri(token_id, _)
| AccountCommand::ChangeTokenAuthority(token_id, _) => Some(*token_id),
AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => None,
},
Expand Down Expand Up @@ -1138,6 +1139,30 @@ async fn update_tables_from_transaction_inputs<T: ApiServerStorageWrite>(
)
.await;
}
AccountCommand::ChangeTokenMetadataUri(token_id, metadata_uri) => {
let issuance =
db_tx.get_fungible_token_issuance(*token_id).await?.expect("must exist");

let issuance = issuance.change_metadata_uri(metadata_uri.clone());
db_tx.set_fungible_token_issuance(*token_id, block_height, issuance).await?;
let amount = chain_config.token_change_metadata_uri_fee();
increase_statistic_amount(
db_tx,
CoinOrTokenStatistic::Burned,
&amount,
CoinOrTokenId::Coin,
block_height,
)
.await;
decrease_statistic_amount(
db_tx,
CoinOrTokenStatistic::CirculatingSupply,
&amount,
CoinOrTokenId::Coin,
block_height,
)
.await;
}
AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => {
// TODO(orders)
}
Expand Down
5 changes: 5 additions & 0 deletions api-server/scanner-lib/src/sync/tests/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@ async fn simulation(
chain_config.token_change_authority_fee(block_height);
burn_coins(&mut statistics, token_change_authority_fee);
}
AccountCommand::ChangeTokenMetadataUri(_token_id, _) => {
let token_change_metadata_fee =
chain_config.token_change_metadata_uri_fee();
burn_coins(&mut statistics, token_change_metadata_fee);
}
AccountCommand::ConcludeOrder(_) | AccountCommand::FillOrder(_, _, _) => {
unimplemented!() // TODO(orders)
}
Expand Down
8 changes: 8 additions & 0 deletions api-server/web-server/src/api/json_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,14 @@ pub fn tx_input_to_json(
"destination": Address::new(chain_config, dest.clone()).expect("no error").as_str(),
})
}
AccountCommand::ChangeTokenMetadataUri(token_id, metadata_uri) => {
json!({
"input_type": "AccountCommand",
"command": "ChangeTokenMetadataUri",
"token_id": Address::new(chain_config, *token_id).expect("addressable").to_string(),
"metadata_uri": metadata_uri,
})
}
},
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ impl ConstrainedValueAccumulator {
CoinOrTokenId::Coin,
chain_config.token_change_authority_fee(block_height),
)),
AccountCommand::ChangeTokenMetadataUri(_, _) => Ok((
CoinOrTokenId::Coin,
chain_config.token_change_metadata_uri_fee(),
)),
AccountCommand::ConcludeOrder(id) => {
let order_data = orders_accounting_delta
.get_order_data(id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ fn calculate_fee_for_token_issuance(#[case] seed: Seed) {
0,
))];
let input_utxos = vec![Some(TxOutput::Transfer(
OutputValue::Coin((token_issuance_fee * 2).unwrap()),
OutputValue::Coin(token_issuance_fee),
Destination::AnyoneCanSpend,
))];

Expand Down Expand Up @@ -781,7 +781,7 @@ fn calculate_fee_for_token_issuance(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(token_issuance_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}

// Check that token supply change fee is not accounted in accumulated fee and is burned rather then goes to staker.
Expand Down Expand Up @@ -833,7 +833,7 @@ fn calculate_token_supply_change_fee(#[case] seed: Seed) {
let input_utxos = vec![
None,
Some(TxOutput::Transfer(
OutputValue::Coin((supply_change_fee * 2).unwrap()),
OutputValue::Coin(supply_change_fee),
Destination::AnyoneCanSpend,
)),
];
Expand Down Expand Up @@ -862,7 +862,7 @@ fn calculate_token_supply_change_fee(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(supply_change_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}

// Unmint
Expand All @@ -877,7 +877,7 @@ fn calculate_token_supply_change_fee(#[case] seed: Seed) {
let input_utxos = vec![
None,
Some(TxOutput::Transfer(
OutputValue::Coin((supply_change_fee * 2).unwrap()),
OutputValue::Coin(supply_change_fee),
Destination::AnyoneCanSpend,
)),
];
Expand Down Expand Up @@ -906,7 +906,7 @@ fn calculate_token_supply_change_fee(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(supply_change_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}

// Lock supply
Expand All @@ -924,7 +924,7 @@ fn calculate_token_supply_change_fee(#[case] seed: Seed) {
let input_utxos = vec![
None,
Some(TxOutput::Transfer(
OutputValue::Coin((supply_change_fee * 2).unwrap()),
OutputValue::Coin(supply_change_fee),
Destination::AnyoneCanSpend,
)),
];
Expand Down Expand Up @@ -953,7 +953,7 @@ fn calculate_token_supply_change_fee(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(supply_change_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}
}

Expand Down Expand Up @@ -1009,7 +1009,7 @@ fn calculate_token_fee_freeze(#[case] seed: Seed) {
let input_utxos = vec![
None,
Some(TxOutput::Transfer(
OutputValue::Coin((supply_change_fee * 2).unwrap()),
OutputValue::Coin(supply_change_fee),
Destination::AnyoneCanSpend,
)),
];
Expand Down Expand Up @@ -1038,7 +1038,7 @@ fn calculate_token_fee_freeze(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(supply_change_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}

// Unfreeze
Expand All @@ -1056,7 +1056,7 @@ fn calculate_token_fee_freeze(#[case] seed: Seed) {
let input_utxos = vec![
None,
Some(TxOutput::Transfer(
OutputValue::Coin((supply_change_fee * 2).unwrap()),
OutputValue::Coin(supply_change_fee),
Destination::AnyoneCanSpend,
)),
];
Expand Down Expand Up @@ -1085,7 +1085,7 @@ fn calculate_token_fee_freeze(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(supply_change_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}
}

Expand Down Expand Up @@ -1136,7 +1136,7 @@ fn calculate_token_fee_change_authority(#[case] seed: Seed) {
let input_utxos = vec![
None,
Some(TxOutput::Transfer(
OutputValue::Coin((supply_change_fee * 2).unwrap()),
OutputValue::Coin(supply_change_fee),
Destination::AnyoneCanSpend,
)),
];
Expand Down Expand Up @@ -1164,7 +1164,7 @@ fn calculate_token_fee_change_authority(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(supply_change_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}

// Check that data deposit fee is not accounted in accumulated fee and is burned rather then goes to staker.
Expand Down Expand Up @@ -1194,7 +1194,7 @@ fn calculate_data_deposit_fee(#[case] seed: Seed) {
0,
))];
let input_utxos = vec![Some(TxOutput::Transfer(
OutputValue::Coin((data_deposit_fee * 2).unwrap()),
OutputValue::Coin(data_deposit_fee),
Destination::AnyoneCanSpend,
))];

Expand All @@ -1220,7 +1220,7 @@ fn calculate_data_deposit_fee(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(data_deposit_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}

// Check that nft issuance fee is not accounted in accumulated fee and is burned rather then goes to staker.
Expand Down Expand Up @@ -1250,7 +1250,7 @@ fn calculate_nft_issuance_fee(#[case] seed: Seed) {
0,
))];
let input_utxos = vec![Some(TxOutput::Transfer(
OutputValue::Coin((nft_issuance_fee * 2).unwrap()),
OutputValue::Coin(nft_issuance_fee),
Destination::AnyoneCanSpend,
))];

Expand Down Expand Up @@ -1281,5 +1281,83 @@ fn calculate_nft_issuance_fee(#[case] seed: Seed) {
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(nft_issuance_fee));
assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}

// Check that token metadata change fee is not accounted in accumulated fee and is burned rather then goes to staker.
#[rstest]
#[trace]
#[case(Seed::from_entropy())]
fn calculate_token_fee_change_metadata_uri(#[case] seed: Seed) {
let mut rng = make_seedable_rng(seed);

let chain_config = common::chain::config::Builder::new(ChainType::Mainnet)
.consensus_upgrades(NetUpgrades::regtest_with_pos())
.build();
let block_height = BlockHeight::new(1);
let change_metadata_uri_fee = chain_config.token_change_metadata_uri_fee();

let pos_store = InMemoryPoSAccounting::new();
let pos_db = PoSAccountingDB::new(&pos_store);

let orders_store = InMemoryOrdersAccounting::new();
let orders_db = OrdersAccountingDB::new(&orders_store);

let token_data = tokens_accounting::TokenData::FungibleToken(
TokenIssuance::V1(test_utils::nft_utils::random_token_issuance_v1(
&chain_config,
Destination::AnyoneCanSpend,
&mut rng,
))
.into(),
);
let token_id = TokenId::random_using(&mut rng);
let tokens_store = tokens_accounting::InMemoryTokensAccounting::from_values(
BTreeMap::from_iter([(token_id, token_data)]),
BTreeMap::new(),
);
let tokens_db = tokens_accounting::TokensAccountingDB::new(&tokens_store);

let inputs = vec![
TxInput::AccountCommand(
AccountNonce::new(0),
AccountCommand::ChangeTokenMetadataUri(token_id, Vec::new()),
),
TxInput::Utxo(UtxoOutPoint::new(
OutPointSourceId::BlockReward(Id::new(H256::random_using(&mut rng))),
0,
)),
];
let input_utxos = vec![
None,
Some(TxOutput::Transfer(
OutputValue::Coin(change_metadata_uri_fee),
Destination::AnyoneCanSpend,
)),
];

let outputs =
vec![TxOutput::Transfer(OutputValue::Coin(Amount::ZERO), Destination::AnyoneCanSpend)];

let inputs_accumulator = ConstrainedValueAccumulator::from_inputs(
&chain_config,
block_height,
&orders_db,
&pos_db,
&tokens_db,
&inputs,
&input_utxos,
)
.unwrap();

let outputs_accumulator =
ConstrainedValueAccumulator::from_outputs(&chain_config, block_height, &outputs).unwrap();

let accumulated_fee = inputs_accumulator
.satisfy_with(outputs_accumulator)
.unwrap()
.map_into_block_fees(&chain_config, block_height)
.unwrap();

assert_eq!(accumulated_fee, Fee(Amount::ZERO));
}
4 changes: 4 additions & 0 deletions chainstate/src/detail/ban_score.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ impl BanScore for TokensError {
TokensError::TokensInBlockReward => 100,
TokensError::InvariantBrokenUndoIssuanceOnNonexistentToken(_) => 100,
TokensError::InvariantBrokenRegisterIssuanceWithDuplicateId(_) => 100,
TokensError::TokenMetadataUriTooLarge(_) => 100,
}
}
}
Expand Down Expand Up @@ -364,6 +365,7 @@ impl BanScore for CheckTransactionError {
CheckTransactionError::HtlcsAreNotActivated => 100,
CheckTransactionError::OrdersAreNotActivated(_) => 100,
CheckTransactionError::OrdersCurrenciesMustBeDifferent(_) => 100,
CheckTransactionError::ChangeTokenMetadataUriNotActivated => 100,
}
}
}
Expand Down Expand Up @@ -629,6 +631,8 @@ impl BanScore for tokens_accounting::Error {
tokens_accounting::Error::CannotLockFrozenToken(_) => 100,
tokens_accounting::Error::CannotChangeAuthorityForFrozenToken(_) => 100,
tokens_accounting::Error::CannotUndoChangeAuthorityForFrozenToken(_) => 100,
tokens_accounting::Error::CannotChangeMetadataUriForFrozenToken(_) => 100,
tokens_accounting::Error::CannotUndoChangeMetadataUriForFrozenToken(_) => 100,
tokens_accounting::Error::InvariantErrorNonZeroSupplyForNonExistingToken => 100,
tokens_accounting::Error::ViewFail => 0,
tokens_accounting::Error::StorageWrite => 0,
Expand Down
4 changes: 4 additions & 0 deletions chainstate/src/detail/error_classification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ impl BlockProcessingErrorClassification for TokensError {
| TokensError::TransferZeroTokens(_, _)
| TokensError::TokenIdCantBeCalculated
| TokensError::TokensInBlockReward
| TokensError::TokenMetadataUriTooLarge(_)
| TokensError::InvariantBrokenUndoIssuanceOnNonexistentToken(_)
| TokensError::InvariantBrokenRegisterIssuanceWithDuplicateId(_) => {
BlockProcessingErrorClass::BadBlock
Expand Down Expand Up @@ -742,6 +743,8 @@ impl BlockProcessingErrorClassification for tokens_accounting::Error {
| Error::CannotUndoUnfreezeTokenThatIsFrozen(_)
| Error::CannotChangeAuthorityForFrozenToken(_)
| Error::CannotUndoChangeAuthorityForFrozenToken(_)
| Error::CannotChangeMetadataUriForFrozenToken(_)
| Error::CannotUndoChangeMetadataUriForFrozenToken(_)
| Error::InvariantErrorNonZeroSupplyForNonExistingToken => {
BlockProcessingErrorClass::BadBlock
}
Expand Down Expand Up @@ -797,6 +800,7 @@ impl BlockProcessingErrorClassification for CheckTransactionError {
| CheckTransactionError::DeprecatedTokenOperationVersion(_, _)
| CheckTransactionError::HtlcsAreNotActivated
| CheckTransactionError::OrdersAreNotActivated(_)
| CheckTransactionError::ChangeTokenMetadataUriNotActivated
| CheckTransactionError::OrdersCurrenciesMustBeDifferent(_) => {
BlockProcessingErrorClass::BadBlock
}
Expand Down
Loading
Loading