use alloc::borrow::Cow;
use core::fmt;
use core::ops::Deref;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sha2::{
digest::{Digest, Update},
Sha256,
};
use thiserror::Error;
use crate::{binary::Binary, forward_ref_partial_eq, HexBinary};
#[derive(
Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, JsonSchema,
)]
pub struct Addr(String);
forward_ref_partial_eq!(Addr, Addr);
impl Addr {
pub fn unchecked(input: impl Into<String>) -> Addr {
Addr(input.into())
}
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub fn into_string(self) -> String {
self.0
}
}
impl fmt::Display for Addr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self.0)
}
}
impl AsRef<str> for Addr {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl PartialEq<&str> for Addr {
fn eq(&self, rhs: &&str) -> bool {
self.0 == *rhs
}
}
impl PartialEq<Addr> for &str {
fn eq(&self, rhs: &Addr) -> bool {
*self == rhs.0
}
}
impl PartialEq<String> for Addr {
fn eq(&self, rhs: &String) -> bool {
&self.0 == rhs
}
}
impl PartialEq<Addr> for String {
fn eq(&self, rhs: &Addr) -> bool {
self == &rhs.0
}
}
impl From<Addr> for String {
fn from(addr: Addr) -> Self {
addr.0
}
}
impl From<&Addr> for String {
fn from(addr: &Addr) -> Self {
addr.0.clone()
}
}
impl From<Addr> for Cow<'_, Addr> {
fn from(addr: Addr) -> Self {
Cow::Owned(addr)
}
}
impl<'a> From<&'a Addr> for Cow<'a, Addr> {
fn from(addr: &'a Addr) -> Self {
Cow::Borrowed(addr)
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, JsonSchema)]
pub struct CanonicalAddr(pub Binary);
impl PartialEq<Binary> for CanonicalAddr {
fn eq(&self, rhs: &Binary) -> bool {
&self.0 == rhs
}
}
impl PartialEq<CanonicalAddr> for Binary {
fn eq(&self, rhs: &CanonicalAddr) -> bool {
self == &rhs.0
}
}
impl PartialEq<HexBinary> for CanonicalAddr {
fn eq(&self, rhs: &HexBinary) -> bool {
self.as_slice() == rhs.as_slice()
}
}
impl PartialEq<CanonicalAddr> for HexBinary {
fn eq(&self, rhs: &CanonicalAddr) -> bool {
self.as_slice() == rhs.0.as_slice()
}
}
impl From<&[u8]> for CanonicalAddr {
fn from(source: &[u8]) -> Self {
Self(source.into())
}
}
impl<const LENGTH: usize> From<&[u8; LENGTH]> for CanonicalAddr {
fn from(source: &[u8; LENGTH]) -> Self {
Self(source.into())
}
}
impl<const LENGTH: usize> From<[u8; LENGTH]> for CanonicalAddr {
fn from(source: [u8; LENGTH]) -> Self {
Self(source.into())
}
}
impl From<Vec<u8>> for CanonicalAddr {
fn from(source: Vec<u8>) -> Self {
Self(source.into())
}
}
impl From<CanonicalAddr> for Vec<u8> {
fn from(source: CanonicalAddr) -> Vec<u8> {
source.0.into()
}
}
impl From<Binary> for CanonicalAddr {
fn from(source: Binary) -> Self {
Self(source)
}
}
impl From<CanonicalAddr> for Binary {
fn from(source: CanonicalAddr) -> Binary {
source.0
}
}
impl From<HexBinary> for CanonicalAddr {
fn from(source: HexBinary) -> Self {
Self(source.into())
}
}
impl From<CanonicalAddr> for HexBinary {
fn from(source: CanonicalAddr) -> HexBinary {
source.0.into()
}
}
impl Deref for CanonicalAddr {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl CanonicalAddr {
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
}
impl fmt::Display for CanonicalAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for byte in self.0.as_slice() {
write!(f, "{byte:02X}")?;
}
Ok(())
}
}
#[derive(Error, Debug, PartialEq, Eq)]
pub enum Instantiate2AddressError {
InvalidChecksumLength,
InvalidSaltLength,
}
impl fmt::Display for Instantiate2AddressError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Instantiate2AddressError::InvalidChecksumLength => write!(f, "invalid checksum length"),
Instantiate2AddressError::InvalidSaltLength => write!(f, "invalid salt length"),
}
}
}
pub fn instantiate2_address(
checksum: &[u8],
creator: &CanonicalAddr,
salt: &[u8],
) -> Result<CanonicalAddr, Instantiate2AddressError> {
let msg = b"";
instantiate2_address_impl(checksum, creator, salt, msg)
}
#[doc(hidden)]
fn instantiate2_address_impl(
checksum: &[u8],
creator: &CanonicalAddr,
salt: &[u8],
msg: &[u8],
) -> Result<CanonicalAddr, Instantiate2AddressError> {
if checksum.len() != 32 {
return Err(Instantiate2AddressError::InvalidChecksumLength);
}
if salt.is_empty() || salt.len() > 64 {
return Err(Instantiate2AddressError::InvalidSaltLength);
};
let mut key = Vec::<u8>::new();
key.extend_from_slice(b"wasm\0");
key.extend_from_slice(&(checksum.len() as u64).to_be_bytes());
key.extend_from_slice(checksum);
key.extend_from_slice(&(creator.len() as u64).to_be_bytes());
key.extend_from_slice(creator);
key.extend_from_slice(&(salt.len() as u64).to_be_bytes());
key.extend_from_slice(salt);
key.extend_from_slice(&(msg.len() as u64).to_be_bytes());
key.extend_from_slice(msg);
let address_data = hash("module", &key);
Ok(address_data.into())
}
fn hash(ty: &str, key: &[u8]) -> Vec<u8> {
let inner = Sha256::digest(ty.as_bytes());
Sha256::new().chain(inner).chain(key).finalize().to_vec()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_hash_works, HexBinary};
use hex_literal::hex;
#[test]
fn addr_unchecked_works() {
let a = Addr::unchecked("123");
let aa = Addr::unchecked(String::from("123"));
let b = Addr::unchecked("be");
assert_eq!(a, aa);
assert_ne!(a, b);
}
#[test]
fn addr_as_str_works() {
let addr = Addr::unchecked("literal-string");
assert_eq!(addr.as_str(), "literal-string");
}
#[test]
fn addr_as_bytes_works() {
let addr = Addr::unchecked("literal-string");
assert_eq!(
addr.as_bytes(),
[108, 105, 116, 101, 114, 97, 108, 45, 115, 116, 114, 105, 110, 103]
);
}
#[test]
fn addr_implements_display() {
let addr = Addr::unchecked("cos934gh9034hg04g0h134");
let embedded = format!("Address: {addr}");
assert_eq!(embedded, "Address: cos934gh9034hg04g0h134");
assert_eq!(addr.to_string(), "cos934gh9034hg04g0h134");
}
#[test]
fn addr_implements_as_ref_for_str() {
let addr = Addr::unchecked("literal-string");
assert_eq!(addr.as_ref(), "literal-string");
}
#[test]
fn addr_implements_partial_eq_with_str_and_string() {
let addr = Addr::unchecked("cos934gh9034hg04g0h134");
assert_eq!(addr, "cos934gh9034hg04g0h134");
assert_eq!("cos934gh9034hg04g0h134", addr);
assert_eq!(addr, String::from("cos934gh9034hg04g0h134"));
assert_eq!(String::from("cos934gh9034hg04g0h134"), addr);
}
#[test]
fn addr_implements_partial_eq_addr_ref() {
let addr = Addr::unchecked("cos934gh9034hg04g0h134");
let addr_ref = &addr;
let addr_ref2 = &addr;
assert_eq!(addr, addr_ref);
assert_eq!(addr_ref, addr);
assert_eq!(addr_ref, addr_ref2);
}
#[test]
fn addr_implements_into_string() {
let addr = Addr::unchecked("cos934gh9034hg04g0h134");
let string: String = addr.into();
assert_eq!(string, "cos934gh9034hg04g0h134");
let addr = Addr::unchecked("cos934gh9034hg04g0h134");
let addr_ref = &addr;
let string: String = addr_ref.into();
assert_eq!(string, "cos934gh9034hg04g0h134");
}
#[test]
fn canonical_addr_from_slice() {
let bytes: &[u8] = &[0u8, 187, 61, 11, 250, 0];
let canonical_addr_slice = CanonicalAddr::from(bytes);
assert_eq!(canonical_addr_slice.as_slice(), &[0u8, 187, 61, 11, 250, 0]);
let bytes: Vec<u8> = vec![0u8, 187, 61, 11, 250, 0];
let canonical_addr_vec = CanonicalAddr::from(bytes);
assert_eq!(canonical_addr_vec.as_slice(), &[0u8, 187, 61, 11, 250, 0]);
}
#[test]
fn canonical_addr_implements_partial_eq_with_binary() {
let addr = CanonicalAddr::from([1, 2, 3]);
let bin1 = Binary::from([1, 2, 3]);
let bin2 = Binary::from([42, 43]);
assert_eq!(addr, bin1);
assert_eq!(bin1, addr);
assert_ne!(addr, bin2);
assert_ne!(bin2, addr);
}
#[test]
fn canonical_addr_implements_partial_eq_with_hex_binary() {
let addr = CanonicalAddr::from([1, 2, 3]);
let bin1 = HexBinary::from([1, 2, 3]);
let bin2 = HexBinary::from([42, 43]);
assert_eq!(addr, bin1);
assert_eq!(bin1, addr);
assert_ne!(addr, bin2);
assert_ne!(bin2, addr);
}
#[test]
fn canonical_addr_implements_from_array() {
let array = [1, 2, 3];
let addr = CanonicalAddr::from(array);
assert_eq!(addr.as_slice(), [1, 2, 3]);
let array_ref = b"foo";
let addr = CanonicalAddr::from(array_ref);
assert_eq!(addr.as_slice(), [0x66, 0x6f, 0x6f]);
}
#[test]
fn canonical_addr_implements_from_and_to_vector() {
let original = vec![0u8, 187, 61, 11, 250, 0];
let original_ptr = original.as_ptr();
let addr: CanonicalAddr = original.into();
assert_eq!(addr.as_slice(), [0u8, 187, 61, 11, 250, 0]);
assert_eq!((addr.0).0.as_ptr(), original_ptr, "must not be copied");
let original = vec![0u8, 187, 61, 11, 250, 0];
let original_ptr = original.as_ptr();
let addr = CanonicalAddr::from(original);
assert_eq!(addr.as_slice(), [0u8, 187, 61, 11, 250, 0]);
assert_eq!((addr.0).0.as_ptr(), original_ptr, "must not be copied");
let original = CanonicalAddr::from(vec![0u8, 187, 61, 11, 250, 0]);
let original_ptr = (original.0).0.as_ptr();
let vec: Vec<u8> = original.into();
assert_eq!(vec.as_slice(), [0u8, 187, 61, 11, 250, 0]);
assert_eq!(vec.as_ptr(), original_ptr, "must not be copied");
let original = CanonicalAddr::from(vec![7u8, 35, 49, 101, 0, 255]);
let original_ptr = (original.0).0.as_ptr();
let vec = Vec::<u8>::from(original);
assert_eq!(vec.as_slice(), [7u8, 35, 49, 101, 0, 255]);
assert_eq!(vec.as_ptr(), original_ptr, "must not be copied");
}
#[test]
fn canonical_addr_implements_from_and_to_binary() {
let original = Binary::from([0u8, 187, 61, 11, 250, 0]);
let original_ptr = original.as_ptr();
let addr = CanonicalAddr::from(original);
assert_eq!(addr.as_slice(), [0u8, 187, 61, 11, 250, 0]);
assert_eq!((addr.0).0.as_ptr(), original_ptr, "must not be copied");
let original = CanonicalAddr::from(vec![7u8, 35, 49, 101, 0, 255]);
let original_ptr = (original.0).0.as_ptr();
let bin = Binary::from(original);
assert_eq!(bin.as_slice(), [7u8, 35, 49, 101, 0, 255]);
assert_eq!(bin.as_ptr(), original_ptr, "must not be copied");
}
#[test]
fn canonical_addr_implements_from_and_to_hex_binary() {
let original = HexBinary::from([0u8, 187, 61, 11, 250, 0]);
let original_ptr = original.as_ptr();
let addr = CanonicalAddr::from(original);
assert_eq!(addr.as_slice(), [0u8, 187, 61, 11, 250, 0]);
assert_eq!((addr.0).0.as_ptr(), original_ptr, "must not be copied");
let original = CanonicalAddr::from(vec![7u8, 35, 49, 101, 0, 255]);
let original_ptr = (original.0).0.as_ptr();
let bin = HexBinary::from(original);
assert_eq!(bin.as_slice(), [7u8, 35, 49, 101, 0, 255]);
assert_eq!(bin.as_ptr(), original_ptr, "must not be copied");
}
#[test]
fn canonical_addr_len() {
let bytes: &[u8] = &[0u8, 187, 61, 11, 250, 0];
let canonical_addr = CanonicalAddr::from(bytes);
assert_eq!(canonical_addr.len(), bytes.len());
}
#[test]
fn canonical_addr_is_empty() {
let bytes: &[u8] = &[0u8, 187, 61, 11, 250, 0];
let canonical_addr = CanonicalAddr::from(bytes);
assert!(!canonical_addr.is_empty());
let empty_canonical_addr = CanonicalAddr::from(vec![]);
assert!(empty_canonical_addr.is_empty());
}
#[test]
fn canonical_addr_implements_display() {
let bytes: &[u8] = &[
0x12, 0x03, 0xab, 0x00, 0xff,
];
let address = CanonicalAddr::from(bytes);
let embedded = format!("Address: {address}");
assert_eq!(embedded, "Address: 1203AB00FF");
assert_eq!(address.to_string(), "1203AB00FF");
}
#[test]
fn canonical_addr_implements_deref() {
let bytes: &[u8] = &[0u8, 187, 61, 11, 250, 0];
let canonical_addr = CanonicalAddr::from(bytes);
assert_eq!(*canonical_addr, [0u8, 187, 61, 11, 250, 0]);
let bytes: &[u8] = &[0u8, 187, 61, 11, 250, 0];
let canonical_addr = CanonicalAddr::from(bytes);
assert_eq!(canonical_addr.len(), 6);
let canonical_addr_slice: &[u8] = &canonical_addr;
assert_eq!(canonical_addr_slice, &[0u8, 187, 61, 11, 250, 0]);
}
#[test]
fn canonical_addr_implements_hash_eq() {
let alice = CanonicalAddr::from([0, 187, 61, 11, 250, 0]);
let bob = CanonicalAddr::from([16, 21, 33, 0, 255, 9]);
assert_hash_works!(alice, bob);
}
fn flexible<'a>(a: impl Into<Cow<'a, Addr>>) -> String {
a.into().into_owned().to_string()
}
#[test]
fn addr_into_cow() {
let value = "wasmeucn0ur0ncny2308ry";
let addr = Addr::unchecked(value);
assert_eq!(value, &flexible(&addr));
assert_eq!(value, &flexible(addr));
}
#[test]
fn instantiate2_address_impl_works() {
let checksum1 =
HexBinary::from_hex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5")
.unwrap();
let creator1 = CanonicalAddr::from(hex!("9999999999aaaaaaaaaabbbbbbbbbbcccccccccc"));
let salt1 = hex!("61");
let salt2 = hex!("aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbccddeeffffeeddbbccddaa66551155aaaabbcc787878789900aabbbbcc221100acadae");
let msg1: &[u8] = b"";
let msg2: &[u8] = b"{}";
let msg3: &[u8] = b"{\"some\":123,\"structure\":{\"nested\":[\"ok\",true]}}";
let expected = CanonicalAddr::from(hex!(
"5e865d3e45ad3e961f77fd77d46543417ced44d924dc3e079b5415ff6775f847"
));
assert_eq!(
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg1).unwrap(),
expected
);
let expected = CanonicalAddr::from(hex!(
"0995499608947a5281e2c7ebd71bdb26a1ad981946dad57f6c4d3ee35de77835"
));
assert_eq!(
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg2).unwrap(),
expected
);
let expected = CanonicalAddr::from(hex!(
"83326e554723b15bac664ceabc8a5887e27003abe9fbd992af8c7bcea4745167"
));
assert_eq!(
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg3).unwrap(),
expected
);
let expected = CanonicalAddr::from(hex!(
"9384c6248c0bb171e306fd7da0993ec1e20eba006452a3a9e078883eb3594564"
));
assert_eq!(
instantiate2_address_impl(&checksum1, &creator1, &salt2, b"").unwrap(),
expected
);
let empty = Vec::<u8>::new();
assert!(matches!(
instantiate2_address_impl(&checksum1, &creator1, &empty, b"").unwrap_err(),
Instantiate2AddressError::InvalidSaltLength
));
let too_long = vec![0x11; 65];
assert!(matches!(
instantiate2_address_impl(&checksum1, &creator1, &too_long, b"").unwrap_err(),
Instantiate2AddressError::InvalidSaltLength
));
let broken_cs = hex!("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2");
assert!(matches!(
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"").unwrap_err(),
Instantiate2AddressError::InvalidChecksumLength
));
let broken_cs = hex!("");
assert!(matches!(
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"").unwrap_err(),
Instantiate2AddressError::InvalidChecksumLength
));
let broken_cs = hex!("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2aaaa");
assert!(matches!(
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"").unwrap_err(),
Instantiate2AddressError::InvalidChecksumLength
));
}
#[test]
fn instantiate2_address_impl_works_for_cosmjs_testvectors() {
const COSMOS_ED25519_TESTS_JSON: &str = "./testdata/instantiate2_addresses.json";
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
struct In {
checksum: HexBinary,
creator: String,
creator_data: HexBinary,
salt: HexBinary,
msg: Option<String>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
struct Intermediate {
key: HexBinary,
address_data: HexBinary,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
struct Out {
address: String,
}
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct Row {
#[serde(rename = "in")]
input: In,
intermediate: Intermediate,
out: Out,
}
fn read_tests() -> Vec<Row> {
use std::fs::File;
use std::io::BufReader;
let file = File::open(COSMOS_ED25519_TESTS_JSON).unwrap();
let reader = BufReader::new(file);
serde_json::from_reader(reader).unwrap()
}
for Row {
input,
intermediate,
out: _,
} in read_tests()
{
let msg = input.msg.map(|msg| msg.into_bytes()).unwrap_or_default();
let addr = instantiate2_address_impl(
&input.checksum,
&input.creator_data.into(),
&input.salt,
&msg,
)
.unwrap();
assert_eq!(addr, intermediate.address_data);
}
}
#[test]
fn hash_works() {
let expected = [
195, 235, 23, 251, 9, 99, 177, 195, 81, 122, 182, 124, 36, 113, 245, 156, 76, 188, 221,
83, 181, 192, 227, 82, 100, 177, 161, 133, 240, 160, 5, 25,
];
assert_eq!(hash("1", &[1]), expected);
}
}