Skip to content

Commit

Permalink
HPl-19: apply eip191 (#30)
Browse files Browse the repository at this point in the history
* wip

* fix: clippy

* fix: apply eip191

* fix: no-std
  • Loading branch information
byeongsu-hong authored Aug 24, 2023
1 parent dd2c0f3 commit 7f61b62
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 28 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ serde-json-wasm = "1.0.0"
thiserror = { version = "1.0.37" }
cw-multi-test = "0.16.5"
cosmwasm-schema = "1.2.7"
cosmwasm-crypto = "1.2.7"
prost = { version = "0.11.9", default-features = false, features = [
"prost-derive",
] }
prost-types = { version = "0.11.9", default-features = false }

k256 = { version = "0.13.1", default-features = false, features = ["ecdsa"] }
anyhow = { version = "1.0.71", features = ["backtrace"] }
k256 = { version = "0.13.1", features = ["std", "ecdsa"] }
digest = { version = "0.10.7" }
hex-literal = { version = "0.4.1" }
osmosis-test-tube = { version = "16.0.0" }

Expand Down
2 changes: 1 addition & 1 deletion contracts/ism-multisig/src/execute/threshold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn set_thresholds(
})
.collect::<StdResult<_>>()?;

Ok(Response::new().add_events(events.into_iter()))
Ok(Response::new().add_events(events))
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion contracts/ism-multisig/src/execute/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub fn enroll_validators(
}
}

Ok(Response::new().add_events(events.into_iter()))
Ok(Response::new().add_events(events))
}

pub fn unenroll_validator(
Expand Down
4 changes: 1 addition & 3 deletions contracts/ism-multisig/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ pub fn verify_message(
.map(|s| (v.signer_pubkey.clone(), s))
.collect::<Vec<(Binary, Binary)>>()
})
.fold(vec![], |acc, item| {
acc.into_iter().chain(item.into_iter()).collect()
});
.fold(vec![], |acc, item| acc.into_iter().chain(item).collect());

let success: u8 = verifiable_cases
.into_iter()
Expand Down
3 changes: 2 additions & 1 deletion contracts/va/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ backtraces = ["cosmwasm-std/backtraces"]
library = []

[dependencies]
k256 = { workspace = true }
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
Expand All @@ -35,5 +36,5 @@ hpl-interface = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
k256 = { workspace = true }
sha3 = { workspace = true }
digest = { workspace = true }
31 changes: 17 additions & 14 deletions contracts/va/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{
ensure, ensure_eq, to_binary, Binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, Order,
QueryResponse, Response, StdResult,
ensure, to_binary, Binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, Order, QueryResponse,
Response, StdResult,
};

use hpl_interface::{
Expand All @@ -12,10 +12,11 @@ use hpl_interface::{
InstantiateMsg, MigrateMsg, QueryMsg,
},
};
use k256::{ecdsa::VerifyingKey, EncodedPoint};

use crate::{
error::ContractError,
pub_to_addr,
eth_hash, pub_to_addr,
state::{
ADDR_PREFIX, LOCAL_DOMAIN, MAILBOX, REPLAY_PROTECITONS, STORAGE_LOCATIONS, VALIDATORS,
},
Expand All @@ -38,12 +39,6 @@ pub fn announcement_hash(mut domain_hash: Vec<u8>, storage_location: &str) -> Bi
bz.append(&mut domain_hash);
bz.append(&mut storage_location.as_bytes().to_vec());

let mut prehash = keccak256_hash(&bz);

let mut bz = vec![];
bz.append(&mut "\x19Ethereum Signed Message:\n".as_bytes().to_vec());
bz.append(&mut prehash.0);

keccak256_hash(&bz)
}

