From 03feb369441929581ab6bf07794ee4852e36655e Mon Sep 17 00:00:00 2001 From: mriise Date: Thu, 12 Jan 2023 17:30:35 -0800 Subject: [PATCH] add precompile test contract, use it for actor_type --- actors/evm/tests/precompile.rs | 140 +++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 31 deletions(-) diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 8f2031855e..dfc5a9cd1d 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -1,5 +1,7 @@ mod asm; +use std::fmt::Debug; + use evm::interpreter::{address::EthAddress, U256}; use fil_actor_evm as evm; use fil_actors_runtime::test_utils::{ @@ -63,58 +65,126 @@ fn test_precompile_hash() { ); } -#[test] -fn test_native_actor_type() { - let bytecode = { +struct PrecompileTest { + pub expected_return: Vec, + pub expected_exit_code: PrecompileExit, + pub precompile_number: u8, + pub output_size: u8, + pub input: Vec, + pub gas_avaliable: u64, +} + +#[repr(u8)] +enum PrecompileExit { + Reverted = 0, + Success = 1, +} + +impl Debug for PrecompileTest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PrecompileTest") + .field("expected_return", &hex::encode(&self.expected_return)) + .field("expected_exit_code", &(self.expected_exit_code as u8)) + .field("precompile_number", &format!("{:x}", self.precompile_number)) + .field("output_size", &self.output_size) + .field("precompile_number", &self.output_size) + .field("input", &hex::encode(&self.input)) + .field("gas_avaliable", &self.gas_avaliable) + .finish() + } +} + +impl PrecompileTest { + fn run_test(&self, rt: &mut MockRuntime) { + rt.expect_gas_available(self.gas_avaliable); + log::trace!("{:#?}", &self); + // first byte is precompile number, second is output buffer size, rest is input to precompile + let result = util::invoke_contract( + rt, + &[vec![self.precompile_number, self.output_size], self.input.clone()].concat(), + ); + log::trace!("returned: {:?}", hex::encode(&result)); + rt.verify(); + assert_eq!(self.expected_exit_code as u8, result[0]); + assert_eq!(&self.expected_return, &result[1..]); + rt.reset(); + } + + fn test_runner_bytecode() -> Vec { let init = ""; let body = r#" - -# get call payload size -calldatasize # store payload to mem 0x00 -push1 0x00 -push1 0x00 +calldatasize +push1 0x00 # input offset +push1 0x00 # dst offset calldatacopy # out size +push1 0x01 # second byte of input +mload +push1 0x08 +push1 0x1f +mul # 31*8 bits +shr + # out off -push1 0x20 -push1 0xA0 +push2 0xA0 # in size -# in off +push1 0x02 calldatasize -push1 0x00 +sub +# in off +push1 0x02 # value push1 0x00 -# dst (get_actor_type precompile) -push20 0xfe00000000000000000000000000000000000004 +# precompile address +push20 0xfe00000000000000000000000000000000000000 + +push1 0x00 # first byte of input is index +mload +push1 0x08 +push1 0x1f +mul # 31*8 bits +shr + +add # gas push1 0x00 call -# copy result to mem 0x00 (overwrites input data) +# write exit code memory +push1 0x00 # offset +mstore8 + +# write precompile return to memory returndatasize -push1 0x00 -push1 0x00 +push1 0x00 # input offset +push1 0x01 # dst offset returndatacopy -# return +# size returndatasize +push1 0x01 +add +# offset push1 0x00 return "#; - asm::new_contract("native_actor_type", init, body).unwrap() - }; + asm::new_contract("precompile_tester", init, body).unwrap() + } +} +#[test] +fn test_native_actor_type() { use evm::interpreter::precompiles::NativeType; - let mut rt = util::construct_and_verify(bytecode); + let mut rt = util::construct_and_verify(PrecompileTest::test_runner_bytecode()); // 0x88 is an EVM actor let evm_target = FILAddress::new_id(0x88); @@ -141,11 +211,15 @@ return rt.set_address_actor_type(other_target, *MULTISIG_ACTOR_CODE_ID); fn test_type(rt: &mut MockRuntime, id: FILAddress, expected: NativeType) { - rt.expect_gas_available(10_000_000_000u64); - let result = util::invoke_contract(rt, &id_to_vec(&id)); - rt.verify(); - assert_eq!(&U256::from(expected as u32).to_bytes(), result.as_slice()); - rt.reset(); + let test = PrecompileTest { + precompile_number: 4, + input: id_to_vec(&id), + output_size: 32, + expected_exit_code: PrecompileExit::Success, + expected_return: U256::from(expected as u32).to_bytes().to_vec(), + gas_avaliable: 10_000_000_000, + }; + test.run_test(rt); } test_type(&mut rt, evm_target, NativeType::EVMContract); @@ -157,11 +231,15 @@ return test_type(&mut rt, FILAddress::new_id(10101), NativeType::NonExistent); // invalid format address - rt.expect_gas_available(10_000_000_000u64); - let result = util::invoke_contract(&mut rt, &[0xff; 64]); - rt.verify(); - assert!(result.is_empty()); - rt.reset(); + let test = PrecompileTest { + precompile_number: 4, + input: vec![0xff; 64], + output_size: 32, + expected_exit_code: PrecompileExit::Reverted, + expected_return: vec![], + gas_avaliable: 10_000_000_000, + }; + test.run_test(&mut rt); } fn resolve_address_contract() -> Vec {