Skip to content

Commit

Permalink
support bech32m Message Id format
Browse files Browse the repository at this point in the history
  • Loading branch information
anstylian committed Nov 20, 2024
1 parent 25c3edf commit f164ff1
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ axelar-wasm-std = { version = "^1.0.0", path = "packages/axelar-wasm-std" }
axelar-wasm-std-derive = { version = "^1.0.0", path = "packages/axelar-wasm-std-derive" }
axelarnet-gateway = { version = "^1.0.0", path = "contracts/axelarnet-gateway" }
bcs = "0.1.5"
bech32 = "0.11.0"
client = { version = "^1.0.0", path = "packages/client" }
coordinator = { version = "^1.1.0", path = "contracts/coordinator" }
cosmwasm-schema = "1.5.5"
Expand Down
1 change: 1 addition & 0 deletions contracts/voting-verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ thiserror = { workspace = true }
[dev-dependencies]
alloy-primitives = { version = "0.7.7", features = ["getrandom"] }
assert_ok = { workspace = true }
bech32 = { workspace = true }
cw-multi-test = "0.15.1"
goldie = { workspace = true }
integration-tests = { workspace = true }
Expand Down
11 changes: 11 additions & 0 deletions contracts/voting-verifier/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ mod test {
assert_err_contains, err_contains, nonempty, MajorityThreshold, Threshold,
VerificationStatus,
};
use bech32::{Bech32m, Hrp};
use cosmwasm_std::testing::{
mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage,
};
Expand Down Expand Up @@ -265,6 +266,16 @@ mod test {
.to_string()
.parse()
.unwrap(),
MessageIdFormat::Bech32m => {
let data = format!("{id}-{index}");
let prefix = "bech32m";
let hrp = Hrp::parse(prefix).expect("valid hrp");
bech32::encode::<Bech32m>(hrp, data.as_bytes())
.unwrap()
.to_string()
.parse()
.unwrap()
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion contracts/voting-verifier/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::str::FromStr;
use std::vec::Vec;

use axelar_wasm_std::msg_id::{
Base58SolanaTxSignatureAndEventIndex, Base58TxDigestAndEventIndex, HexTxHash,
Base58SolanaTxSignatureAndEventIndex, Base58TxDigestAndEventIndex, Bech32mFormat, HexTxHash,
HexTxHashAndEventIndex, MessageIdFormat,
};
use axelar_wasm_std::voting::{PollId, Vote};
Expand Down Expand Up @@ -186,6 +186,11 @@ fn parse_message_id(

Ok((id.tx_hash_as_hex(), 0))
}
MessageIdFormat::Bech32m => {
let bech32m_message_id = Bech32mFormat::from_str(message_id)
.map_err(|_| ContractError::InvalidMessageID(message_id.into()))?;
Ok((bech32m_message_id.to_string().try_into()?, 0))
}
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/axelar-wasm-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \
[dependencies]
alloy-primitives = { workspace = true }
axelar-wasm-std-derive = { workspace = true, optional = true }
bech32 = { workspace = true }
bs58 = "0.5.1"
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
Expand Down
68 changes: 68 additions & 0 deletions packages/axelar-wasm-std/src/msg_id/bech32m.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::fmt::{self, Display};
use std::str::FromStr;

use bech32::primitives::decode::CheckedHrpstring;
use bech32::Bech32m;
use error_stack::{bail, Report, ResultExt};
use lazy_static::lazy_static;
use regex::Regex;

use super::Error;

pub struct Bech32mFormat {
pub encoded: String,
}

impl Bech32mFormat {
pub fn new(encoded: String) -> Self {
Self { encoded }
}
}

const PATTERN: &str = "^([0-9ac-hj-np-z]{8,90})$";

lazy_static! {
static ref REGEX: Regex = Regex::new(PATTERN).expect("invalid regex");
}

impl FromStr for Bech32mFormat {
type Err = Report<Error>;

fn from_str(message_id: &str) -> Result<Self, Self::Err>
where
Self: Sized,
{
let (_, [string]) = REGEX
.captures(message_id)
.ok_or(Error::InvalidMessageID {
id: message_id.to_string(),
expected_format: "Bech32m".to_string(),
})?
.extract();

verify_bech32m(string)?;

Ok(Self {
encoded: string.to_string(),
})
}
}

impl Display for Bech32mFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.encoded)
}
}

fn verify_bech32m(input: &str) -> Result<(), Report<Error>> {
let checked = CheckedHrpstring::new::<Bech32m>(input)
.change_context(Error::InvalidBech32m(input.to_string()))?;

if checked.data_part_ascii_no_checksum().is_empty() {
bail!(Error::InvalidBech32m(format!(
"Message Id is missing the data part: '{input}'"
)));
}

Ok(())
}
21 changes: 21 additions & 0 deletions packages/axelar-wasm-std/src/msg_id/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use error_stack::Report;

pub use self::base_58_event_index::Base58TxDigestAndEventIndex;
pub use self::base_58_solana_event_index::Base58SolanaTxSignatureAndEventIndex;
pub use self::bech32m::Bech32mFormat;
pub use self::tx_hash::HexTxHash;
pub use self::tx_hash_event_index::HexTxHashAndEventIndex;

mod base_58_event_index;
mod base_58_solana_event_index;
mod bech32m;
mod tx_hash;
mod tx_hash_event_index;

Expand All @@ -25,6 +27,8 @@ pub enum Error {
InvalidTxHash(String),
#[error("invalid tx digest in message id '{0}'")]
InvalidTxDigest(String),
#[error("Invalid bech32m: '{0}'")]
InvalidBech32m(String),
}

/// Any message id format must implement this trait.
Expand All @@ -45,6 +49,7 @@ pub enum MessageIdFormat {
Base58TxDigestAndEventIndex,
Base58SolanaTxSignatureAndEventIndex,
HexTxHash,
Bech32m,
}

// function the router calls to verify msg ids
Expand All @@ -60,6 +65,7 @@ pub fn verify_msg_id(message_id: &str, format: &MessageIdFormat) -> Result<(), R
Base58SolanaTxSignatureAndEventIndex::from_str(message_id).map(|_| ())
}
MessageIdFormat::HexTxHash => HexTxHash::from_str(message_id).map(|_| ()),
MessageIdFormat::Bech32m => Bech32mFormat::from_str(message_id).map(|_| ()),
}
}

Expand Down Expand Up @@ -111,4 +117,19 @@ mod test {
.to_string();
assert!(verify_msg_id(&msg_id, &MessageIdFormat::HexTxHashAndEventIndex).is_err());
}

#[test]
fn should_verify_bech32m() {
let message_id = "at1hs0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaju";
assert!(verify_msg_id(message_id, &MessageIdFormat::Bech32m).is_ok());
}

#[test]
fn should_not_verify_bech32m() {
let message_id = "aths0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaju";
assert!(verify_msg_id(message_id, &MessageIdFormat::Bech32m).is_err());

let message_id = "ath1s0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaj";
assert!(verify_msg_id(message_id, &MessageIdFormat::Bech32m).is_err());
}
}

0 comments on commit f164ff1

Please sign in to comment.