Skip to content

Commit 45a8920

Browse files
committed
add test for mint input amount decimals
1 parent ffe241f commit 45a8920

File tree

2 files changed

+196
-13
lines changed

2 files changed

+196
-13
lines changed

api-server/stack-test-suite/tests/v2/transaction.rs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,3 +434,186 @@ async fn ok(#[case] seed: Seed) {
434434

435435
task.abort();
436436
}
437+
438+
#[rstest]
439+
#[trace]
440+
#[case(Seed::from_entropy())]
441+
#[tokio::test]
442+
async fn mint_tokens(#[case] seed: Seed) {
443+
use chainstate_test_framework::empty_witness;
444+
use common::chain::{
445+
make_token_id,
446+
tokens::{TokenIssuance, TokenTotalSupply},
447+
AccountCommand, AccountNonce, UtxoOutPoint,
448+
};
449+
450+
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
451+
let addr = listener.local_addr().unwrap();
452+
453+
let (tx, rx) = tokio::sync::oneshot::channel();
454+
455+
let task = tokio::spawn(async move {
456+
let web_server_state = {
457+
let mut rng = make_seedable_rng(seed);
458+
let chain_config = create_unit_test_config();
459+
460+
let chainstate_blocks = {
461+
let mut tf = TestFramework::builder(&mut rng)
462+
.with_chain_config(chain_config.clone())
463+
.build();
464+
465+
let token_issuance_fee =
466+
tf.chainstate.get_chain_config().fungible_token_issuance_fee();
467+
468+
let issuance = test_utils::nft_utils::random_token_issuance_v1(
469+
tf.chain_config(),
470+
Destination::AnyoneCanSpend,
471+
&mut rng,
472+
);
473+
let amount_to_mint = match issuance.total_supply {
474+
TokenTotalSupply::Fixed(limit) => {
475+
Amount::from_atoms(rng.gen_range(1..=limit.into_atoms()))
476+
}
477+
TokenTotalSupply::Lockable | TokenTotalSupply::Unlimited => {
478+
Amount::from_atoms(rng.gen_range(100..1000))
479+
}
480+
};
481+
let mint_amount_decimal =
482+
amount_to_mint.into_fixedpoint_str(issuance.number_of_decimals);
483+
484+
let genesis_outpoint = UtxoOutPoint::new(tf.best_block_id().into(), 0);
485+
let genesis_coins = chainstate_test_framework::get_output_value(
486+
tf.chainstate.utxo(&genesis_outpoint).unwrap().unwrap().output(),
487+
)
488+
.unwrap()
489+
.coin_amount()
490+
.unwrap();
491+
let coins_after_issue = (genesis_coins - token_issuance_fee).unwrap();
492+
493+
// Issue token
494+
let tx1 = TransactionBuilder::new()
495+
.add_input(genesis_outpoint.into(), empty_witness(&mut rng))
496+
.add_output(TxOutput::Transfer(
497+
OutputValue::Coin(coins_after_issue),
498+
Destination::AnyoneCanSpend,
499+
))
500+
.add_output(TxOutput::IssueFungibleToken(Box::new(TokenIssuance::V1(
501+
issuance,
502+
))))
503+
.build();
504+
let token_id = make_token_id(
505+
&chain_config,
506+
BlockHeight::new(1),
507+
tx1.transaction().inputs(),
508+
)
509+
.unwrap();
510+
let tx1_id = tx1.transaction().get_id();
511+
let block1 = tf.make_block_builder().add_transaction(tx1).build(&mut rng);
512+
513+
tf.process_block(block1.clone(), chainstate::BlockSource::Local).unwrap();
514+
515+
// Mint tokens
516+
let token_supply_change_fee =
517+
tf.chainstate.get_chain_config().token_supply_change_fee(BlockHeight::zero());
518+
let coins_after_mint = (coins_after_issue - token_supply_change_fee).unwrap();
519+
520+
let tx2 = TransactionBuilder::new()
521+
.add_input(
522+
TxInput::from_command(
523+
AccountNonce::new(0),
524+
AccountCommand::MintTokens(token_id, amount_to_mint),
525+
),
526+
empty_witness(&mut rng),
527+
)
528+
.add_input(
529+
TxInput::from_utxo(tx1_id.into(), 0),
530+
empty_witness(&mut rng),
531+
)
532+
.add_output(TxOutput::Transfer(
533+
OutputValue::Coin(coins_after_mint),
534+
Destination::AnyoneCanSpend,
535+
))
536+
.add_output(TxOutput::Transfer(
537+
OutputValue::TokenV1(token_id, amount_to_mint),
538+
Destination::AnyoneCanSpend,
539+
))
540+
.build();
541+
542+
let tx2_id = tx2.transaction().get_id();
543+
let block2 = tf.make_block_builder().add_transaction(tx2).build(&mut rng);
544+
545+
tf.process_block(block2.clone(), chainstate::BlockSource::Local).unwrap();
546+
547+
_ = tx.send((
548+
tx2_id.to_hash().encode_hex::<String>(),
549+
mint_amount_decimal,
550+
Address::new(&chain_config, token_id).expect("no error").into_string(),
551+
));
552+
553+
vec![block1, block2]
554+
};
555+
556+
let storage = {
557+
let mut storage = TransactionalApiServerInMemoryStorage::new(&chain_config);
558+
559+
let mut db_tx = storage.transaction_rw().await.unwrap();
560+
db_tx.reinitialize_storage(&chain_config).await.unwrap();
561+
db_tx.commit().await.unwrap();
562+
563+
storage
564+
};
565+
566+
let chain_config = Arc::new(chain_config);
567+
let mut local_node = BlockchainState::new(Arc::clone(&chain_config), storage);
568+
local_node.scan_genesis(chain_config.genesis_block()).await.unwrap();
569+
local_node.scan_blocks(BlockHeight::new(0), chainstate_blocks).await.unwrap();
570+
571+
ApiServerWebServerState {
572+
db: Arc::new(local_node.storage().clone_storage().await),
573+
chain_config: Arc::clone(&chain_config),
574+
rpc: Arc::new(DummyRPC {}),
575+
cached_values: Arc::new(CachedValues {
576+
feerate_points: RwLock::new((get_time(), vec![])),
577+
}),
578+
time_getter: Default::default(),
579+
}
580+
};
581+
582+
web_server(listener, web_server_state, true).await
583+
});
584+
585+
let (transaction_id, mint_amount, token_id) = rx.await.unwrap();
586+
let url = format!("/api/v2/transaction/{transaction_id}");
587+
588+
// Given that the listener port is open, this will block until a
589+
// response is made (by the web server, which takes the listener
590+
// over)
591+
let response = reqwest::get(format!("http://{}:{}{url}", addr.ip(), addr.port()))
592+
.await
593+
.unwrap();
594+
595+
assert_eq!(response.status(), 200);
596+
597+
let body = response.text().await.unwrap();
598+
let body: serde_json::Value = serde_json::from_str(&body).unwrap();
599+
let body = body.as_object().unwrap();
600+
601+
let inputs = body.get("inputs").unwrap().as_array().unwrap();
602+
assert_eq!(inputs.len(), 2);
603+
let mint_inp = inputs.first().unwrap().as_object().unwrap().get("input").unwrap();
604+
assert_eq!(
605+
mint_inp.as_object().unwrap().get("command").unwrap().as_str().unwrap(),
606+
"MintTokens"
607+
);
608+
assert_eq!(
609+
mint_inp.as_object().unwrap().get("token_id").unwrap().as_str().unwrap(),
610+
token_id,
611+
);
612+
let amount = mint_inp.as_object().unwrap().get("amount").unwrap().as_object().unwrap();
613+
assert_eq!(
614+
amount.get("decimal").unwrap().as_str().unwrap(),
615+
mint_amount
616+
);
617+
618+
task.abort();
619+
}

