Skip to content

Commit 18cf7e3

Browse files
committed
fix: verify that nonce advances and the fee is collected, but no state changes materialize when a transaction fails due to an analysis error found at runtime
1 parent 0edbe05 commit 18cf7e3

File tree

1 file changed

+153
-60
lines changed

1 file changed

+153
-60
lines changed

src/chainstate/stacks/db/transactions.rs

Lines changed: 153 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2551,7 +2551,48 @@ pub mod test {
25512551

25522552
let signed_tx = signer.get_tx().unwrap();
25532553

2554-
for (dbi, burn_db) in ALL_BURN_DBS.iter().enumerate() {
2554+
// invalid contract-calls
2555+
let contract_calls = vec![
2556+
(
2557+
addr.clone(),
2558+
"hello-world",
2559+
"set-bar-not-a-method",
2560+
vec![Value::Int(1), Value::Int(1)],
2561+
), // call into non-existant method
2562+
(
2563+
addr.clone(),
2564+
"hello-world-not-a-contract",
2565+
"set-bar",
2566+
vec![Value::Int(1), Value::Int(1)],
2567+
), // call into non-existant contract
2568+
(
2569+
addr_2.clone(),
2570+
"hello-world",
2571+
"set-bar",
2572+
vec![Value::Int(1), Value::Int(1)],
2573+
), // address does not have a contract
2574+
(addr.clone(), "hello-world", "set-bar", vec![Value::Int(1)]), // wrong number of args (too few)
2575+
(
2576+
addr.clone(),
2577+
"hello-world",
2578+
"set-bar",
2579+
vec![Value::Int(1), Value::Int(1), Value::Int(1)],
2580+
), // wrong number of args (too many)
2581+
(
2582+
addr.clone(),
2583+
"hello-world",
2584+
"set-bar",
2585+
vec![Value::buff_from([0xff, 4].to_vec()).unwrap(), Value::Int(1)],
2586+
), // wrong arg type
2587+
(
2588+
addr.clone(),
2589+
"hello-world",
2590+
"set-bar",
2591+
vec![Value::UInt(1), Value::Int(1)],
2592+
), // wrong arg type
2593+
];
2594+
2595+
for (dbi, burn_db) in PRE_21_DBS.iter().enumerate() {
25552596
let mut conn = chainstate.block_begin(
25562597
burn_db,
25572598
&FIRST_BURNCHAIN_CONSENSUS_HASH,
@@ -2562,52 +2603,11 @@ pub mod test {
25622603
let (_fee, _) =
25632604
StacksChainState::process_transaction(&mut conn, &signed_tx, false).unwrap();
25642605

2565-
// invalid contract-calls
2566-
let contract_calls = vec![
2567-
(
2568-
addr.clone(),
2569-
"hello-world",
2570-
"set-bar-not-a-method",
2571-
vec![Value::Int(1), Value::Int(1)],
2572-
), // call into non-existant method
2573-
(
2574-
addr.clone(),
2575-
"hello-world-not-a-contract",
2576-
"set-bar",
2577-
vec![Value::Int(1), Value::Int(1)],
2578-
), // call into non-existant contract
2579-
(
2580-
addr_2.clone(),
2581-
"hello-world",
2582-
"set-bar",
2583-
vec![Value::Int(1), Value::Int(1)],
2584-
), // address does not have a contract
2585-
(addr.clone(), "hello-world", "set-bar", vec![Value::Int(1)]), // wrong number of args (too few)
2586-
(
2587-
addr.clone(),
2588-
"hello-world",
2589-
"set-bar",
2590-
vec![Value::Int(1), Value::Int(1), Value::Int(1)],
2591-
), // wrong number of args (too many)
2592-
(
2593-
addr.clone(),
2594-
"hello-world",
2595-
"set-bar",
2596-
vec![Value::buff_from([0xff, 4].to_vec()).unwrap(), Value::Int(1)],
2597-
), // wrong arg type
2598-
(
2599-
addr.clone(),
2600-
"hello-world",
2601-
"set-bar",
2602-
vec![Value::UInt(1), Value::Int(1)],
2603-
), // wrong arg type
2604-
];
2605-
26062606
let next_nonce = 0;
26072607

2608-
for contract_call in contract_calls {
2608+
for contract_call in contract_calls.iter() {
26092609
let (contract_addr, contract_name, contract_function, contract_args) =
2610-
contract_call;
2610+
contract_call.clone();
26112611
let mut tx_contract_call = StacksTransaction::new(
26122612
TransactionVersion::Testnet,
26132613
auth_2.clone(),
@@ -2649,6 +2649,64 @@ pub mod test {
26492649
}
26502650
conn.commit_block();
26512651
}
2652+
2653+
// in 2.1, all of these are mineable -- the fee will be collected, and the nonce(s) will
2654+
// advance, but no state changes go through
2655+
let mut conn = chainstate.block_begin(
2656+
&TestBurnStateDB_21,
2657+
&FIRST_BURNCHAIN_CONSENSUS_HASH,
2658+
&FIRST_STACKS_BLOCK_HASH,
2659+
&ConsensusHash([3u8; 20]),
2660+
&BlockHeaderHash([3u8; 32]),
2661+
);
2662+
let (_fee, _) =
2663+
StacksChainState::process_transaction(&mut conn, &signed_tx, false).unwrap();
2664+
2665+
let mut next_nonce = 0;
2666+
2667+
for contract_call in contract_calls.iter() {
2668+
let (contract_addr, contract_name, contract_function, contract_args) =
2669+
contract_call.clone();
2670+
let mut tx_contract_call = StacksTransaction::new(
2671+
TransactionVersion::Testnet,
2672+
auth_2.clone(),
2673+
TransactionPayload::new_contract_call(
2674+
contract_addr.clone(),
2675+
contract_name,
2676+
contract_function,
2677+
contract_args,
2678+
)
2679+
.unwrap(),
2680+
);
2681+
2682+
tx_contract_call.chain_id = 0x80000000;
2683+
tx_contract_call.set_tx_fee(0);
2684+
tx_contract_call.set_origin_nonce(next_nonce);
2685+
2686+
let mut signer_2 = StacksTransactionSigner::new(&tx_contract_call);
2687+
signer_2.sign_origin(&privk_2).unwrap();
2688+
2689+
let signed_tx_2 = signer_2.get_tx().unwrap();
2690+
2691+
let account_2 =
2692+
StacksChainState::get_account(&mut conn, &addr_2.to_account_principal());
2693+
2694+
assert_eq!(account_2.nonce, next_nonce);
2695+
2696+
// this is expected to be mined
2697+
let res = StacksChainState::process_transaction(&mut conn, &signed_tx_2, false);
2698+
assert!(res.is_ok());
2699+
2700+
next_nonce += 1;
2701+
let account_2 =
2702+
StacksChainState::get_account(&mut conn, &addr_2.to_account_principal());
2703+
assert_eq!(account_2.nonce, next_nonce);
2704+
2705+
// no state change though
2706+
let var_res = StacksChainState::get_data_var(&mut conn, &contract_id, "bar").unwrap();
2707+
assert!(var_res.is_some());
2708+
assert_eq!(var_res, Some(Value::Int(1)));
2709+
}
26522710
}
26532711

26542712
#[test]
@@ -8594,6 +8652,7 @@ pub mod test {
85948652
(use-trait trait .foo.foo)
85958653
85968654
(define-data-var mutex bool true)
8655+
(define-data-var executed bool false)
85978656
85988657
(define-public (flip)
85998658
(ok (var-set mutex (not (var-get mutex))))
@@ -8605,6 +8664,7 @@ pub mod test {
86058664
(ok (internal (if (var-get mutex)
86068665
(begin
86078666
(print \"some case\")
8667+
(var-set executed true)
86088668
(some ref)
86098669
)
86108670
none
@@ -8645,7 +8705,7 @@ pub mod test {
86458705

86468706
tx_runtime_checkerror_trait.post_condition_mode = TransactionPostConditionMode::Allow;
86478707
tx_runtime_checkerror_trait.chain_id = 0x80000000;
8648-
tx_runtime_checkerror_trait.set_tx_fee(0);
8708+
tx_runtime_checkerror_trait.set_tx_fee(1);
86498709
tx_runtime_checkerror_trait.set_origin_nonce(0);
86508710

86518711
let mut signer = StacksTransactionSigner::new(&tx_runtime_checkerror_trait);
@@ -8666,7 +8726,7 @@ pub mod test {
86668726

86678727
tx_runtime_checkerror_impl.post_condition_mode = TransactionPostConditionMode::Allow;
86688728
tx_runtime_checkerror_impl.chain_id = 0x80000000;
8669-
tx_runtime_checkerror_impl.set_tx_fee(0);
8729+
tx_runtime_checkerror_impl.set_tx_fee(1);
86708730
tx_runtime_checkerror_impl.set_origin_nonce(1);
86718731

86728732
let mut signer = StacksTransactionSigner::new(&tx_runtime_checkerror_impl);
@@ -8687,7 +8747,7 @@ pub mod test {
86878747

86888748
tx_runtime_checkerror.post_condition_mode = TransactionPostConditionMode::Allow;
86898749
tx_runtime_checkerror.chain_id = 0x80000000;
8690-
tx_runtime_checkerror.set_tx_fee(0);
8750+
tx_runtime_checkerror.set_tx_fee(1);
86918751
tx_runtime_checkerror.set_origin_nonce(2);
86928752

86938753
let mut signer = StacksTransactionSigner::new(&tx_runtime_checkerror);
@@ -8711,7 +8771,7 @@ pub mod test {
87118771

87128772
tx_test_trait_checkerror.post_condition_mode = TransactionPostConditionMode::Allow;
87138773
tx_test_trait_checkerror.chain_id = 0x80000000;
8714-
tx_test_trait_checkerror.set_tx_fee(0);
8774+
tx_test_trait_checkerror.set_tx_fee(1);
87158775
tx_test_trait_checkerror.set_origin_nonce(3);
87168776

87178777
let mut signer = StacksTransactionSigner::new(&tx_test_trait_checkerror);
@@ -8732,14 +8792,19 @@ pub mod test {
87328792

87338793
tx_runtime_checkerror_cc_contract.post_condition_mode = TransactionPostConditionMode::Allow;
87348794
tx_runtime_checkerror_cc_contract.chain_id = 0x80000000;
8735-
tx_runtime_checkerror_cc_contract.set_tx_fee(0);
8795+
tx_runtime_checkerror_cc_contract.set_tx_fee(1);
87368796
tx_runtime_checkerror_cc_contract.set_origin_nonce(3);
87378797

87388798
let mut signer = StacksTransactionSigner::new(&tx_runtime_checkerror_cc_contract);
87398799
signer.sign_origin(&privk).unwrap();
87408800

87418801
let signed_runtime_checkerror_cc_contract_tx = signer.get_tx().unwrap();
87428802

8803+
let contract_id = QualifiedContractIdentifier::new(
8804+
StandardPrincipalData::from(addr.clone()),
8805+
ContractName::from("trait-checkerror"),
8806+
);
8807+
87438808
// in 2.0, this invalidates the block
87448809
let mut conn = chainstate.block_begin(
87458810
&TestBurnStateDB_20,
@@ -8755,20 +8820,20 @@ pub mod test {
87558820
false,
87568821
)
87578822
.unwrap();
8758-
assert_eq!(fee, 0);
8823+
assert_eq!(fee, 1);
87598824

87608825
let (fee, _) = StacksChainState::process_transaction(
87618826
&mut conn,
87628827
&signed_runtime_checkerror_impl_tx,
87638828
false,
87648829
)
87658830
.unwrap();
8766-
assert_eq!(fee, 0);
8831+
assert_eq!(fee, 1);
87678832

87688833
let (fee, _) =
87698834
StacksChainState::process_transaction(&mut conn, &signed_runtime_checkerror_tx, false)
87708835
.unwrap();
8771-
assert_eq!(fee, 0);
8836+
assert_eq!(fee, 1);
87728837

87738838
let err = StacksChainState::process_transaction(
87748839
&mut conn,
@@ -8783,6 +8848,8 @@ pub mod test {
87838848
} else {
87848849
panic!("Did not get unchecked interpreter error");
87858850
}
8851+
let acct = StacksChainState::get_account(&mut conn, &addr.into());
8852+
assert_eq!(acct.nonce, 3);
87868853

87878854
let err = StacksChainState::process_transaction(
87888855
&mut conn,
@@ -8797,6 +8864,9 @@ pub mod test {
87978864
} else {
87988865
panic!("Did not get unchecked interpreter error");
87998866
}
8867+
let acct = StacksChainState::get_account(&mut conn, &addr.into());
8868+
assert_eq!(acct.nonce, 3);
8869+
88008870
conn.commit_block();
88018871

88028872
// in 2.05, this invalidates the block
@@ -8814,20 +8884,20 @@ pub mod test {
88148884
false,
88158885
)
88168886
.unwrap();
8817-
assert_eq!(fee, 0);
8887+
assert_eq!(fee, 1);
88188888

88198889
let (fee, _) = StacksChainState::process_transaction(
88208890
&mut conn,
88218891
&signed_runtime_checkerror_impl_tx,
88228892
false,
88238893
)
88248894
.unwrap();
8825-
assert_eq!(fee, 0);
8895+
assert_eq!(fee, 1);
88268896

88278897
let (fee, _) =
88288898
StacksChainState::process_transaction(&mut conn, &signed_runtime_checkerror_tx, false)
88298899
.unwrap();
8830-
assert_eq!(fee, 0);
8900+
assert_eq!(fee, 1);
88318901

88328902
let err = StacksChainState::process_transaction(
88338903
&mut conn,
@@ -8842,6 +8912,8 @@ pub mod test {
88428912
} else {
88438913
panic!("Did not get unchecked interpreter error");
88448914
}
8915+
let acct = StacksChainState::get_account(&mut conn, &addr.into());
8916+
assert_eq!(acct.nonce, 3);
88458917

88468918
let err = StacksChainState::process_transaction(
88478919
&mut conn,
@@ -8856,6 +8928,9 @@ pub mod test {
88568928
} else {
88578929
panic!("Did not get unchecked interpreter error");
88588930
}
8931+
let acct = StacksChainState::get_account(&mut conn, &addr.into());
8932+
assert_eq!(acct.nonce, 3);
8933+
88598934
conn.commit_block();
88608935

88618936
// in 2.1, this is a runtime error
@@ -8881,28 +8956,37 @@ pub mod test {
88818956
false,
88828957
)
88838958
.unwrap();
8884-
assert_eq!(fee, 0);
8959+
assert_eq!(fee, 1);
88858960

88868961
let (fee, _) = StacksChainState::process_transaction(
88878962
&mut conn,
88888963
&signed_runtime_checkerror_impl_tx,
88898964
false,
88908965
)
88918966
.unwrap();
8892-
assert_eq!(fee, 0);
8967+
assert_eq!(fee, 1);
88938968

88948969
let (fee, _) =
88958970
StacksChainState::process_transaction(&mut conn, &signed_runtime_checkerror_tx, false)
88968971
.unwrap();
8897-
assert_eq!(fee, 0);
8972+
assert_eq!(fee, 1);
88988973

88998974
let (fee, tx_receipt) = StacksChainState::process_transaction(
89008975
&mut conn,
89018976
&signed_test_trait_checkerror_tx,
89028977
false,
89038978
)
89048979
.unwrap();
8905-
assert_eq!(fee, 0);
8980+
assert_eq!(fee, 1);
8981+
8982+
// nonce keeps advancing despite error
8983+
let acct = StacksChainState::get_account(&mut conn, &addr.into());
8984+
assert_eq!(acct.nonce, 4);
8985+
8986+
// no state change materialized
8987+
let executed_var =
8988+
StacksChainState::get_data_var(&mut conn, &contract_id, "executed").unwrap();
8989+
assert_eq!(executed_var, Some(Value::Bool(false)));
89068990

89078991
assert!(tx_receipt.vm_error.is_some());
89088992
let err_str = tx_receipt.vm_error.unwrap();
@@ -8916,7 +9000,16 @@ pub mod test {
89169000
false,
89179001
)
89189002
.unwrap();
8919-
assert_eq!(fee, 0);
9003+
assert_eq!(fee, 1);
9004+
9005+
// nonce keeps advancing despite error
9006+
let acct = StacksChainState::get_account(&mut conn, &addr.into());
9007+
assert_eq!(acct.nonce, 5);
9008+
9009+
// no state change materialized
9010+
let executed_var =
9011+
StacksChainState::get_data_var(&mut conn, &contract_id, "executed").unwrap();
9012+
assert_eq!(executed_var, Some(Value::Bool(false)));
89209013

89219014
assert!(tx_receipt.vm_error.is_some());
89229015
let err_str = tx_receipt.vm_error.unwrap();

0 commit comments

Comments
 (0)