Skip to content

Commit

Permalink
cosmrs: fully variable-width AccountId (#204)
Browse files Browse the repository at this point in the history
This commit changes the constructor and `to_bytes` methods to fully
support variable-width account IDs, as added in cosmos/cosmos-sdk#8363.

This is a breaking change.
  • Loading branch information
tony-iqlusion authored Apr 21, 2022
1 parent ea21b5b commit 1e8300b
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 30 deletions.
2 changes: 1 addition & 1 deletion cosmrs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cosmrs"
version = "0.5.1"
version = "0.6.0-pre"
authors = ["Tony Arcieri <tony@iqlusion.io>"]
license = "Apache-2.0"
repository = "https://github.com/cosmos/cosmos-rust/tree/main/cosmrs"
Expand Down
65 changes: 37 additions & 28 deletions cosmrs/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,28 @@ pub struct AccountId {
impl AccountId {
/// Create an [`AccountId`] with the given human-readable prefix and
/// public key hash.
pub fn new(prefix: &str, bytes: [u8; tendermint::account::LENGTH]) -> Result<Self> {
pub fn new(prefix: &str, bytes: &[u8]) -> Result<Self> {
let id = bech32::encode(prefix, &bytes);

// TODO(tarcieri): ensure this is the proper validation for an account prefix
if prefix.chars().all(|c| matches!(c, 'a'..='z')) {
if !prefix.chars().all(|c| matches!(c, 'a'..='z')) {
return Err(Error::AccountId { id })
.wrap_err("expected prefix to be lowercase alphabetical characters only");
}

if matches!(bytes.len(), 1..=MAX_ADDRESS_LENGTH) {
Ok(Self {
bech32: id,
hrp_length: prefix.len(),
})
} else {
Err(Error::AccountId { id })
.wrap_err("expected prefix to be lowercase alphabetical characters only")
Err(Error::AccountId { id }).wrap_err_with(|| {
format!(
"account ID should be at most {} bytes long, but was {} bytes long",
MAX_ADDRESS_LENGTH,
bytes.len()
)
})
}
}

Expand All @@ -43,11 +53,10 @@ impl AccountId {
}

/// Decode an account ID from Bech32 to an inner byte value.
pub fn to_bytes(&self) -> [u8; tendermint::account::LENGTH] {
pub fn to_bytes(&self) -> Vec<u8> {
bech32::decode(&self.bech32)
.ok()
.and_then(|result| result.1.try_into().ok())
.expect("malformed Bech32 AccountId")
.1
}
}

Expand All @@ -74,33 +83,33 @@ impl FromStr for AccountId {

fn from_str(s: &str) -> Result<Self> {
let (hrp, bytes) = bech32::decode(s).wrap_err("failed to decode bech32")?;

if matches!(bytes.len(), 1..=MAX_ADDRESS_LENGTH) {
Ok(Self {
bech32: s.to_owned(),
hrp_length: hrp.len(),
})
} else {
Err(Error::AccountId { id: s.to_owned() }).wrap_err_with(|| {
format!(
"account ID should be at most {} bytes long, but was {} bytes long",
MAX_ADDRESS_LENGTH,
bytes.len()
)
})
}
Self::new(&hrp, &bytes)
}
}

impl From<AccountId> for tendermint::account::Id {
fn from(id: AccountId) -> tendermint::account::Id {
tendermint::account::Id::from(&id)
impl TryFrom<AccountId> for tendermint::account::Id {
type Error = ErrorReport;

fn try_from(id: AccountId) -> Result<tendermint::account::Id> {
tendermint::account::Id::try_from(&id)
}
}

impl From<&AccountId> for tendermint::account::Id {
fn from(id: &AccountId) -> tendermint::account::Id {
tendermint::account::Id::new(id.to_bytes())
// TODO(tarcieri): non-fixed-width account ID type
impl TryFrom<&AccountId> for tendermint::account::Id {
type Error = ErrorReport;

fn try_from(id: &AccountId) -> Result<tendermint::account::Id> {
let bytes = id.to_bytes();
let len = bytes.len();

match bytes.try_into() {
Ok(bytes) => Ok(tendermint::account::Id::new(bytes)),
_ => Err(Error::AccountId {
id: id.bech32.clone(),
})
.wrap_err_with(|| format!("invalid length for account ID: {}", len)),
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion cosmrs/src/crypto/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl PublicKey {
match &self.0 {
tendermint::PublicKey::Secp256k1(encoded_point) => {
let id = tendermint::account::Id::from(*encoded_point);
AccountId::new(prefix, id.as_bytes().try_into()?)
AccountId::new(prefix, id.as_bytes())
}
_ => Err(Error::Crypto.into()),
}
Expand Down

0 comments on commit 1e8300b

Please sign in to comment.