@@ -196,7 +196,7 @@ async fn multiple_tx_in_same_block(#[case] seed: Seed) {
196
196
"is_replaceable" : transaction. is_replaceable( ) ,
197
197
"flags" : transaction. flags( ) ,
198
198
"inputs" : transaction. inputs( ) . iter( ) . zip( utxos) . map( |( inp, utxo) | json!( {
199
- "input" : tx_input_to_json( inp, & chain_config) ,
199
+ "input" : tx_input_to_json( inp, & TokenDecimals :: Single ( None ) , & chain_config) ,
200
200
"utxo" : utxo. as_ref( ) . map( |txo| txoutput_to_json( txo, & chain_config, & TokenDecimals :: Single ( None ) ) ) ,
201
201
} ) ) . collect:: <Vec <_>>( ) ,
202
202
"outputs" : transaction. outputs( )
@@ -342,7 +342,7 @@ async fn ok(#[case] seed: Seed) {
342
342
"is_replaceable" : transaction. is_replaceable( ) ,
343
343
"flags" : transaction. flags( ) ,
344
344
"inputs" : transaction. inputs( ) . iter( ) . zip( utxos) . map( |( inp, utxo) | json!( {
345
- "input" : tx_input_to_json( inp, & chain_config) ,
345
+ "input" : tx_input_to_json( inp, & TokenDecimals :: Single ( None ) , & chain_config) ,
346
346
"utxo" : utxo. as_ref( ) . map( |txo| txoutput_to_json( txo, & chain_config, & TokenDecimals :: Single ( None ) ) ) ,
347
347
} ) ) . collect:: <Vec <_>>( ) ,
348
348
"outputs" : transaction. outputs( )
@@ -434,3 +434,191 @@ async fn ok(#[case] seed: Seed) {
434
434
435
435
task. abort ( ) ;
436
436
}
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 :: Burn ( OutputValue :: TokenV1 (
533
+ token_id,
534
+ amount_to_mint,
535
+ ) ) )
536
+ . add_output ( TxOutput :: Transfer (
537
+ OutputValue :: Coin ( coins_after_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
+ let outputs = body. get ( "outputs" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
619
+ assert_eq ! ( outputs. len( ) , 2 ) ;
620
+ let burn_out = outputs. first ( ) . unwrap ( ) . as_object ( ) . unwrap ( ) ;
621
+ assert_eq ! ( burn_out. get( "type" ) . unwrap( ) . as_str( ) . unwrap( ) , "Burn" , ) ;
622
+
623
+ task. abort ( ) ;
624
+ }
0 commit comments