Skip to content

Commit

Permalink
Merge pull request #476 from cburgdorf/christoph/feat/panics
Browse files Browse the repository at this point in the history
Implement panics
  • Loading branch information
g-r-a-n-t authored Jul 9, 2021
2 parents c36445f + 15cb46c commit 95b833c
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 93 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)
12 changes: 12 additions & 0 deletions crates/test-files/fixtures/solidity/revert_test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ error StructError(Bag data);

contract Foo {

function revert_bare() public pure {
revert();
}

function revert_me() public pure {
revert("Not enough Ether provided.");
}
Expand Down Expand Up @@ -50,4 +54,12 @@ contract Foo {
revert StructError(Bag ({ val1: 100, val2: -100, val3: true }));
}

function panic_divide_by_zero(uint256 val1, uint256 val2) public pure {
val1 / val2;
}

function panic_assert() public pure {
assert(false);
}

}
22 changes: 16 additions & 6 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 Expand Up @@ -257,11 +267,11 @@ pub fn deploy_solidity_contract(

#[allow(dead_code)]
pub fn encode_error_reason(reason: &str) -> Vec<u8> {
encode_error("Error(string)", &[string_token(reason)])
encode_revert("Error(string)", &[string_token(reason)])
}

#[allow(dead_code)]
pub fn encode_error(selector: &str, input: &[ethabi::Token]) -> Vec<u8> {
pub fn encode_revert(selector: &str, input: &[ethabi::Token]) -> Vec<u8> {
let mut data = String::new();
for param in input {
let encoded = match param {
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
84 changes: 41 additions & 43 deletions crates/tests/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,16 @@ fn test_revert() {
with_executor(&|mut executor| {
let harness = deploy_contract(&mut executor, "revert.fe", "Foo", &[]);

let exit = harness.capture_call(&mut executor, "bar", &[]);

assert!(matches!(
exit,
evm::Capture::Exit((evm::ExitReason::Revert(_), _))
));

let exit2 = harness.capture_call(&mut executor, "revert_custom_error", &[]);
validate_revert(harness.capture_call(&mut executor, "bar", &[]), &[]);

validate_revert(
exit2,
&encode_error("Error(uint256,bool)", &[uint_token(1), bool_token(true)]),
harness.capture_call(&mut executor, "revert_custom_error", &[]),
&encode_revert("Error(uint256,bool)", &[uint_token(1), bool_token(true)]),
);

let exit3 = harness.capture_call(&mut executor, "revert_other_error", &[]);

validate_revert(
exit3,
&encode_error(
harness.capture_call(&mut executor, "revert_other_error", &[]),
&encode_revert(
"OtherError(uint256,bool)",
&[uint_token(1), bool_token(true)],
),
Expand All @@ -86,43 +77,31 @@ fn test_assert() {
with_executor(&|mut executor| {
let harness = deploy_contract(&mut executor, "assert.fe", "Foo", &[]);

let exit1 = harness.capture_call(&mut executor, "bar", &[uint_token(4)]);

match exit1 {
evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => assert_eq!(output.len(), 0),
_ => panic!("Did not revert correctly"),
}

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

assert!(matches!(
exit2,
harness.capture_call(&mut executor, "bar", &[uint_token(42)]),
evm::Capture::Exit((evm::ExitReason::Succeed(_), _))
));

let exit3 =
harness.capture_call(&mut executor, "revert_with_static_string", &[uint_token(4)]);

match exit3 {
evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => {
assert_eq!(output, encode_error_reason("Must be greater than five"))
}
_ => panic!("Did not revert correctly"),
}
validate_revert(
harness.capture_call(&mut executor, "revert_with_static_string", &[uint_token(4)]),
&encode_error_reason("Must be greater than five"),
);

let reason = "A very looooooooooooooong reason that consumes multiple words";
let exit4 = harness.capture_call(
&mut executor,
"revert_with",
&[uint_token(4), string_token(&reason)],
);

match exit4 {
evm::Capture::Exit((evm::ExitReason::Revert(_), output)) => {
assert_eq!(output, encode_error_reason(reason))
}
_ => panic!("Did not revert correctly"),
}
validate_revert(
harness.capture_call(
&mut executor,
"revert_with",
&[uint_token(4), string_token(&reason)],
),
&encode_error_reason(reason),
);
})
}

Expand Down Expand Up @@ -703,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 @@ -722,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 @@ -737,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 @@ -753,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 @@ -768,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 @@ -783,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 @@ -799,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 @@ -814,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 @@ -837,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 @@ -854,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 @@ -889,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 @@ -904,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 @@ -936,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 @@ -951,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 @@ -966,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
Loading

0 comments on commit 95b833c

Please sign in to comment.