api-server/web-server/src/api/json_helpers.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -438,19 +438,19 @@ pub fn tx_to_json(
438438
) -> serde_json::Value {
439439
let token_decimals = &(&additional_info.token_decimals).into();
440440
json!({
441-
"id": tx.get_id().to_hash().encode_hex::<String>(),
442-
"version_byte": tx.version_byte(),
443-
"is_replaceable": tx.is_replaceable(),
444-
"flags": tx.flags(),
445-
"fee": amount_to_json(additional_info.fee, chain_config.coin_decimals()),
446-
"inputs": tx.inputs().iter().zip(additional_info.input_utxos.iter()).map(|(inp, utxo)| json!({
447-
"input": tx_input_to_json(inp, token_decimals, chain_config),
448-
"utxo": utxo.as_ref().map(|txo| txoutput_to_json(txo, chain_config, token_decimals)),
449-
})).collect::<Vec<_>>(),
450-
"outputs": tx.outputs()
451-
.iter()
452-
.map(|out| txoutput_to_json(out, chain_config, token_decimals))
453-
.collect::<Vec<_>>()
441+
"id": tx.get_id().to_hash().encode_hex::<String>(),
442+
"version_byte": tx.version_byte(),
443+
"is_replaceable": tx.is_replaceable(),
444+
"flags": tx.flags(),
445+
"fee": amount_to_json(additional_info.fee, chain_config.coin_decimals()),
446+
"inputs": tx.inputs().iter().zip(additional_info.input_utxos.iter()).map(|(inp, utxo)| json!({
447+
"input": tx_input_to_json(inp, token_decimals, chain_config),
448+
"utxo": utxo.as_ref().map(|txo| txoutput_to_json(txo, chain_config, token_decimals)),
449+
})).collect::<Vec<_>>(),
450+
"outputs": tx.outputs()
451+
.iter()
452+
.map(|out| txoutput_to_json(out, chain_config, token_decimals))
453+
.collect::<Vec<_>>()
454454
})
455455
}
456456

0 commit comments

Comments
 (0)