-
Notifications
You must be signed in to change notification settings - Fork 417
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: delegate keypair encoding to cosmrs (#2887)
### Description Delegates bech32 encoding, decoding, and cosmos address building from a private key to cosmrs/tendermint upstream crates. The only place where this wasn't doable was the tests, because of a cyclic dependency - left a TODO comment there. As part of this, renamed `rust/chains/hyperlane-cosmos/src/libs/verify.rs` -> `rust/chains/hyperlane-cosmos/src/libs/address.rs` ~~**Warning**: We should re-enable e2e and make sure everything still works locally, since the actual `encode`/`decode` functions that end up being called _are_ different~~ ### Testing Tested manually EVM <> Duality (both ways)
- Loading branch information
1 parent
453734a
commit 2a7d6d6
Showing
27 changed files
with
256 additions
and
266 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
use std::str::FromStr; | ||
|
||
use cosmrs::{ | ||
crypto::{secp256k1::SigningKey, PublicKey}, | ||
AccountId, | ||
}; | ||
use derive_new::new; | ||
use hyperlane_core::{ChainCommunicationError, ChainResult, Error::Overflow, H256}; | ||
use tendermint::account::Id as TendermintAccountId; | ||
use tendermint::public_key::PublicKey as TendermintPublicKey; | ||
|
||
/// Wrapper around the cosmrs AccountId type that abstracts bech32 encoding | ||
#[derive(new, Debug)] | ||
pub struct CosmosAddress { | ||
/// Bech32 encoded cosmos account | ||
account_id: AccountId, | ||
/// Hex representation (digest) of cosmos account | ||
digest: H256, | ||
} | ||
|
||
impl CosmosAddress { | ||
/// Returns a Bitcoin style address: RIPEMD160(SHA256(pubkey)) | ||
/// Source: https://github.com/cosmos/cosmos-sdk/blob/177e7f45959215b0b4e85babb7c8264eaceae052/crypto/keys/secp256k1/secp256k1.go#L154 | ||
pub fn from_pubkey(pubkey: PublicKey, prefix: &str) -> ChainResult<Self> { | ||
// Get the inner type | ||
let tendermint_pubkey = TendermintPublicKey::from(pubkey); | ||
// Get the RIPEMD160(SHA256(pubkey)) | ||
let tendermint_id = TendermintAccountId::from(tendermint_pubkey); | ||
// Bech32 encoding | ||
let account_id = AccountId::new(prefix, tendermint_id.as_bytes())?; | ||
// Hex digest | ||
let digest = Self::bech32_decode(account_id.clone())?; | ||
Ok(CosmosAddress::new(account_id, digest)) | ||
} | ||
|
||
/// Creates a wrapper arround a cosmrs AccountId from a private key byte array | ||
pub fn from_privkey(priv_key: &[u8], prefix: &str) -> ChainResult<Self> { | ||
let pubkey = SigningKey::from_slice(priv_key)?.public_key(); | ||
Self::from_pubkey(pubkey, prefix) | ||
} | ||
|
||
/// Creates a wrapper arround a cosmrs AccountId from a H256 digest | ||
/// | ||
/// - digest: H256 digest (hex representation of address) | ||
/// - prefix: Bech32 prefix | ||
pub fn from_h256(digest: H256, prefix: &str) -> ChainResult<Self> { | ||
// This is the hex-encoded version of the address | ||
let bytes = digest.as_bytes(); | ||
// Bech32 encode it | ||
let account_id = AccountId::new(prefix, bytes)?; | ||
Ok(CosmosAddress::new(account_id, digest)) | ||
} | ||
|
||
/// Builds a H256 digest from a cosmos AccountId (Bech32 encoding) | ||
fn bech32_decode(account_id: AccountId) -> ChainResult<H256> { | ||
// Temporarily set the digest to a default value as a placeholder. | ||
// Can't implement H256::try_from for AccountId to avoid this. | ||
let cosmos_address = CosmosAddress::new(account_id, Default::default()); | ||
H256::try_from(&cosmos_address) | ||
} | ||
|
||
/// String representation of a cosmos AccountId | ||
pub fn address(&self) -> String { | ||
self.account_id.to_string() | ||
} | ||
|
||
/// H256 digest of the cosmos AccountId | ||
pub fn digest(&self) -> H256 { | ||
self.digest | ||
} | ||
} | ||
|
||
impl TryFrom<&CosmosAddress> for H256 { | ||
type Error = ChainCommunicationError; | ||
|
||
fn try_from(cosmos_address: &CosmosAddress) -> Result<Self, Self::Error> { | ||
// `to_bytes()` decodes the Bech32 into a hex, represented as a byte vec | ||
let bytes = cosmos_address.account_id.to_bytes(); | ||
let h256_len = H256::len_bytes(); | ||
let Some(start_point) = h256_len.checked_sub(bytes.len()) else { | ||
// input is too large to fit in a H256 | ||
return Err(Overflow.into()); | ||
}; | ||
let mut empty_hash = H256::default(); | ||
let result = empty_hash.as_bytes_mut(); | ||
result[start_point..].copy_from_slice(bytes.as_slice()); | ||
Ok(H256::from_slice(result)) | ||
} | ||
} | ||
|
||
impl FromStr for CosmosAddress { | ||
type Err = ChainCommunicationError; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let account_id = AccountId::from_str(s)?; | ||
let digest = Self::bech32_decode(account_id.clone())?; | ||
Ok(Self::new(account_id, digest)) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
pub mod test { | ||
use hyperlane_core::utils::hex_or_base58_to_h256; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_bech32_decode() { | ||
let addr = "dual1pk99xge6q94qtu3568x3qhp68zzv0mx7za4ct008ks36qhx5tvss3qawfh"; | ||
let cosmos_address = CosmosAddress::from_str(addr).unwrap(); | ||
assert_eq!( | ||
cosmos_address.digest, | ||
H256::from_str("0d8a53233a016a05f234d1cd105c3a3884c7ecde176b85bde7b423a05cd45b21") | ||
.unwrap() | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_bech32_decode_from_cosmos_key() { | ||
let hex_key = "0x5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26"; | ||
let key = hex_or_base58_to_h256(hex_key).unwrap(); | ||
let prefix = "neutron"; | ||
let addr = CosmosAddress::from_privkey(key.as_bytes(), prefix) | ||
.expect("Cosmos address creation failed"); | ||
assert_eq!( | ||
addr.address(), | ||
"neutron1kknekjxg0ear00dky5ykzs8wwp2gz62z9s6aaj" | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_bech32_encode_from_h256() { | ||
let hex_key = "0x1b16866227825a5166eb44031cdcf6568b3e80b52f2806e01b89a34dc90ae616"; | ||
let key = hex_or_base58_to_h256(hex_key).unwrap(); | ||
let prefix = "dual"; | ||
let addr = CosmosAddress::from_h256(key, prefix).expect("Cosmos address creation failed"); | ||
assert_eq!( | ||
addr.address(), | ||
"dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq" | ||
); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,2 @@ | ||
/// This module contains all the verification variables the libraries used by the Hyperlane Cosmos chain. | ||
pub mod verify; | ||
|
||
/// This module contains all the Binary variables used by the Hyperlane Cosmos chain. | ||
pub mod binary; | ||
pub mod address; |
Oops, something went wrong.