From 7810fad666ee1992e3353928da7550ec5b70ad51 Mon Sep 17 00:00:00 2001 From: gluax <16431709+gluax@users.noreply.github.com> Date: Wed, 22 Jan 2025 08:18:07 -0800 Subject: [PATCH] fix: memo staking verification --- .gitignore | 3 +- crates/common/Cargo.toml | 2 +- .../common/src/msgs/staking/execute/stake.rs | 34 +++++++---- crates/common/src/msgs/staking/mod.rs | 2 + crates/common/src/msgs/staking/proof_tests.rs | 59 +++++++++++++++++++ 5 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 crates/common/src/msgs/staking/proof_tests.rs diff --git a/.gitignore b/.gitignore index 0fb9edc..be93d01 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ Cargo.lock *.pdb # Others -.DS_Store \ No newline at end of file +.DS_Store +*.test.json \ No newline at end of file diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 00f35c0..0d6df7a 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "seda-common" -version = "0.4.2" +version = "0.5.0" edition = "2021" [features] diff --git a/crates/common/src/msgs/staking/execute/stake.rs b/crates/common/src/msgs/staking/execute/stake.rs index f739d61..1233f4f 100644 --- a/crates/common/src/msgs/staking/execute/stake.rs +++ b/crates/common/src/msgs/staking/execute/stake.rs @@ -1,3 +1,7 @@ +#[cfg(not(feature = "cosmwasm"))] +use base64::{prelude::BASE64_STANDARD, Engine}; +use sha3::{Digest, Keccak256}; + use crate::{error::Result, types::*}; #[cfg_attr(feature = "cosmwasm", cosmwasm_schema::cw_serde)] @@ -10,14 +14,23 @@ pub struct Execute { } impl Execute { - fn generate_hash(memo: Option<&Bytes>, chain_id: &str, contract_addr: &str, sequence: U128) -> Hash { - crate::crypto::hash([ + fn generate_hash(memo: Option<&Bytes>, chain_id: &str, contract_addr: &str, sequence: U128) -> Result { + let mut memo_hasher = Keccak256::new(); + if let Some(memo) = memo.as_ref() { + #[cfg(feature = "cosmwasm")] + memo_hasher.update(memo.as_slice()); + #[cfg(not(feature = "cosmwasm"))] + memo_hasher.update(BASE64_STANDARD.decode(memo)?); + } + let memo_hash = memo_hasher.finalize(); + + Ok(crate::crypto::hash([ "stake".as_bytes(), - &memo.hash(), + &memo_hash, chain_id.as_bytes(), contract_addr.as_bytes(), &sequence.to_be_bytes(), - ]) + ])) } } @@ -29,12 +42,7 @@ impl VerifySelf for Execute { } fn msg_hash(&self, chain_id: &str, contract_addr: &str, sequence: Self::Extra) -> Result { - Ok(Self::generate_hash( - self.memo.as_ref(), - chain_id, - contract_addr, - sequence, - )) + Self::generate_hash(self.memo.as_ref(), chain_id, contract_addr, sequence) } } @@ -66,9 +74,9 @@ impl Execute { chain_id: &str, contract_addr: &str, sequence: U128, - ) -> ExecuteFactory { - let hash = Self::generate_hash(memo.as_ref(), chain_id, contract_addr, sequence); - ExecuteFactory { public_key, memo, hash } + ) -> Result { + let hash = Self::generate_hash(memo.as_ref(), chain_id, contract_addr, sequence)?; + Ok(ExecuteFactory { public_key, memo, hash }) } pub fn verify(&self, public_key: &[u8], chain_id: &str, contract_addr: &str, sequence: U128) -> Result<()> { diff --git a/crates/common/src/msgs/staking/mod.rs b/crates/common/src/msgs/staking/mod.rs index 93cebc4..ca0b62e 100644 --- a/crates/common/src/msgs/staking/mod.rs +++ b/crates/common/src/msgs/staking/mod.rs @@ -11,4 +11,6 @@ mod test { mod execute_tests; mod query_tests; mod types_tests; + + mod proof_tests; } diff --git a/crates/common/src/msgs/staking/proof_tests.rs b/crates/common/src/msgs/staking/proof_tests.rs new file mode 100644 index 0000000..5136a88 --- /dev/null +++ b/crates/common/src/msgs/staking/proof_tests.rs @@ -0,0 +1,59 @@ +#[cfg(not(feature = "cosmwasm"))] +use base64::Engine; + +#[cfg(not(feature = "cosmwasm"))] +use crate::crypto::VRF; + +#[cfg(not(feature = "cosmwasm"))] +const SIGNING_KEY: [u8; 32] = [ + 219, 250, 64, 32, 30, 234, 99, 114, 97, 170, 110, 172, 152, 165, 220, 129, 127, 165, 104, 32, 6, 97, 222, 68, 164, + 143, 185, 62, 132, 40, 237, 146, +]; +const PUBLIC_KEY: [u8; 33] = [ + 3, 248, 41, 12, 151, 21, 115, 241, 156, 228, 228, 226, 57, 172, 191, 44, 41, 132, 80, 177, 87, 88, 64, 180, 49, 82, + 228, 233, 77, 87, 251, 171, 251, +]; + +#[test] +#[cfg(not(feature = "cosmwasm"))] +fn test_stake_execute_factory_create_message() { + let pub_key_hex = hex::encode(PUBLIC_KEY); + + let chain_id = "test_chain_id"; + let contract_addr = "test_contract_addr"; + let sequence = 1u128.into(); + + let memo = base64::prelude::BASE64_STANDARD.encode("memo".as_bytes()); + + let factory = + super::execute::stake::Execute::factory(pub_key_hex, Some(memo.clone()), chain_id, contract_addr, sequence) + .unwrap(); + let proof = VRF.prove(&SIGNING_KEY, factory.get_hash()).unwrap(); + let msg = factory.create_message(proof); + let mut msg_json = serde_json::to_value(&msg).unwrap(); + serde_json::to_writer( + std::fs::File::create("stake_execute_factory_create_message.test.json").unwrap(), + &msg_json["stake"].take(), + ) + .unwrap(); +} + +#[test] +#[cfg(feature = "cosmwasm")] +fn test_stake_execute_verify() { + let chain_id = "test_chain_id"; + let contract_addr = "test_contract_addr"; + let sequence = 1u128.into(); + + let msg: super::execute::stake::Execute = + serde_json::from_reader(std::fs::File::open("stake_execute_factory_create_message.test.json").unwrap()) + .unwrap(); + + let memo = Some(cosmwasm_std::Binary::from("memo".as_bytes())); + assert_eq!(msg.memo, memo); + + let public_key = hex::decode(&msg.public_key).unwrap(); + assert_eq!(public_key, PUBLIC_KEY); + + msg.verify(&public_key, chain_id, contract_addr, sequence).unwrap(); +}