Skip to content

Commit ab73c15

Browse files
committed
Merge branch 'master' into remove-elided-lifetimes-in-associated-constant
2 parents 3bf078e + 3c93830 commit ab73c15

File tree

5 files changed

+168
-99
lines changed

5 files changed

+168
-99
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
## Fixed
1111
- [895](https://github.com/FuelLabs/fuel-vm/pull/895): Fix elided lifetimes compilation warnings that became errors after the release of rust 1.83.0.
1212
- [895](https://github.com/FuelLabs/fuel-vm/pull/895): Bump proptest-derive to version `0.5.1` to fix non-local impl errors on the derivation of `proptest_derive::Arbitrary` introduced by rust 1.83.0.
13+
- [889](https://github.com/FuelLabs/fuel-vm/pull/889): Debugger breakpoint caused receipts to be produced incorrectly.
1314

1415
## [Version 0.59.1]
1516

fuel-vm/src/interpreter/executors/main.rs

Lines changed: 87 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -944,82 +944,9 @@ where
944944
)?;
945945
ProgramState::Return(1)
946946
} else {
947-
let gas_limit;
948-
let is_empty_script;
949-
if let Some(script) = self.transaction().as_script() {
950-
gas_limit = *script.script_gas_limit();
951-
is_empty_script = script.script().is_empty();
952-
} else {
953-
unreachable!(
954-
"Only `Script` transactions can be executed inside of the VM"
955-
)
956-
}
957-
958-
// TODO set tree balance
959-
960947
// `Interpreter` supports only `Create` and `Script` transactions. It is not
961948
// `Create` -> it is `Script`.
962-
let program = if !is_empty_script {
963-
self.run_program()
964-
} else {
965-
// Return `1` as successful execution.
966-
let return_val = 1;
967-
self.ret(return_val)?;
968-
Ok(ProgramState::Return(return_val))
969-
};
970-
971-
let gas_used = gas_limit
972-
.checked_sub(self.remaining_gas())
973-
.ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
974-
975-
// Catch VM panic and don't propagate, generating a receipt
976-
let (status, program) = match program {
977-
Ok(s) => {
978-
// either a revert or success
979-
let res = if let ProgramState::Revert(_) = &s {
980-
ScriptExecutionResult::Revert
981-
} else {
982-
ScriptExecutionResult::Success
983-
};
984-
(res, s)
985-
}
986-
987-
Err(e) => match e.instruction_result() {
988-
Some(result) => {
989-
self.append_panic_receipt(result);
990-
991-
(ScriptExecutionResult::Panic, ProgramState::Revert(0))
992-
}
993-
994-
// This isn't a specified case of an erroneous program and should be
995-
// propagated. If applicable, OS errors will fall into this category.
996-
None => return Err(e),
997-
},
998-
};
999-
1000-
let receipt = Receipt::script_result(status, gas_used);
1001-
1002-
self.receipts.push(receipt)?;
1003-
1004-
if program.is_debug() {
1005-
self.debugger_set_last_state(program);
1006-
}
1007-
1008-
let revert = matches!(program, ProgramState::Revert(_));
1009-
let gas_price = self.gas_price();
1010-
Self::finalize_outputs(
1011-
&mut self.tx,
1012-
&gas_costs,
1013-
&fee_params,
1014-
&base_asset_id,
1015-
revert,
1016-
gas_used,
1017-
&self.initial_balances,
1018-
&self.balances,
1019-
gas_price,
1020-
)?;
1021-
1022-
program
949+
self.run_program()?
1023950
};
1024951
self.update_transaction_outputs()?;
1025952

@@ -1029,29 +956,94 @@ where
1029956
pub(crate) fn run_program(
1030957
&mut self,
1031958
) -> Result<ProgramState, InterpreterError<S::DataError>> {
1032-
loop {
1033-
// Check whether the instruction will be executed in a call context
1034-
let in_call = !self.frames.is_empty();
1035-
1036-
let state = self.execute()?;
1037-
1038-
if in_call {
1039-
// Only reverts should terminate execution from a call context
1040-
match state {
1041-
ExecuteState::Revert(r) => return Ok(ProgramState::Revert(r)),
1042-
ExecuteState::DebugEvent(d) => return Ok(ProgramState::RunProgram(d)),
1043-
_ => {}
1044-
}
1045-
} else {
1046-
match state {
1047-
ExecuteState::Return(r) => return Ok(ProgramState::Return(r)),
1048-
ExecuteState::ReturnData(d) => return Ok(ProgramState::ReturnData(d)),
1049-
ExecuteState::Revert(r) => return Ok(ProgramState::Revert(r)),
1050-
ExecuteState::DebugEvent(d) => return Ok(ProgramState::RunProgram(d)),
1051-
ExecuteState::Proceed => {}
959+
let Some(script) = self.transaction().as_script() else {
960+
unreachable!("Only `Script` transactions can be executed inside of the VM")
961+
};
962+
let gas_limit = *script.script_gas_limit();
963+
964+
let (result, state) = if script.script().is_empty() {
965+
// Empty script is special-cased to simply return `1` as successful execution.
966+
let return_val = 1;
967+
self.ret(return_val)?;
968+
(
969+
ScriptExecutionResult::Success,
970+
ProgramState::Return(return_val),
971+
)
972+
} else {
973+
// TODO set tree balance
974+
loop {
975+
// Check whether the instruction will be executed in a call context
976+
let in_call = !self.frames.is_empty();
977+
978+
match self.execute() {
979+
// Proceeding with the execution normally
980+
Ok(ExecuteState::Proceed) => continue,
981+
// Debugger events are returned directly to the caller
982+
Ok(ExecuteState::DebugEvent(d)) => {
983+
self.debugger_set_last_state(ProgramState::RunProgram(d));
984+
return Ok(ProgramState::RunProgram(d));
985+
}
986+
// Reverting terminated execution immediately
987+
Ok(ExecuteState::Revert(r)) => {
988+
break (ScriptExecutionResult::Revert, ProgramState::Revert(r))
989+
}
990+
// Returning in call context is ignored
991+
Ok(ExecuteState::Return(_) | ExecuteState::ReturnData(_))
992+
if in_call =>
993+
{
994+
continue
995+
}
996+
// In non-call context, returning terminates the execution
997+
Ok(ExecuteState::Return(r)) => {
998+
break (ScriptExecutionResult::Success, ProgramState::Return(r))
999+
}
1000+
Ok(ExecuteState::ReturnData(d)) => {
1001+
break (
1002+
ScriptExecutionResult::Success,
1003+
ProgramState::ReturnData(d),
1004+
)
1005+
}
1006+
// Error always terminates the execution
1007+
Err(e) => match e.instruction_result() {
1008+
Some(result) => {
1009+
self.append_panic_receipt(result);
1010+
break (ScriptExecutionResult::Panic, ProgramState::Revert(0));
1011+
}
1012+
// This isn't a specified case of an erroneous program and should
1013+
// be propagated. If applicable, OS errors
1014+
// will fall into this category.
1015+
// The VM state is not finalized in this case.
1016+
None => return Err(e),
1017+
},
10521018
}
10531019
}
1054-
}
1020+
};
1021+
1022+
// Produce result receipt
1023+
let gas_used = gas_limit
1024+
.checked_sub(self.remaining_gas())
1025+
.ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
1026+
self.receipts
1027+
.push(Receipt::script_result(result, gas_used))?;
1028+
1029+
// Finalize the outputs
1030+
let fee_params = *self.fee_params();
1031+
let base_asset_id = *self.base_asset_id();
1032+
let gas_costs = self.gas_costs().clone();
1033+
let gas_price = self.gas_price();
1034+
Self::finalize_outputs(
1035+
&mut self.tx,
1036+
&gas_costs,
1037+
&fee_params,
1038+
&base_asset_id,
1039+
matches!(state, ProgramState::Revert(_)),
1040+
gas_used,
1041+
&self.initial_balances,
1042+
&self.balances,
1043+
gas_price,
1044+
)?;
1045+
1046+
Ok(state)
10551047
}
10561048

10571049
/// Update tx fields after execution

fuel-vm/src/tests/crypto.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ use sha3::{
3535

3636
use crate::{
3737
prelude::*,
38-
storage::predicate::EmptyStorage,
3938
tests::test_helpers::set_full_word,
4039
util::test_helpers::check_expected_reason_for_instructions,
4140
};
@@ -237,7 +236,7 @@ async fn recover_tx_id_predicate() {
237236
.estimate_predicates_async::<TokioWithRayon>(
238237
&check_params,
239238
&DummyPool,
240-
&EmptyStorage,
239+
&crate::storage::predicate::EmptyStorage,
241240
)
242241
.await
243242
.expect("Should estimate predicate successfully");
@@ -248,8 +247,12 @@ async fn recover_tx_id_predicate() {
248247
}
249248

250249
// sequential version
251-
tx.estimate_predicates(&check_params, MemoryInstance::new(), &EmptyStorage)
252-
.expect("Should estimate predicate successfully");
250+
tx.estimate_predicates(
251+
&check_params,
252+
MemoryInstance::new(),
253+
&crate::storage::predicate::EmptyStorage,
254+
)
255+
.expect("Should estimate predicate successfully");
253256

254257
tx.into_checked(maturity, &consensus_params)
255258
.expect("Should check predicate successfully");

fuel-vm/src/tests/debugger.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use alloc::{
2+
vec,
3+
vec::Vec,
4+
};
5+
6+
use fuel_asm::{
7+
op,
8+
RegId,
9+
};
10+
use fuel_tx::{
11+
ConsensusParameters,
12+
Finalizable,
13+
GasCosts,
14+
Script,
15+
TransactionBuilder,
16+
};
17+
18+
use crate::{
19+
prelude::{
20+
Interpreter,
21+
IntoChecked,
22+
},
23+
state::ProgramState,
24+
};
25+
26+
#[test]
27+
fn receipts_are_produced_correctly_with_stepping() {
28+
let script = vec![
29+
op::movi(0x20, 1234),
30+
op::log(0x20, RegId::ZERO, RegId::ZERO, RegId::ZERO),
31+
op::ret(RegId::ONE),
32+
]
33+
.into_iter()
34+
.collect();
35+
36+
let params = ConsensusParameters::standard();
37+
let tx = TransactionBuilder::script(script, Vec::new())
38+
.script_gas_limit(1_000_000)
39+
.maturity(Default::default())
40+
.add_fee_input()
41+
.finalize()
42+
.into_checked(Default::default(), &params)
43+
.expect("failed to check tx")
44+
.into_ready(0, &GasCosts::default(), params.fee_params(), None)
45+
.expect("failed to ready tx");
46+
47+
let mut vm = Interpreter::<_, _, Script>::with_memory_storage();
48+
vm.transact(tx.clone()).expect("panicked");
49+
let receipts_without_debugger = vm.receipts().to_vec();
50+
51+
let mut vm = Interpreter::<_, _, Script>::with_memory_storage();
52+
vm.set_single_stepping(true);
53+
let mut t = *vm.transact(tx).expect("panicked").state();
54+
loop {
55+
match t {
56+
ProgramState::Return(_)
57+
| ProgramState::ReturnData(_)
58+
| ProgramState::Revert(_) => {
59+
break;
60+
}
61+
ProgramState::RunProgram(_) => {
62+
t = vm.resume().expect("panicked");
63+
}
64+
ProgramState::VerifyPredicate(_) => {
65+
unreachable!("no predicates in this test")
66+
}
67+
}
68+
}
69+
let receipts_with_debugger = vm.receipts();
70+
71+
assert_eq!(receipts_without_debugger, receipts_with_debugger);
72+
}

fuel-vm/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod code_coverage;
1717
mod coins;
1818
mod contract;
1919
mod crypto;
20+
mod debugger;
2021
mod encoding;
2122
mod external;
2223
mod flow;

0 commit comments

Comments
 (0)