Skip to content

Commit da7040c

Browse files
authored
Merge pull request #1948 from mintlayer/minor_order_test_improvements
Order-related tests: fix a couple spurious failures; add one new test
2 parents 4f1572f + 8f00fa9 commit da7040c

1 file changed

Lines changed: 167 additions & 22 deletions

File tree

chainstate/test-suite/src/tests/orders_tests.rs

Lines changed: 167 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,35 @@ fn create_test_framework_with_orders(
7777
fn 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

Comments
 (0)