From a4bac80612cd244fa3f372d00bfa39788b04083e Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Fri, 20 Oct 2023 13:06:15 +0100 Subject: [PATCH] TEST: Hash checkpoint as tuple --- .../testing/contract-test/tests/staking/machine.rs | 14 +++++--------- fendermint/vm/actor_interface/src/ipc.rs | 14 +++++++++++--- fendermint/vm/interpreter/src/fvm/state/ipc.rs | 9 ++++++++- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/fendermint/testing/contract-test/tests/staking/machine.rs b/fendermint/testing/contract-test/tests/staking/machine.rs index 8b1e37ac8..43faba8b0 100644 --- a/fendermint/testing/contract-test/tests/staking/machine.rs +++ b/fendermint/testing/contract-test/tests/staking/machine.rs @@ -242,7 +242,8 @@ impl StateMachine for StakingMachine { // eprintln!("\n> CMD: CKPT cn={}", checkpoint.next_configuration_number); // Build the checkpoint payload. - // No messages. + + // No messages in this test. let cross_messages_hash = abi_hash::>(Vec::new()); let (root, route) = subnet_id_to_eth(&system.subnet_id).unwrap(); @@ -255,14 +256,9 @@ impl StateMachine for StakingMachine { cross_messages_hash, }; - let checkpoint_hash = abi_hash(checkpoint.clone()); - // eprintln!("CHECKPOINT = {:?}", checkpoint); - // eprintln!("CHECKPOINT HASH = {}", hex::encode(checkpoint_hash)); - // eprintln!( - // "CHECKPOINT ABI = {}", - // hex::encode(ethers::abi::encode(&checkpoint.clone().into_tokens())) - // ); - // eprintln!("CHECKPOINT TOKENS = {:?}", checkpoint.clone().into_tokens()); + // Checkpoint has to be a tuple to match Solidity. + let checkpoint_tuple = (checkpoint.clone(),); + let checkpoint_hash = abi_hash(checkpoint_tuple); let mut signatures = Vec::new(); diff --git a/fendermint/vm/actor_interface/src/ipc.rs b/fendermint/vm/actor_interface/src/ipc.rs index aec7636f9..2f8a60b0c 100644 --- a/fendermint/vm/actor_interface/src/ipc.rs +++ b/fendermint/vm/actor_interface/src/ipc.rs @@ -348,7 +348,7 @@ pub mod subnet { #[cfg(test)] mod tests { - use ethers::abi::Tokenize; + use ethers::abi::{AbiType, Tokenize}; use ethers::core::types::Bytes; use ipc_actors_abis::subnet_actor_manager_facet::{BottomUpCheckpoint, SubnetID}; @@ -378,13 +378,21 @@ pub mod subnet { ], }; + let param_type = BottomUpCheckpoint::param_type(); + // Captured value of `abi.encode` in Solidity. let expected_abi: Bytes = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000156b736f342ab34d9afe4234a92bdb190c35b2e8d822d9601b00b9d7089b190f010000000000000000000000000000000000000000000000000000000000000001569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd000000000000000000000000000000000000000000000000abc8e314f58b4de5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000007b11cf9ca8ccee13bb3d003c97af5c18434067a90000000000000000000000003d9019b8bf3bfd5e979ddc3b2761be54af867c47".parse().unwrap(); - let observed_tokens = checkpoint.into_tokens(); + // XXX: It doesn't work with `decode_whole`. + let expected_tokens = + ethers::abi::decode(&[param_type], &expected_abi).expect("invalid Solidity ABI"); + + // The data needs to be wrapped into a tuple. + let observed_tokens = (checkpoint,).into_tokens(); let observed_abi: Bytes = ethers::abi::encode(&observed_tokens).into(); - assert_eq!(observed_abi, expected_abi) + assert_eq!(observed_tokens, expected_tokens); + assert_eq!(observed_abi, expected_abi); } } } diff --git a/fendermint/vm/interpreter/src/fvm/state/ipc.rs b/fendermint/vm/interpreter/src/fvm/state/ipc.rs index c3e12efa5..3046c91cc 100644 --- a/fendermint/vm/interpreter/src/fvm/state/ipc.rs +++ b/fendermint/vm/interpreter/src/fvm/state/ipc.rs @@ -94,6 +94,8 @@ impl GatewayCaller { height: u64, ) -> anyhow::Result<[u8; 32]> { let msgs = self.bottom_up_msgs(state, height)?; + + // TODO: Not sure this is the right way to hash a vector. Ok(abi_hash(msgs)) } @@ -154,7 +156,9 @@ impl GatewayCaller { let height = checkpoint.block_height; let weight = et::U256::from(validator.power.0); - let hash = abi_hash(checkpoint); + // Checkpoint has to be hashed as a tuple. + let hash = abi_hash((checkpoint,)); + let signature = sign_secp256k1(secret_key, &hash); let signature = from_fvm::to_eth_signature(&signature).context("invalid signature")?; let signature = et::Bytes::from(signature.to_vec()); @@ -185,6 +189,9 @@ impl GatewayCaller { } /// Hash some value in the same way we'd hash it in Solidity. +/// +/// Be careful that if we have to hash a single struct, Solidity's `abi.encode` +/// function will treat it as a tuple. pub fn abi_hash(value: T) -> [u8; 32] { keccak256(ethers::abi::encode(&value.into_tokens())) }