Skip to content

Commit 2ba828d

Browse files
committed
Fix complete order fill
1 parent e5c4702 commit 2ba828d

File tree

9 files changed

+303
-64
lines changed

9 files changed

+303
-64
lines changed

chainstate/constraints-value-accumulator/src/constraints_accumulator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,11 @@ impl ConstrainedValueAccumulator {
274274
let ask_balance = orders_accounting_view
275275
.get_ask_balance(id)
276276
.map_err(|_| orders_accounting::Error::ViewFail)?
277-
.ok_or(orders_accounting::Error::OrderAskBalanceNotFound(*id))?;
277+
.unwrap_or(Amount::ZERO);
278278
let give_balance = orders_accounting_view
279279
.get_give_balance(id)
280280
.map_err(|_| orders_accounting::Error::ViewFail)?
281-
.ok_or(orders_accounting::Error::OrderGiveBalanceNotFound(*id))?;
281+
.unwrap_or(Amount::ZERO);
282282

283283
let initially_asked = output_value_amount(order_data.ask())?;
284284
let filled_amount = (initially_asked - ask_balance)

chainstate/src/detail/ban_score.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -675,13 +675,10 @@ impl BanScore for orders_accounting::Error {
675675
Error::InvariantOrderDataExistForConcludeUndo(_) => 100,
676676
Error::InvariantOrderAskBalanceExistForConcludeUndo(_) => 100,
677677
Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) => 100,
678-
Error::FillOrderChangeLeft(_) => 100,
679678
Error::CurrencyMismatch => 100,
680679
Error::OrderOverflow(_) => 100,
681680
Error::OrderOverbid(_, _, _) => 100,
682681
Error::AttemptedConcludeNonexistingOrderData(_) => 100,
683-
Error::AttemptedConcludeNonexistingAskBalance(_) => 100,
684-
Error::AttemptedConcludeNonexistingGiveBalance(_) => 100,
685682
Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100,
686683
Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100,
687684
Error::UnsupportedTokenVersion => 100,

chainstate/src/detail/error_classification.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -911,13 +911,10 @@ impl BlockProcessingErrorClassification for orders_accounting::Error {
911911
| Error::InvariantOrderDataExistForConcludeUndo(_)
912912
| Error::InvariantOrderAskBalanceExistForConcludeUndo(_)
913913
| Error::InvariantOrderGiveBalanceExistForConcludeUndo(_)
914-
| Error::FillOrderChangeLeft(_)
915914
| Error::CurrencyMismatch
916915
| Error::OrderOverflow(_)
917916
| Error::OrderOverbid(_, _, _)
918917
| Error::AttemptedConcludeNonexistingOrderData(_)
919-
| Error::AttemptedConcludeNonexistingAskBalance(_)
920-
| Error::AttemptedConcludeNonexistingGiveBalance(_)
921918
| Error::UnsupportedTokenVersion
922919
| Error::InvariantNonzeroAskBalanceForMissingOrder(_)
923920
| Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => {

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

Lines changed: 263 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use common::{
2929
AccountCommand, AccountNonce, ChainstateUpgrade, Destination, OrderData, SignedTransaction,
3030
TxInput, TxOutput, UtxoOutPoint,
3131
},
32-
primitives::{Amount, BlockHeight, Idable},
32+
primitives::{Amount, BlockHeight, CoinOrTokenId, Idable},
3333
};
3434
use crypto::key::{KeyKind, PrivateKey};
3535
use orders_accounting::OrdersAccountingDB;
@@ -511,7 +511,7 @@ fn fill_order_check_storage(#[case] seed: Seed) {
511511
tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap();
512512

513513
assert_eq!(
514-
Some(order_data),
514+
Some(order_data.clone()),
515515
tf.chainstate.get_order_data(&order_id).unwrap()
516516
);
517517
assert_eq!(
@@ -550,7 +550,10 @@ fn fill_order_check_storage(#[case] seed: Seed) {
550550
.build();
551551
tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap();
552552

553-
assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap());
553+
assert_eq!(
554+
Some(order_data),
555+
tf.chainstate.get_order_data(&order_id).unwrap()
556+
);
554557
assert_eq!(
555558
None,
556559
tf.chainstate.get_order_ask_balance(&order_id).unwrap()
@@ -618,6 +621,79 @@ fn fill_partially_then_conclude(#[case] seed: Seed) {
618621
.build();
619622
tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap();
620623

624+
{
625+
// Try overspend give in conclude order
626+
let tx = TransactionBuilder::new()
627+
.add_input(
628+
TxInput::AccountCommand(
629+
AccountNonce::new(1),
630+
AccountCommand::ConcludeOrder(order_id),
631+
),
632+
InputWitness::NoSignature(None),
633+
)
634+
.add_output(TxOutput::Transfer(
635+
OutputValue::TokenV1(
636+
token_id,
637+
(give_amount - filled_amount)
638+
.and_then(|v| v + Amount::from_atoms(1))
639+
.unwrap(),
640+
),
641+
Destination::AnyoneCanSpend,
642+
))
643+
.add_output(TxOutput::Transfer(
644+
OutputValue::Coin(output_value_amount(&fill_value)),
645+
Destination::AnyoneCanSpend,
646+
))
647+
.build();
648+
let tx_id = tx.transaction().get_id();
649+
let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng);
650+
assert_eq!(res.unwrap_err(),
651+
chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed(
652+
ConnectTransactionError::ConstrainedValueAccumulatorError(
653+
constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints(
654+
CoinOrTokenId::TokenId(token_id)
655+
),
656+
tx_id.into()
657+
)
658+
))
659+
);
660+
}
661+
662+
{
663+
// Try overspend ask in conclude order
664+
let tx = TransactionBuilder::new()
665+
.add_input(
666+
TxInput::AccountCommand(
667+
AccountNonce::new(1),
668+
AccountCommand::ConcludeOrder(order_id),
669+
),
670+
InputWitness::NoSignature(None),
671+
)
672+
.add_output(TxOutput::Transfer(
673+
OutputValue::TokenV1(token_id, (give_amount - filled_amount).unwrap()),
674+
Destination::AnyoneCanSpend,
675+
))
676+
.add_output(TxOutput::Transfer(
677+
OutputValue::Coin(
678+
(output_value_amount(&fill_value) + Amount::from_atoms(1)).unwrap(),
679+
),
680+
Destination::AnyoneCanSpend,
681+
))
682+
.build();
683+
let tx_id = tx.transaction().get_id();
684+
let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng);
685+
assert_eq!(res.unwrap_err(),
686+
chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed(
687+
ConnectTransactionError::ConstrainedValueAccumulatorError(
688+
constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints(
689+
CoinOrTokenId::Coin
690+
),
691+
tx_id.into()
692+
)
693+
))
694+
);
695+
}
696+
621697
// conclude the order
622698
let tx = TransactionBuilder::new()
623699
.add_input(
@@ -650,6 +726,190 @@ fn fill_partially_then_conclude(#[case] seed: Seed) {
650726
});
651727
}
652728

729+
#[rstest]
730+
#[trace]
731+
#[case(Seed::from_entropy())]
732+
fn fill_completely_then_conclude(#[case] seed: Seed) {
733+
utils::concurrency::model(move || {
734+
let mut rng = make_seedable_rng(seed);
735+
let mut tf = TestFramework::builder(&mut rng).build();
736+
737+
let (token_id, tokens_outpoint, coins_outpoint) =
738+
issue_and_mint_token_from_genesis(&mut rng, &mut tf);
739+
740+
let ask_amount = Amount::from_atoms(rng.gen_range(1u128..1000));
741+
let give_amount = Amount::from_atoms(rng.gen_range(1u128..1000));
742+
let order_data = OrderData::new(
743+
Destination::AnyoneCanSpend,
744+
OutputValue::Coin(ask_amount),
745+
OutputValue::TokenV1(token_id, give_amount),
746+
);
747+
748+
let order_id = make_order_id(&tokens_outpoint);
749+
let tx = TransactionBuilder::new()
750+
.add_input(tokens_outpoint.into(), InputWitness::NoSignature(None))
751+
.add_output(TxOutput::AnyoneCanTake(Box::new(order_data)))
752+
.build();
753+
tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap();
754+
755+
{
756+
// Try overspend complete fill order
757+
let tx = TransactionBuilder::new()
758+
.add_input(
759+
coins_outpoint.clone().into(),
760+
InputWitness::NoSignature(None),
761+
)
762+
.add_input(
763+
TxInput::AccountCommand(
764+
AccountNonce::new(0),
765+
AccountCommand::FillOrder(
766+
order_id,
767+
OutputValue::Coin(ask_amount),
768+
Destination::AnyoneCanSpend,
769+
),
770+
),
771+
InputWitness::NoSignature(None),
772+
)
773+
.add_output(TxOutput::Transfer(
774+
OutputValue::TokenV1(token_id, (give_amount + Amount::from_atoms(1)).unwrap()),
775+
Destination::AnyoneCanSpend,
776+
))
777+
.build();
778+
let tx_id = tx.transaction().get_id();
779+
let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng);
780+
assert_eq!(res.unwrap_err(),
781+
chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed(
782+
ConnectTransactionError::ConstrainedValueAccumulatorError(
783+
constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints(
784+
CoinOrTokenId::TokenId(token_id)
785+
),
786+
tx_id.into()
787+
)
788+
))
789+
);
790+
}
791+
792+
{
793+
// Try overbid complete fill order
794+
let tx = TransactionBuilder::new()
795+
.add_input(
796+
coins_outpoint.clone().into(),
797+
InputWitness::NoSignature(None),
798+
)
799+
.add_input(
800+
TxInput::AccountCommand(
801+
AccountNonce::new(0),
802+
AccountCommand::FillOrder(
803+
order_id,
804+
OutputValue::Coin((ask_amount + Amount::from_atoms(1)).unwrap()),
805+
Destination::AnyoneCanSpend,
806+
),
807+
),
808+
InputWitness::NoSignature(None),
809+
)
810+
.add_output(TxOutput::Transfer(
811+
OutputValue::TokenV1(token_id, give_amount),
812+
Destination::AnyoneCanSpend,
813+
))
814+
.build();
815+
let tx_id = tx.transaction().get_id();
816+
let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng);
817+
assert_eq!(
818+
res.unwrap_err(),
819+
chainstate::ChainstateError::ProcessBlockError(
820+
chainstate::BlockError::StateUpdateFailed(
821+
ConnectTransactionError::ConstrainedValueAccumulatorError(
822+
orders_accounting::Error::OrderOverbid(
823+
order_id,
824+
ask_amount,
825+
(ask_amount + Amount::from_atoms(1)).unwrap()
826+
)
827+
.into(),
828+
tx_id.into()
829+
)
830+
)
831+
)
832+
);
833+
}
834+
835+
// Fill the order completely
836+
let tx = TransactionBuilder::new()
837+
.add_input(coins_outpoint.into(), InputWitness::NoSignature(None))
838+
.add_input(
839+
TxInput::AccountCommand(
840+
AccountNonce::new(0),
841+
AccountCommand::FillOrder(
842+
order_id,
843+
OutputValue::Coin(ask_amount),
844+
Destination::AnyoneCanSpend,
845+
),
846+
),
847+
InputWitness::NoSignature(None),
848+
)
849+
.add_output(TxOutput::Transfer(
850+
OutputValue::TokenV1(token_id, give_amount),
851+
Destination::AnyoneCanSpend,
852+
))
853+
.build();
854+
tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap();
855+
856+
{
857+
// Try overspend conclude order
858+
let tx = TransactionBuilder::new()
859+
.add_input(
860+
TxInput::AccountCommand(
861+
AccountNonce::new(1),
862+
AccountCommand::ConcludeOrder(order_id),
863+
),
864+
InputWitness::NoSignature(None),
865+
)
866+
.add_output(TxOutput::Transfer(
867+
OutputValue::Coin((ask_amount + Amount::from_atoms(1)).unwrap()),
868+
Destination::AnyoneCanSpend,
869+
))
870+
.build();
871+
let tx_id = tx.transaction().get_id();
872+
let res = tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng);
873+
assert_eq!(res.unwrap_err(),
874+
chainstate::ChainstateError::ProcessBlockError(chainstate::BlockError::StateUpdateFailed(
875+
ConnectTransactionError::ConstrainedValueAccumulatorError(
876+
constraints_value_accumulator::Error::AttemptToPrintMoneyOrViolateTimelockConstraints(
877+
CoinOrTokenId::Coin
878+
),
879+
tx_id.into()
880+
)
881+
))
882+
);
883+
}
884+
885+
// conclude the order
886+
let tx = TransactionBuilder::new()
887+
.add_input(
888+
TxInput::AccountCommand(
889+
AccountNonce::new(1),
890+
AccountCommand::ConcludeOrder(order_id),
891+
),
892+
InputWitness::NoSignature(None),
893+
)
894+
.add_output(TxOutput::Transfer(
895+
OutputValue::Coin(ask_amount),
896+
Destination::AnyoneCanSpend,
897+
))
898+
.build();
899+
tf.make_block_builder().add_transaction(tx).build_and_process(&mut rng).unwrap();
900+
901+
assert_eq!(None, tf.chainstate.get_order_data(&order_id).unwrap());
902+
assert_eq!(
903+
None,
904+
tf.chainstate.get_order_ask_balance(&order_id).unwrap()
905+
);
906+
assert_eq!(
907+
None,
908+
tf.chainstate.get_order_give_balance(&order_id).unwrap()
909+
);
910+
});
911+
}
912+
653913
#[rstest]
654914
#[trace]
655915
#[case(Seed::from_entropy())]

mempool/src/error/ban_score.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,10 @@ impl MempoolBanScore for orders_accounting::Error {
515515
Error::InvariantOrderGiveBalanceExistForConcludeUndo(_) => 100,
516516
Error::InvariantNonzeroAskBalanceForMissingOrder(_) => 100,
517517
Error::InvariantNonzeroGiveBalanceForMissingOrder(_) => 100,
518-
Error::FillOrderChangeLeft(_) => 100,
519518
Error::CurrencyMismatch => 100,
520519
Error::OrderOverflow(_) => 100,
521520
Error::OrderOverbid(_, _, _) => 100,
522521
Error::AttemptedConcludeNonexistingOrderData(_) => 0,
523-
Error::AttemptedConcludeNonexistingAskBalance(_) => 0,
524-
Error::AttemptedConcludeNonexistingGiveBalance(_) => 0,
525522
Error::UnsupportedTokenVersion => 100,
526523
Error::ViewFail => 0,
527524
Error::StorageWrite => 0,

0 commit comments

Comments
 (0)