Skip to content

Commit

Permalink
Propagate certain reverts as panics
Browse files Browse the repository at this point in the history
Fixes #339
  • Loading branch information
cburgdorf committed Jul 8, 2021
1 parent a24e934 commit bf654f2
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 38 deletions.
2 changes: 1 addition & 1 deletion crates/test-files/fixtures/features/revert.fe
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ contract Foo:
revert Error(msg=1, val=true)

pub def revert_other_error():
revert OtherError(msg=1, val=true)
revert OtherError(msg=1, val=true)
18 changes: 14 additions & 4 deletions crates/test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,9 @@ impl ContractHarness {
executor: &mut Executor,
name: &str,
input: &[ethabi::Token],
revert_data: &[u8],
) {
match self.capture_call(executor, name, input) {
evm::Capture::Exit((ExitReason::Revert(_), _)) => {}
_ => panic!("function did not revert"),
}
validate_revert(self.capture_call(executor, name, input), revert_data)
}

// Executor must be passed by value to get emitted events.
Expand Down Expand Up @@ -204,6 +202,18 @@ pub fn validate_revert(
};
}

pub fn encoded_panic_assert() -> Vec<u8> {
encode_revert("Panic(uint256)", &[uint_token(0x01)])
}

pub fn encoded_over_or_underflow() -> Vec<u8> {
encode_revert("Panic(uint256)", &[uint_token(0x11)])
}

pub fn encoded_div_or_mod_by_zero() -> Vec<u8> {
encode_revert("Panic(uint256)", &[uint_token(0x12)])
}

#[allow(dead_code)]
#[cfg(feature = "solc-backend")]
pub fn deploy_contract(
Expand Down
1 change: 1 addition & 0 deletions crates/tests/src/demo_erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ fn erc20_token() {
address_token(bob),
uint_token_from_dec_str("5000000000000000"),
],
&encoded_panic_assert(),
);
harness.test_function(
&mut executor,
Expand Down
21 changes: 20 additions & 1 deletion crates/tests/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ fn test_assert() {

validate_revert(
harness.capture_call(&mut executor, "bar", &[uint_token(4)]),
&[],
&encoded_panic_assert(),
);

assert!(matches!(
Expand Down Expand Up @@ -682,10 +682,12 @@ fn checked_arithmetic() {
// ADDITION

// unsigned: max_value + 1 fails

harness.test_function_reverts(
&mut executor,
&format!("add_u{}", config.size),
&[config.u_max.clone(), uint_token(1)],
&encoded_over_or_underflow(),
);

// unsigned: max_value + 0 works
Expand All @@ -701,6 +703,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("add_i{}", config.size),
&[config.i_max.clone(), int_token(1)],
&encoded_over_or_underflow(),
);

// signed: max_value + 0 works
Expand All @@ -716,6 +719,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("add_i{}", config.size),
&[config.i_min.clone(), int_token(-1)],
&encoded_over_or_underflow(),
);

// signed: min_value + 0 works
Expand All @@ -732,6 +736,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("sub_u{}", config.size),
&[config.u_min.clone(), uint_token(1)],
&encoded_over_or_underflow(),
);

// unsigned: min_value - 0 works
Expand All @@ -747,6 +752,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("sub_i{}", config.size),
&[config.i_min.clone(), int_token(1)],
&encoded_over_or_underflow(),
);

// signed: min_value - 0 works
Expand All @@ -762,6 +768,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("sub_i{}", config.size),
&[config.i_max.clone(), int_token(-1)],
&encoded_over_or_underflow(),
);

// signed: max_value - -0 works
Expand All @@ -778,6 +785,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("div_u{}", config.size),
&[config.u_max.clone(), uint_token(0)],
&encoded_div_or_mod_by_zero(),
);

// unsigned: 3 / 2 works
Expand All @@ -793,13 +801,15 @@ fn checked_arithmetic() {
&mut executor,
&format!("div_i{}", config.size),
&[config.i_max.clone(), int_token(0)],
&encoded_div_or_mod_by_zero(),
);

// signed: min_value / -1 fails
harness.test_function_reverts(
&mut executor,
&format!("div_i{}", config.size),
&[config.i_min.clone(), int_token(-1)],
&encoded_over_or_underflow(),
);

// signed: 3 / -2 works
Expand All @@ -816,6 +826,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("pow_u{}", config.size),
&[config.u_max.clone(), uint_token(2)],
&encoded_over_or_underflow(),
);

// unsigned: 2 ** (bit_len-1) works
Expand All @@ -833,13 +844,15 @@ fn checked_arithmetic() {
&mut executor,
&format!("pow_i{}", config.size),
&[config.i_max.clone(), uint_token(2)],
&encoded_over_or_underflow(),
);

// signed: min ** 3 fails (underflow)
harness.test_function_reverts(
&mut executor,
&format!("pow_i{}", config.size),
&[config.i_min.clone(), uint_token(3)],
&encoded_over_or_underflow(),
);

// signed: 2 ** (bit_len-2) works
Expand Down Expand Up @@ -868,6 +881,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("mod_u{}", config.size),
&[config.u_max.clone(), uint_token(0)],
&encoded_div_or_mod_by_zero(),
);

// unsigned: max_value % 2 works
Expand All @@ -883,6 +897,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("mod_i{}", config.size),
&[config.i_max.clone(), int_token(0)],
&encoded_div_or_mod_by_zero(),
);

// unsigned: max_value % 2 works
Expand Down Expand Up @@ -915,6 +930,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("mul_u{}", config.size),
&[config.u_max.clone(), uint_token(2)],
&encoded_over_or_underflow(),
);

// unsigned: max_value * 1 works
Expand All @@ -930,6 +946,7 @@ fn checked_arithmetic() {
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_max.clone(), int_token(2)],
&encoded_over_or_underflow(),
);

// signed: max_value * 1 works
Expand All @@ -945,13 +962,15 @@ fn checked_arithmetic() {
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_max.clone(), int_token(-2)],
&encoded_over_or_underflow(),
);

// signed: min_value * -2 fails
harness.test_function_reverts(
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_min.clone(), int_token(-2)],
&encoded_over_or_underflow(),
);

harness.test_function(
Expand Down
7 changes: 7 additions & 0 deletions crates/yulgen/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,10 @@ pub fn numeric_min_max() -> HashMap<Integer, (yul::Expression, yul::Expression)>
)
}
}

// Panic codes as defined by solidity
// https://docs.soliditylang.org/en/v0.8.6/control-structures.html?highlight=0x12#panic-via-assert-and-error-via-require

pub const PANIC_FAILED_ASSERTION: usize = 0x01;
pub const PANIC_OVER_OR_UNDERFLOW: usize = 0x11;
pub const PANIC_DIV_OR_MOD_BY_ZERO: usize = 0x12;
5 changes: 4 additions & 1 deletion crates/yulgen/src/mappers/functions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::constants::PANIC_FAILED_ASSERTION;
use crate::mappers::{assignments, declarations, expressions};
use crate::names;
use crate::operations::abi as abi_operations;
Expand Down Expand Up @@ -189,7 +190,9 @@ fn assert(context: &mut Context, stmt: &Node<fe::FuncStmt>) -> yul::Statement {
}
unreachable!()
}
None => statement! { if (iszero([test])) { (revert(0, 0)) } },
None => {
statement! { if (iszero([test])) { (revert_with_panic([literal_expression! {(PANIC_FAILED_ASSERTION)}])) } }
}
};
}

Expand Down
Loading

0 comments on commit bf654f2

Please sign in to comment.