Skip to content

Commit

Permalink
Fix tokens in api-server
Browse files Browse the repository at this point in the history
  • Loading branch information
azarovh committed May 15, 2024
1 parent 4c2e0e4 commit 4612e65
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 43 deletions.
45 changes: 26 additions & 19 deletions api-server/api-server-common/src/storage/impls/in_memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ use super::CURRENT_STORAGE_VERSION;
struct ApiServerInMemoryStorage {
block_table: BTreeMap<Id<Block>, BlockWithExtraData>,
block_aux_data_table: BTreeMap<Id<Block>, BlockAuxData>,
address_balance_table: BTreeMap<String, BTreeMap<BlockHeight, BTreeMap<CoinOrTokenId, Amount>>>,
address_locked_balance_table:
BTreeMap<String, BTreeMap<BlockHeight, BTreeMap<CoinOrTokenId, Amount>>>,
address_balance_table: BTreeMap<String, BTreeMap<(CoinOrTokenId, BlockHeight), Amount>>,
address_locked_balance_table: BTreeMap<String, BTreeMap<(CoinOrTokenId, BlockHeight), Amount>>,
address_transactions_table: BTreeMap<String, BTreeMap<BlockHeight, Vec<Id<Transaction>>>>,
delegation_table: BTreeMap<DelegationId, BTreeMap<BlockHeight, Delegation>>,
main_chain_blocks_table: BTreeMap<BlockHeight, Id<Block>>,
Expand Down Expand Up @@ -105,7 +104,10 @@ impl ApiServerInMemoryStorage {
self.address_balance_table.get(address).map_or_else(
|| Ok(None),
|balance| {
Ok(balance.last_key_value().and_then(|(_, v)| v.get(&coin_or_token_id).copied()))
let range_begin = (coin_or_token_id, BlockHeight::zero());
let range_end = (coin_or_token_id, BlockHeight::max());
let range = balance.range(range_begin..=range_end);
Ok(range.last().map(|(_, v)| *v))
},
)
}
Expand All @@ -118,7 +120,10 @@ impl ApiServerInMemoryStorage {
self.address_locked_balance_table.get(address).map_or_else(
|| Ok(None),
|balance| {
Ok(balance.last_key_value().and_then(|(_, v)| v.get(&coin_or_token_id).copied()))
let range_begin = (coin_or_token_id, BlockHeight::zero());
let range_end = (coin_or_token_id, BlockHeight::max());
let range = balance.range(range_begin..=range_end);
Ok(range.last().map(|(_, v)| *v))
},
)
}
Expand Down Expand Up @@ -573,12 +578,13 @@ impl ApiServerInMemoryStorage {

self.address_balance_table.iter_mut().for_each(|(_, balance)| {
balance
.range((Excluded(block_height), Unbounded))
.map(|b| b.0.into_int())
.iter()
.filter(|((_, height), _)| *height > block_height)
.map(|(key, _)| *key)
.collect::<Vec<_>>()
.iter()
.for_each(|&b| {
balance.remove(&BlockHeight::new(b));
.for_each(|key| {
balance.remove(key);
})
});

Expand All @@ -593,12 +599,13 @@ impl ApiServerInMemoryStorage {

self.address_locked_balance_table.iter_mut().for_each(|(_, balance)| {
balance
.range((Excluded(block_height), Unbounded))
.map(|b| b.0.into_int())
.iter()
.filter(|((_, height), _)| *height > block_height)
.map(|(key, _)| *key)
.collect::<Vec<_>>()
.iter()
.for_each(|&b| {
balance.remove(&BlockHeight::new(b));
.for_each(|key| {
balance.remove(key);
})
});

Expand Down Expand Up @@ -635,9 +642,9 @@ impl ApiServerInMemoryStorage {
self.address_balance_table
.entry(address.to_string())
.or_default()
.entry(block_height)
.or_default()
.insert(coin_or_token_id, amount);
.entry((coin_or_token_id, block_height))
.and_modify(|e| *e = amount)
.or_insert(amount);

Ok(())
}
Expand All @@ -652,9 +659,9 @@ impl ApiServerInMemoryStorage {
self.address_locked_balance_table
.entry(address.to_string())
.or_default()
.entry(block_height)
.or_default()
.insert(coin_or_token_id, amount);
.entry((coin_or_token_id, block_height))
.and_modify(|e| *e = amount)
.or_insert(amount);

Ok(())
}
Expand Down
33 changes: 28 additions & 5 deletions api-server/scanner-lib/src/blockchain_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ async fn update_locked_amounts_for_current_block<T: ApiServerStorageWrite>(
db_tx,
address,
amount,
CoinOrTokenId::Coin,
CoinOrTokenId::TokenId(*token_id),
block_height,
)
.await;
Expand Down Expand Up @@ -1075,12 +1075,25 @@ async fn update_tables_from_transaction_outputs<T: ApiServerStorageWrite>(
};
db_tx.set_fungible_token_issuance(token_id, block_height, issuance).await?;
}
TxOutput::IssueNft(token_id, issuance, _) => {
TxOutput::IssueNft(token_id, issuance, destination) => {
let address = Address::<Destination>::new(&chain_config, destination.clone())
.expect("Unable to encode destination");
address_transactions.entry(address.clone()).or_default().insert(transaction_id);

increase_address_amount(
db_tx,
&address,
&Amount::from_atoms(1),
CoinOrTokenId::TokenId(*token_id),
block_height,
)
.await;

db_tx.set_nft_token_issuance(*token_id, block_height, *issuance.clone()).await?;
set_utxo(
outpoint,
output,
Some(1),
None,
db_tx,
block_height,
false,
Expand Down Expand Up @@ -1363,7 +1376,12 @@ async fn decrease_address_amount<T: ApiServerStorageWrite>(
.expect("Unable to get balance")
.unwrap_or(Amount::ZERO);

let new_amount = current_balance.sub(*amount).expect("Balance should not overflow");
let new_amount = current_balance.sub(*amount).unwrap_or_else(|| {
panic!(
"Balance should not overflow {:?} {:?} {:?}",
coin_or_token_id, current_balance, *amount
)
});

db_tx
.set_address_balance_at_height(address.as_str(), new_amount, coin_or_token_id, block_height)
Expand All @@ -1384,7 +1402,12 @@ async fn decrease_address_locked_amount<T: ApiServerStorageWrite>(
.expect("Unable to get balance")
.unwrap_or(Amount::ZERO);

let new_amount = current_balance.sub(*amount).expect("Balance should not overflow");
let new_amount = current_balance.sub(*amount).unwrap_or_else(|| {
panic!(
"Balance should not overflow {:?} {:?} {:?}",
coin_or_token_id, current_balance, *amount
)
});

db_tx
.set_address_locked_balance_at_height(
Expand Down
2 changes: 1 addition & 1 deletion api-server/stack-test-suite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ common = { path = "../../common" }
crypto = { path = "../../crypto" }
randomness = { path = "../../randomness" }
serialization = { path = "../../serialization" }
test-utils = { path = "../../test-utils" }
utils = { path = "../../utils" }
pos-accounting = { path = "../../pos-accounting" }
node-comm = { path = "../../wallet/wallet-node-client" }
Expand All @@ -29,4 +30,3 @@ serde.workspace = true
serde_json.workspace = true
tokio = { workspace = true, features = ["full"] }
rstest.workspace = true
test-utils = { path = "../../test-utils" }
55 changes: 37 additions & 18 deletions api-server/stack-test-suite/tests/v2/nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@

use api_web_server::api::json_helpers::nft_issuance_data_to_json;
use common::{
chain::tokens::{make_token_id, NftIssuance, NftIssuanceV0, TokenId},
chain::tokens::{make_token_id, NftIssuance, TokenId},
primitives::H256,
};
use serialization::extras::non_empty_vec::DataOrNoVec;

use crate::DummyRPC;

Expand Down Expand Up @@ -83,22 +82,12 @@ async fn ok(#[case] seed: Seed) {

// generate addresses

let (_, alice_pk) = PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr);
let (alice_sk, alice_pk) =
PrivateKey::new_from_rng(&mut rng, KeyKind::Secp256k1Schnorr);

let alice_destination = Destination::PublicKeyHash(PublicKeyHash::from(&alice_pk));

let nft = NftIssuanceV0 {
metadata: common::chain::tokens::Metadata {
creator: None,
name: "Name".as_bytes().to_vec(),
description: "SomeNFT".as_bytes().to_vec(),
ticker: "XXXX".as_bytes().to_vec(),
icon_uri: DataOrNoVec::from(None),
additional_metadata_uri: DataOrNoVec::from(None),
media_uri: DataOrNoVec::from(None),
media_hash: "123456".as_bytes().to_vec(),
},
};
let nft = test_utils::nft_utils::random_nft_issuance(&chain_config, &mut rng);

let input = TxInput::from_utxo(
OutPointSourceId::BlockReward(tf.genesis().get_id().into()),
Expand All @@ -107,18 +96,48 @@ async fn ok(#[case] seed: Seed) {

let token_id = make_token_id(&[input.clone()]).unwrap();

let transaction = TransactionBuilder::new()
// issue NFT
let issue_nft_tx = TransactionBuilder::new()
.add_input(input, InputWitness::NoSignature(None))
.add_output(TxOutput::IssueNft(
token_id,
Box::new(NftIssuance::V0(nft.clone())),
alice_destination,
alice_destination.clone(),
))
.build();
let issue_nft_tx_id = issue_nft_tx.transaction().get_id();

// transfer NFT
let transfer_nft_tx = {
let tx = TransactionBuilder::new()
.add_input(
TxInput::from_utxo(issue_nft_tx_id.into(), 0),
InputWitness::NoSignature(None),
)
.add_output(TxOutput::Transfer(
OutputValue::TokenV1(token_id, Amount::from_atoms(1)),
alice_destination.clone(),
))
.build();

let sig = InputWitness::Standard(
StandardInputSignature::produce_uniparty_signature_for_input(
&alice_sk,
SigHashType::try_from(SigHashType::ALL).unwrap(),
alice_destination,
&tx,
&[issue_nft_tx.outputs().get(0)],
0,
&mut rng,
)
.unwrap(),
);
SignedTransaction::new(tx.take_transaction(), vec![sig]).unwrap()
};

let chainstate_block_ids = [*tf
.make_block_builder()
.add_transaction(transaction.clone())
.with_transactions(vec![issue_nft_tx, transfer_nft_tx])
.build_and_process(&mut rng)
.unwrap()
.unwrap()
Expand Down

0 comments on commit 4612e65

Please sign in to comment.