@@ -77,20 +77,35 @@ fn create_test_framework_with_orders(
7777fn issue_and_mint_token_from_genesis (
7878 rng : & mut ( impl Rng + CryptoRng ) ,
7979 tf : & mut TestFramework ,
80+ ) -> ( TokenId , UtxoOutPoint , UtxoOutPoint ) {
81+ let to_mint = Amount :: from_atoms ( rng. gen_range ( 100 ..100_000_000 ) ) ;
82+ issue_and_mint_token_amount_from_genesis ( rng, tf, to_mint)
83+ }
84+
85+ fn issue_and_mint_token_amount_from_genesis (
86+ rng : & mut ( impl Rng + CryptoRng ) ,
87+ tf : & mut TestFramework ,
88+ to_mint : Amount ,
8089) -> ( TokenId , UtxoOutPoint , UtxoOutPoint ) {
8190 let genesis_block_id = tf. genesis ( ) . get_id ( ) ;
8291 let utxo = UtxoOutPoint :: new ( genesis_block_id. into ( ) , 0 ) ;
8392
84- issue_and_mint_token_from_best_block ( rng, tf, utxo)
93+ issue_and_mint_random_token_from_best_block (
94+ rng,
95+ tf,
96+ utxo,
97+ to_mint,
98+ TokenTotalSupply :: Unlimited ,
99+ IsTokenFreezable :: Yes ,
100+ )
85101}
86102
87- fn issue_and_mint_token_from_best_block (
103+ fn issue_and_mint_token_amount_from_best_block (
88104 rng : & mut ( impl Rng + CryptoRng ) ,
89105 tf : & mut TestFramework ,
90106 utxo_outpoint : UtxoOutPoint ,
107+ to_mint : Amount ,
91108) -> ( TokenId , UtxoOutPoint , UtxoOutPoint ) {
92- let to_mint = Amount :: from_atoms ( rng. gen_range ( 100 ..100_000_000 ) ) ;
93-
94109 issue_and_mint_random_token_from_best_block (
95110 rng,
96111 tf,
@@ -394,15 +409,17 @@ fn create_order_tokens_for_tokens(#[case] seed: Seed) {
394409
395410 let ( token_id_1, _, coins_outpoint) = issue_and_mint_token_from_genesis ( & mut rng, & mut tf) ;
396411
397- let ( token_id_2, tokens_outpoint_2, _) =
398- issue_and_mint_token_from_best_block ( & mut rng, & mut tf, coins_outpoint) ;
399-
400- let tokens_circulating_supply_2 =
401- tf. chainstate . get_token_circulating_supply ( & token_id_2) . unwrap ( ) . unwrap ( ) ;
412+ let token_2_mint_amount = Amount :: from_atoms ( rng. gen_range ( 100 ..100_000_000 ) ) ;
413+ let ( token_id_2, tokens_outpoint_2, _) = issue_and_mint_token_amount_from_best_block (
414+ & mut rng,
415+ & mut tf,
416+ coins_outpoint,
417+ token_2_mint_amount,
418+ ) ;
402419
403420 let ask_amount = Amount :: from_atoms ( rng. gen_range ( 1u128 ..1000 ) ) ;
404421 let give_amount =
405- Amount :: from_atoms ( rng. gen_range ( 1u128 ..tokens_circulating_supply_2 . into_atoms ( ) ) ) ;
422+ Amount :: from_atoms ( rng. gen_range ( 1u128 ..token_2_mint_amount . into_atoms ( ) ) ) ;
406423
407424 // Trade tokens for coins
408425 let order_data = OrderData :: new (
@@ -2363,6 +2380,111 @@ fn fill_order_with_zero(#[case] seed: Seed, #[case] version: OrdersVersion) {
23632380 } ) ;
23642381}
23652382
2383+ #[ rstest]
2384+ #[ trace]
2385+ #[ case( Seed :: from_entropy( ) , OrdersVersion :: V0 ) ]
2386+ #[ trace]
2387+ #[ case( Seed :: from_entropy( ) , OrdersVersion :: V1 ) ]
2388+ fn fill_order_underbid ( #[ case] seed : Seed , #[ case] version : OrdersVersion ) {
2389+ utils:: concurrency:: model ( move || {
2390+ use orders_accounting:: calculate_filled_amount;
2391+
2392+ let mut rng = make_seedable_rng ( seed) ;
2393+ let mut tf = create_test_framework_with_orders ( & mut rng, version) ;
2394+
2395+ let min_ask_atoms = 1000 ;
2396+ let max_ask_atoms = 2000 ;
2397+ let min_give_atoms = 100 ;
2398+ let max_give_atoms = 200 ;
2399+
2400+ let token_amount_to_mint = Amount :: from_atoms ( rng. gen_range ( max_give_atoms..100_000_000 ) ) ;
2401+ let ( token_id, tokens_outpoint, coins_outpoint) =
2402+ issue_and_mint_token_amount_from_genesis ( & mut rng, & mut tf, token_amount_to_mint) ;
2403+
2404+ let ask_amount = Amount :: from_atoms ( rng. gen_range ( min_ask_atoms..max_ask_atoms) ) ;
2405+ let give_amount = Amount :: from_atoms ( rng. gen_range ( min_give_atoms..=max_give_atoms) ) ;
2406+ let order_data = OrderData :: new (
2407+ Destination :: AnyoneCanSpend ,
2408+ OutputValue :: Coin ( ask_amount) ,
2409+ OutputValue :: TokenV1 ( token_id, give_amount) ,
2410+ ) ;
2411+
2412+ let tx = TransactionBuilder :: new ( )
2413+ . add_input ( tokens_outpoint. into ( ) , InputWitness :: NoSignature ( None ) )
2414+ . add_output ( TxOutput :: CreateOrder ( Box :: new ( order_data. clone ( ) ) ) )
2415+ . build ( ) ;
2416+ let order_id = make_order_id ( tx. inputs ( ) ) . unwrap ( ) ;
2417+ tf. make_block_builder ( ) . add_transaction ( tx) . build_and_process ( & mut rng) . unwrap ( ) ;
2418+
2419+ let max_fill_atoms = ask_amount. into_atoms ( ) / give_amount. into_atoms ( ) - 1 ;
2420+ let fill_amount = Amount :: from_atoms ( rng. gen_range ( 1 ..=max_fill_atoms) ) ;
2421+
2422+ // Sanity check: the filled_amount is zero.
2423+ // Note:
2424+ // a) we can't use calculate_fill_order, because it'd fail with OrderUnderbid for orders V1;
2425+ // b) we can use original ask/give amounts for orders V0, since it's our first fill.
2426+ let filled_amount = calculate_filled_amount ( ask_amount, give_amount, fill_amount) . unwrap ( ) ;
2427+ assert_eq ! ( filled_amount, Amount :: ZERO ) ;
2428+
2429+ // Fill the order
2430+ let fill_input = match version {
2431+ OrdersVersion :: V0 => TxInput :: AccountCommand (
2432+ AccountNonce :: new ( 0 ) ,
2433+ AccountCommand :: FillOrder ( order_id, fill_amount, Destination :: AnyoneCanSpend ) ,
2434+ ) ,
2435+ OrdersVersion :: V1 => {
2436+ TxInput :: OrderAccountCommand ( OrderAccountCommand :: FillOrder ( order_id, fill_amount) )
2437+ }
2438+ } ;
2439+ let tx = TransactionBuilder :: new ( )
2440+ . add_input ( coins_outpoint. into ( ) , InputWitness :: NoSignature ( None ) )
2441+ . add_input ( fill_input, InputWitness :: NoSignature ( None ) )
2442+ . build ( ) ;
2443+ let tx_id = tx. transaction ( ) . get_id ( ) ;
2444+ let result = tf. make_block_builder ( ) . add_transaction ( tx) . build_and_process ( & mut rng) ;
2445+
2446+ match version {
2447+ OrdersVersion :: V0 => {
2448+ // The ask balance must have changed, but the give balance must not.
2449+ let expected_ask_balance = ( ask_amount - fill_amount) . unwrap ( ) ;
2450+ assert ! ( result. is_ok( ) ) ;
2451+ assert_eq ! (
2452+ tf. chainstate
2453+ . get_account_nonce_count( common:: chain:: AccountType :: Order ( order_id) )
2454+ . unwrap( ) ,
2455+ Some ( AccountNonce :: new( 0 ) )
2456+ ) ;
2457+ assert_eq ! (
2458+ tf. chainstate. get_order_data( & order_id) . unwrap( ) ,
2459+ Some ( order_data. into( ) )
2460+ ) ;
2461+ assert_eq ! (
2462+ tf. chainstate. get_order_ask_balance( & order_id) . unwrap( ) ,
2463+ Some ( expected_ask_balance) ,
2464+ ) ;
2465+ assert_eq ! (
2466+ tf. chainstate. get_order_give_balance( & order_id) . unwrap( ) ,
2467+ Some ( give_amount) ,
2468+ ) ;
2469+ }
2470+ OrdersVersion :: V1 => {
2471+ assert_eq ! (
2472+ result. unwrap_err( ) ,
2473+ chainstate:: ChainstateError :: ProcessBlockError (
2474+ chainstate:: BlockError :: StateUpdateFailed (
2475+ ConnectTransactionError :: ConstrainedValueAccumulatorError (
2476+ orders_accounting:: Error :: OrderUnderbid ( order_id, fill_amount)
2477+ . into( ) ,
2478+ tx_id. into( )
2479+ )
2480+ )
2481+ )
2482+ ) ;
2483+ }
2484+ }
2485+ } ) ;
2486+ }
2487+
23662488#[ rstest]
23672489#[ trace]
23682490#[ case( Seed :: from_entropy( ) , vec![ 108 , 56 , 65 , 38 , 217 , 22 , 244 , 28 , 38 , 184 ] ) ]
@@ -3053,7 +3175,7 @@ fn fill_freeze_conclude_order(#[case] seed: Seed) {
30533175 tf. make_block_builder ( ) . add_transaction ( tx) . build_and_process ( & mut rng) . unwrap ( ) ;
30543176
30553177 // Fill order partially
3056- let fill_amount = Amount :: from_atoms ( rng. gen_range ( 1 ..ask_amount. into_atoms ( ) ) ) ;
3178+ let fill_amount = Amount :: from_atoms ( rng. gen_range ( 1 ..= ask_amount. into_atoms ( ) ) ) ;
30573179 let filled_amount = calculate_fill_order ( & tf, & order_id, fill_amount, OrdersVersion :: V1 ) ;
30583180 let left_to_fill = ( ask_amount - fill_amount) . unwrap ( ) ;
30593181 let fill_tx = TransactionBuilder :: new ( )
@@ -3105,17 +3227,37 @@ fn fill_freeze_conclude_order(#[case] seed: Seed) {
31053227 let tx_id = tx. transaction ( ) . get_id ( ) ;
31063228 let result = tf. make_block_builder ( ) . add_transaction ( tx) . build_and_process ( & mut rng) ;
31073229
3108- assert_eq ! (
3109- result. unwrap_err( ) ,
3110- chainstate:: ChainstateError :: ProcessBlockError (
3111- chainstate:: BlockError :: StateUpdateFailed (
3112- ConnectTransactionError :: ConstrainedValueAccumulatorError (
3113- orders_accounting:: Error :: AttemptedFillFrozenOrder ( order_id, ) . into( ) ,
3114- tx_id. into( )
3230+ if left_to_fill != Amount :: ZERO {
3231+ assert_eq ! (
3232+ result. unwrap_err( ) ,
3233+ chainstate:: ChainstateError :: ProcessBlockError (
3234+ chainstate:: BlockError :: StateUpdateFailed (
3235+ ConnectTransactionError :: ConstrainedValueAccumulatorError (
3236+ orders_accounting:: Error :: AttemptedFillFrozenOrder ( order_id, )
3237+ . into( ) ,
3238+ tx_id. into( )
3239+ )
31153240 )
31163241 )
3117- )
3118- ) ;
3242+ ) ;
3243+ } else {
3244+ // Note: in orders V1 zero fills are not allowed and the zero fill check happens earlier,
3245+ // so we'll hit this error instead.
3246+ assert_eq ! (
3247+ result. unwrap_err( ) ,
3248+ chainstate:: ChainstateError :: ProcessBlockError (
3249+ chainstate:: BlockError :: CheckBlockFailed (
3250+ CheckBlockError :: CheckTransactionFailed (
3251+ CheckBlockTransactionsError :: CheckTransactionError (
3252+ CheckTransactionError :: AttemptToFillOrderWithZero (
3253+ order_id, tx_id
3254+ )
3255+ )
3256+ )
3257+ )
3258+ )
3259+ ) ;
3260+ }
31193261 }
31203262
31213263 //Try freezing the order once more
@@ -3502,8 +3644,11 @@ fn fill_order_v1_must_not_be_signed(#[case] seed: Seed) {
35023644 let ( sk, pk) = PrivateKey :: new_from_rng ( & mut rng, KeyKind :: Secp256k1Schnorr ) ;
35033645 let output_destination = Destination :: PublicKey ( pk) ;
35043646
3505- let fill_amount =
3506- Amount :: from_atoms ( rng. gen_range ( 1 ..initial_ask_amount. into_atoms ( ) / 10 ) ) ;
3647+ let min_fill_amount =
3648+ initial_ask_amount. into_atoms ( ) . div_ceil ( initial_give_amount. into_atoms ( ) ) ;
3649+ let fill_amount = Amount :: from_atoms (
3650+ rng. gen_range ( min_fill_amount..initial_ask_amount. into_atoms ( ) / 10 ) ,
3651+ ) ;
35073652 let filled_amount = calculate_fill_order ( & tf, & order_id, fill_amount, OrdersVersion :: V1 ) ;
35083653 let fill_order_input =
35093654 TxInput :: OrderAccountCommand ( OrderAccountCommand :: FillOrder ( order_id, fill_amount) ) ;
0 commit comments