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