-
Notifications
You must be signed in to change notification settings - Fork 412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: delegate keypair encoding to cosmrs #2887
Changes from all commits
19d6ed3
d7bf02d
c283034
a366115
f5b19b3
f8d4c7a
ad875bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. may be a good place to have some quick unit tests if we're uncertain about the encoding / decoding like iiuc you flag in the PR description |
||
/// 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.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😎