Expand Down Expand Up @@ -106,21 +101,29 @@ pub fn execute(
let mailbox = MAILBOX.load(deps.storage)?;

// make digest
let announcement_hash = announcement_hash(
let message_hash = eth_hash(announcement_hash(
domain_hash(local_domain, mailbox.as_str())?.0,
&storage_location,
);
))?;

// recover pubkey from signature & verify
let recovered = deps.api.secp256k1_recover_pubkey(
&announcement_hash,
&message_hash,
&signature.as_slice()[..64],
// We subs 27 according to this - https://eips.ethereum.org/EIPS/eip-155
signature[64] - 27,
)?;

let recovered_addr = pub_to_addr(Binary(recovered), &ADDR_PREFIX.load(deps.storage)?)?;
ensure_eq!(recovered_addr, validator, ContractError::Unauthorized {});
let public_key =
VerifyingKey::from_encoded_point(&EncodedPoint::from_bytes(recovered).unwrap())
.expect("invalid recovered public key");
let public_key_compressed = public_key.to_encoded_point(true).as_bytes().to_vec();

let recovered_addr = pub_to_addr(
Binary(public_key_compressed),
&ADDR_PREFIX.load(deps.storage)?,
)?;
ensure!(recovered_addr == validator, ContractError::VerifyFailed {});

// save validator if not saved yet
if !VALIDATORS.has(deps.storage, validator.clone()) {
Expand Down
3 changes: 3 additions & 0 deletions contracts/va/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ pub enum ContractError {

#[error("InvalidPubKey")]
InvalidPubKey {},

#[error("VerifyFailed")]
VerifyFailed {},
}
11 changes: 11 additions & 0 deletions contracts/va/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bech32::ToBase32;
use cosmwasm_std::{Binary, HexBinary};
use error::ContractError;
use hpl_interface::types::keccak256_hash;
use ripemd::Ripemd160;
use sha2::{Digest, Sha256};

Expand All @@ -11,10 +12,20 @@ pub mod state;
#[cfg(test)]
mod tests;

const PREFIX: &str = "\x19Ethereum Signed Message:\n";

// version info for migration info
pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn eth_hash(message: Binary) -> Result<Binary, ContractError> {
let mut eth_message = format!("{PREFIX}{}", message.len()).into_bytes();
eth_message.extend_from_slice(&message);
let message_hash = keccak256_hash(&eth_message);

Ok(message_hash)
}

pub fn sha256_digest(bz: impl AsRef<[u8]>) -> Result<[u8; 32], ContractError> {
let mut hasher = Sha256::new();

Expand Down
69 changes: 62 additions & 7 deletions contracts/va/src/tests/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use cosmwasm_std::{
testing::{mock_dependencies, mock_env},
Addr, Binary, Response,
Addr, Binary, HexBinary, Response,
};
use hpl_interface::types::bech32_encode;
use k256::{
ecdsa::{RecoveryId, Signature, SigningKey},
elliptic_curve::{rand_core::OsRng, sec1::ToEncodedPoint},
schnorr::signature::hazmat::PrehashSigner,
SecretKey,
};

use crate::{
contract::{announcement_hash, domain_hash},
error::ContractError,
pub_to_addr, pub_to_addr_binary,
eth_hash, pub_to_addr, pub_to_addr_binary,
state::{ADDR_PREFIX, LOCAL_DOMAIN, MAILBOX, STORAGE_LOCATIONS, VALIDATORS},
};

Expand Down Expand Up @@ -86,15 +85,18 @@ fn test_announce() -> anyhow::Result<()> {
let public_key = secret_key.public_key();
let signing_key = SigningKey::from(secret_key);

let public_key_bz = Binary(public_key.to_encoded_point(false).as_bytes().to_vec());
let public_key_bz = Binary(public_key.to_encoded_point(true).as_bytes().to_vec());
let addr_binary = pub_to_addr_binary(public_key_bz.clone())?;
let public_key_addr = Addr::unchecked(pub_to_addr(public_key_bz, testdata.addr_prefix)?);

let announcement_hash = announcement_hash(
let verify_digest = eth_hash(announcement_hash(
domain_hash(testdata.local_domain, testdata.mailbox.as_str())?.0,
testdata.storage_location,
);
let signature = pack_signature(signing_key.sign_prehash(&announcement_hash)?);
))?;
let signature = signing_key
.sign_prehash_recoverable(&verify_digest)
.unwrap();
let signature = pack_signature(signature);

va.announce(
&testdata.deployer,
Expand All @@ -112,3 +114,56 @@ fn test_announce() -> anyhow::Result<()> {

Ok(())
}

#[test]
fn test_announce_external() -> anyhow::Result<()> {
let cases = [
(
"0xf9e25a6be80f6d48727e42381fc3c3b7834c0cb4",
"0x62634b0c56b57fef1c27f25039cfb872875a9eeeb42d80a034f8d6b55ed20d09",
26658,
"file:///var/folders/3v/g38z040x54x8l6b160vv66b40000gn/T/.tmp7XoxND/checkpoint",
"0x6c30e1072f0e23694d3a3a96dc41fc4d17636ce145e83adef3224a6f4732c2db715407b42478c581b6ac1b79e64807a7748935d398a33bf4b73d37924c293c941b",
),
(
"0xf9e25a6be80f6d48727e42381fc3c3b7834c0cb4",
"0x62634b0c56b57fef1c27f25039cfb872875a9eeeb42d80a034f8d6b55ed20d09",
26657,
"file:///var/folders/3v/g38z040x54x8l6b160vv66b40000gn/T/.tmpBJPK8C/checkpoint",
"0x76c637d605f683734c672c0437f14ae48520e85fb68b0c0b9c28069f183e3bfc46f0de0655f06937c74b5a0a15f5b8fe37f1d1ad4dd8b64dc55307a2103fedad1c",
),
];

let remove_hex_prefix = |v: String| -> String {
if v.starts_with("0x") {
v.strip_prefix("0x").unwrap().to_string()
} else {
v
}
};

for (validator, mailbox, domain, location, signature) in cases {
let testdata = TestData {
mailbox: Addr::unchecked(bech32_encode(
"osmo",
HexBinary::from_hex(&remove_hex_prefix(mailbox.to_string()))?.as_slice(),
)?),
local_domain: domain,

..Default::default()
};

let mut va = VA::new(mock_dependencies(), mock_env());

testdata.init(&mut va)?;

va.announce(
&testdata.deployer,
HexBinary::from_hex(&remove_hex_prefix(validator.to_string()))?,
location,
HexBinary::from_hex(&remove_hex_prefix(signature.to_string()))?.into(),
)?;
}

Ok(())
}

0 comments on commit 7f61b62

Please sign in to comment.