Skip to content

Commit

Permalink
more restricted implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
anstylian committed Nov 23, 2024
1 parent 6434f72 commit 5414e33
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 27 deletions.
6 changes: 4 additions & 2 deletions contracts/voting-verifier/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,11 @@ mod test {
.to_string()
.parse()
.unwrap(),
MessageIdFormat::Bech32m => {
MessageIdFormat::Bech32m {
prefix,
length: _length,
} => {
let data = format!("{id}-{index}");
let prefix = "bech32m";
let hrp = Hrp::parse(prefix).expect("valid hrp");
bech32::encode::<Bech32m>(hrp, data.as_bytes())
.unwrap()
Expand Down
4 changes: 2 additions & 2 deletions contracts/voting-verifier/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ fn parse_message_id(

Ok((id.tx_hash_as_hex(), 0))
}
MessageIdFormat::Bech32m => {
let bech32m_message_id = Bech32mFormat::from_str(message_id)
MessageIdFormat::Bech32m { prefix, length } => {
let bech32m_message_id = Bech32mFormat::from_str(prefix, *length, message_id)
.map_err(|_| ContractError::InvalidMessageID(message_id.into()))?;
Ok((bech32m_message_id.to_string().try_into()?, 0))
}
Expand Down
95 changes: 77 additions & 18 deletions packages/axelar-wasm-std/src/msg_id/bech32m.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
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 error_stack::{bail, ensure, Report, ResultExt};
use regex::Regex;

use super::Error;

#[derive(Debug)]
pub struct Bech32mFormat {
pub encoded: String,
}
Expand All @@ -17,30 +16,36 @@ impl Bech32mFormat {
pub fn new(encoded: String) -> Self {
Self { encoded }
}
}

const PATTERN: &str = "^([0-9ac-hj-np-z]{8,90})$";
pub fn from_str(prefix: &str, length: usize, message_id: &str) -> Result<Self, Report<Error>> {
ensure!(
!prefix.is_empty(),
Error::InvalidBech32m("Prefix should not be empty".to_string())
);

lazy_static! {
static ref REGEX: Regex = Regex::new(PATTERN).expect("invalid regex");
}
ensure!(
prefix.len() <= 83,
Error::InvalidBech32m("Prefix size should be between 1 and 83".to_string())
);

let pattern = format!(
"^({}1[02-9ac-hj-np-z]{{{}}})$",
prefix,
length.saturating_sub(prefix.len()).saturating_sub(1)
);

impl FromStr for Bech32mFormat {
type Err = Report<Error>;
let regex = Regex::new(pattern.as_str())
.change_context(Error::InvalidBech32m("Failed to create regex".to_string()))?;

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

verify_bech32m(string)?;
verify_bech32m(string, prefix)?;

Ok(Self {
encoded: string.to_string(),
Expand All @@ -54,10 +59,17 @@ impl Display for Bech32mFormat {
}
}

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

ensure!(
checked.hrp().as_str() == expected_prefix,
Error::InvalidBech32m(format!(
"Expected prefix '{expected_prefix}' not found: '{input}'"
))
);

if checked.data_part_ascii_no_checksum().is_empty() {
bail!(Error::InvalidBech32m(format!(
"Message Id is missing the data part: '{input}'"
Expand All @@ -66,3 +78,50 @@ fn verify_bech32m(input: &str) -> Result<(), Report<Error>> {

Ok(())
}

#[cfg(test)]
mod test {
use bech32::Hrp;
use rand::Rng;

use super::*;

#[test]
fn should_pass_bech32m() {
let mut rng = rand::thread_rng();

let chars = [
'q', 'p', 'z', 'r', 'y', '9', 'x', '8', 'g', 'f', '2', 't', 'v', 'd', 'w', '0', 's',
'3', 'j', 'n', '5', '4', 'k', 'h', 'c', 'e', '6', 'm', 'u', 'a', '7', 'l',
];
let char_set = chars.len();

for _ in 0..100 {
let hrp_str = (0..rng.gen_range(1..=83))
.map(|_| chars[rng.gen_range(0..char_set)])
.collect::<String>();

let data = (0..80)
.map(|_| char::from(rng.gen_range(32..=126)))
.collect::<String>();

let hrp = Hrp::parse(hrp_str.as_str()).expect("valid hrp");
let string =
bech32::encode::<Bech32m>(hrp, data.as_bytes()).expect("failed to encode string");

let res = Bech32mFormat::from_str(hrp.as_str(), string.len(), string.as_str());

assert!(res.is_ok());
}
}

#[test]
fn should_not_pass_bech32m() {
let message_id = "at1hs0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaju";
assert!(Bech32mFormat::from_str("at", 61, message_id).is_ok());
let message_id = "aths0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaju";
assert!(Bech32mFormat::from_str("at", 60, message_id).is_err());
let message_id = "at1as0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaju";
assert!(Bech32mFormat::from_str("at", 61, message_id).is_err());
}
}
33 changes: 28 additions & 5 deletions packages/axelar-wasm-std/src/msg_id/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub enum MessageIdFormat {
Base58TxDigestAndEventIndex,
Base58SolanaTxSignatureAndEventIndex,
HexTxHash,
Bech32m,
Bech32m { prefix: String, length: usize },
}

// function the router calls to verify msg ids
Expand All @@ -65,7 +65,9 @@ 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(|_| ()),
MessageIdFormat::Bech32m { prefix, length } => {
Bech32mFormat::from_str(prefix, *length, message_id).map(|_| ())
}
}
}

Expand Down Expand Up @@ -121,15 +123,36 @@ mod test {
#[test]
fn should_verify_bech32m() {
let message_id = "at1hs0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaju";
assert!(verify_msg_id(message_id, &MessageIdFormat::Bech32m).is_ok());
assert!(verify_msg_id(
message_id,
&MessageIdFormat::Bech32m {
prefix: "at".to_string(),
length: 61
}
)
.is_ok());
}

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

let message_id = "ath1s0xk375g4kvw53rcem9nyjsdw5lsv94fl065n77cpt0774nsyysdecaj";
assert!(verify_msg_id(message_id, &MessageIdFormat::Bech32m).is_err());
assert!(verify_msg_id(
message_id,
&MessageIdFormat::Bech32m {
prefix: "at".to_string(),
length: 61
}
)
.is_err());
}
}

0 comments on commit 5414e33

Please sign in to comment.