From a68e99024d630d721f801d2684ab462981e8da49 Mon Sep 17 00:00:00 2001 From: Alex North <445306+anorth@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:18:58 +1200 Subject: [PATCH] Add tests for beacon randomness precompile. --- actors/evm/src/interpreter/precompiles/mod.rs | 1 - actors/evm/tests/precompile.rs | 38 +++++++++++++++++++ actors/evm/tests/util.rs | 3 +- runtime/src/runtime/fvm.rs | 8 ++-- runtime/src/test_utils.rs | 24 +++++++----- 5 files changed, 59 insertions(+), 15 deletions(-) diff --git a/actors/evm/src/interpreter/precompiles/mod.rs b/actors/evm/src/interpreter/precompiles/mod.rs index fc939a919..74625fc0f 100644 --- a/actors/evm/src/interpreter/precompiles/mod.rs +++ b/actors/evm/src/interpreter/precompiles/mod.rs @@ -48,7 +48,6 @@ impl Precompiles { None, // 0xfe00..04 get_actor_type DISABLED Some(call_actor_id::), // 0xfe00..05 Some(get_randomness::), // 0xfe00..06 - ]); /// EVM specific precompiles diff --git a/actors/evm/tests/precompile.rs b/actors/evm/tests/precompile.rs index 073e50940..f0915ae42 100644 --- a/actors/evm/tests/precompile.rs +++ b/actors/evm/tests/precompile.rs @@ -223,6 +223,44 @@ fn test_resolve_delegated() { rt.reset(); } +#[test] +fn test_precompile_randomness() { + let (init, body) = PrecompileTest::test_runner_assembly(); + let rt = + util::construct_and_verify(asm::new_contract("precompile-tester", &init, &body).unwrap()); + let rand_epoch = 100; + let rand_epoch_u256 = U256::from(rand_epoch); + { + // Underlying syscall succeeds. + let result = U256::from(0xdeadbeefu32).to_bytes(); + let test = PrecompileTest { + precompile_address: NativePrecompile::GetRandomness.eth_address(), + output_size: 32, + expected_exit_code: PrecompileExit::Success, + gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::StaticCall, + input: rand_epoch_u256.to_bytes().to_vec(), + expected_return: result.to_vec(), + }; + rt.expect_get_beacon_randomness(rand_epoch, result, ExitCode::OK); + test.run_test(&rt); + } + { + // Underlying syscall fails. + let test = PrecompileTest { + precompile_address: NativePrecompile::GetRandomness.eth_address(), + output_size: 32, + expected_exit_code: PrecompileExit::Reverted, // Precompile reverts due to syscall failure + gas_avaliable: 10_000_000_000, + call_op: util::PrecompileCallOpcode::StaticCall, + input: rand_epoch_u256.to_bytes().to_vec(), + expected_return: vec![], + }; + rt.expect_get_beacon_randomness(rand_epoch, [0u8; 32], ExitCode::USR_ILLEGAL_ARGUMENT); + test.run_test(&rt); + } +} + #[test] fn test_precompile_transfer() { let (init, body) = util::PrecompileTest::test_runner_assembly(); diff --git a/actors/evm/tests/util.rs b/actors/evm/tests/util.rs index 335612fbb..b91ddc9f3 100644 --- a/actors/evm/tests/util.rs +++ b/actors/evm/tests/util.rs @@ -112,8 +112,9 @@ pub enum NativePrecompile { ResolveAddress = 1, LookupDelegatedAddress = 2, CallActor = 3, - GetActorType = 4, + GetActorTypeDISABLED = 4, CallActorId = 5, + GetRandomness = 6, } #[allow(dead_code)] diff --git a/runtime/src/runtime/fvm.rs b/runtime/src/runtime/fvm.rs index 12b9dfbae..810196b6c 100644 --- a/runtime/src/runtime/fvm.rs +++ b/runtime/src/runtime/fvm.rs @@ -234,8 +234,8 @@ where ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { let digest = fvm::rand::get_chain_randomness(rand_epoch).map_err(|e| { match e { - ErrorNumber::LimitExceeded => { - actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) + ErrorNumber::LimitExceeded | ErrorNumber::IllegalArgument => { + actor_error!(illegal_argument; "invalid lookback epoch: {}", e) } e => actor_error!(assertion_failed; "get chain randomness failed with an unexpected error: {}", e), } @@ -271,8 +271,8 @@ where ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { fvm::rand::get_beacon_randomness(rand_epoch).map_err(|e| { match e { - ErrorNumber::LimitExceeded => { - actor_error!(illegal_argument; "randomness lookback exceeded: {}", e) + ErrorNumber::LimitExceeded | ErrorNumber::IllegalArgument => { + actor_error!(illegal_argument; "invalid lookback epoch: {}", e) } e => actor_error!(assertion_failed; "get beacon randomness failed with an unexpected error: {}", e), } diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index e196c07f8..643b245a4 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -432,6 +432,7 @@ pub struct ExpectRandomness { pub struct ExpectGetBeacon { epoch: ChainEpoch, out: [u8; RANDOMNESS_LENGTH], + exit_code: ExitCode, } #[derive(Debug)] @@ -761,8 +762,13 @@ impl MockRuntime { } #[allow(dead_code)] - pub fn expect_get_beacon_randomness(&self, epoch: ChainEpoch, out: [u8; RANDOMNESS_LENGTH]) { - let a = ExpectGetBeacon { epoch, out }; + pub fn expect_get_beacon_randomness( + &self, + epoch: ChainEpoch, + out: [u8; RANDOMNESS_LENGTH], + exit_code: ExitCode, + ) { + let a = ExpectGetBeacon { epoch, out, exit_code }; self.expectations.borrow_mut().expect_get_beacon_randomness.push_back(a); } @@ -1035,7 +1041,6 @@ impl Runtime for MockRuntime { .pop_front() .expect("unexpected call to get_randomness_from_tickets"); - assert!(epoch <= *self.epoch.borrow(), "attempt to get randomness from future"); assert_eq!( expected.tag, tag, "unexpected domain separation tag, expected: {:?}, actual: {:?}", @@ -1068,7 +1073,6 @@ impl Runtime for MockRuntime { .pop_front() .expect("unexpected call to get_randomness_from_beacon"); - assert!(epoch <= *self.epoch.borrow(), "attempt to get randomness from future"); assert_eq!( expected.tag, tag, "unexpected domain separation tag, expected: {:?}, actual: {:?}", @@ -1092,20 +1096,22 @@ impl Runtime for MockRuntime { &self, epoch: ChainEpoch, ) -> Result<[u8; RANDOMNESS_LENGTH], ActorError> { - let expected = self + let exp = self .expectations .borrow_mut() .expect_get_beacon_randomness .pop_front() .expect("unexpected call to get_randomness_from_beacon"); - assert!(epoch <= *self.epoch.borrow(), "attempt to get randomness from future"); assert_eq!( - expected.epoch, epoch, + exp.epoch, epoch, "unexpected epoch, expected: {:?}, actual: {:?}", - expected.epoch, epoch + exp.epoch, epoch ); - Ok(expected.out) + if exp.exit_code != ExitCode::OK { + return Err(ActorError::unchecked(exp.exit_code, "Expected Failure".to_string())); + } + Ok(exp.out) } fn create(&self, obj: &T) -> Result<(), ActorError> {