Skip to content

Commit b1cc063

Browse files
authored
Merge pull request #2371 from CosmWasm/instantiate2_address_impl-len
Add new len param to instantiate2_address_impl
2 parents d7948db + 7c14883 commit b1cc063

File tree

3 files changed

+123
-13
lines changed

3 files changed

+123
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ and this project adheres to
2424
- cosmwasm-std: Document safety invariants of the internal memory repr ([#2344])
2525
- cosmwasm-std: Enforce non-null pointers using `ptr::NonNull` in the internal
2626
memory repr ([#2344])
27+
- cosmwasm-std: Make `instantiate2_address_impl` public and let it take a new
28+
`len` argument to allow truncating address data as part of the generation
29+
process. ([#2155])
2730

2831
## Fixed
2932

3033
- cosmwasm-schema: The schema export now doesn't overwrite existing
3134
`additionalProperties` values anymore ([#2310])
3235

36+
[#2155]: https://github.com/CosmWasm/cosmwasm/issues/2155
3337
[#2268]: https://github.com/CosmWasm/cosmwasm/issues/2268
3438
[#2269]: https://github.com/CosmWasm/cosmwasm/issues/2269
3539
[#2310]: https://github.com/CosmWasm/cosmwasm/pull/2310

packages/std/src/addresses.rs

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -313,18 +313,50 @@ pub fn instantiate2_address(
313313
// Non-empty msg values are discouraged.
314314
// See https://medium.com/cosmwasm/dev-note-3-limitations-of-instantiate2-and-how-to-deal-with-them-a3f946874230.
315315
let msg = b"";
316-
instantiate2_address_impl(checksum, creator, salt, msg)
316+
let len = 32;
317+
instantiate2_address_impl(checksum, creator, salt, msg, len)
317318
}
318319

319320
/// The instantiate2 address derivation implementation. This API is used for
320321
/// testing purposes only. The `msg` field is discouraged and should not be used.
321322
/// Use [`instantiate2_address`].
323+
///
324+
/// `len` is the address length on bytes. The resulting address data will be truncated to
325+
/// that length. A value > 32 is invalid because [`hash`] returns only 32 bytes of data.
326+
/// A value of 0 is considered invalid because it indicates a bug.
327+
/// For ADR-028 compatibility, 32 must be used.
328+
/// However, some chains use 20 for compatibility with the Ethereum ecosystem.
329+
/// Using any other value than 32 requires a coordination with the chain implementation.
330+
/// See also <https://github.com/CosmWasm/cosmwasm/issues/2155>.
331+
///
332+
/// ## Examples
333+
///
334+
/// ```
335+
/// use cosmwasm_std::{instantiate2_address_impl, CanonicalAddr, HexBinary, Instantiate2AddressError};
336+
///
337+
/// fn instantiate2_address_evm_compatible(
338+
/// checksum: &[u8],
339+
/// creator: &CanonicalAddr,
340+
/// salt: &[u8],
341+
/// ) -> Result<CanonicalAddr, Instantiate2AddressError> {
342+
/// instantiate2_address_impl(checksum, creator, salt, b"", 20)
343+
/// }
344+
///
345+
/// let checksum = HexBinary::from_hex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5").unwrap();
346+
/// let creator = CanonicalAddr::from(HexBinary::from_hex("9999999999aaaaaaaaaabbbbbbbbbbcccccccccc").unwrap());
347+
/// let salt = b"\x61";
348+
///
349+
/// let address = instantiate2_address_evm_compatible(&checksum, &creator, salt).unwrap();
350+
/// assert_eq!(address, HexBinary::from_hex("5e865d3e45ad3e961f77fd77d46543417ced44d9").unwrap());
351+
/// ```
322352
#[doc(hidden)]
323-
fn instantiate2_address_impl(
353+
#[inline] // Only call this through a wrapper like instantiate2_address or a custom instantiate2_address_evm_compatible
354+
pub fn instantiate2_address_impl(
324355
checksum: &[u8],
325356
creator: &CanonicalAddr,
326357
salt: &[u8],
327358
msg: &[u8],
359+
len: usize,
328360
) -> Result<CanonicalAddr, Instantiate2AddressError> {
329361
if checksum.len() != 32 {
330362
return Err(Instantiate2AddressError::InvalidChecksumLength);
@@ -344,7 +376,17 @@ fn instantiate2_address_impl(
344376
key.extend_from_slice(salt);
345377
key.extend_from_slice(&(msg.len() as u64).to_be_bytes());
346378
key.extend_from_slice(msg);
347-
let address_data = hash("module", &key);
379+
let mut address_data = hash("module", &key);
380+
381+
// Use the first `len` bytes
382+
// Fingers crossed Rust can optimize this whole block out in the default case (32), because otherwise
383+
// truncate will do a resize for len == address_data.len(), see https://github.com/rust-lang/rust/issues/76089
384+
if len != 32 {
385+
debug_assert!(len <= 32);
386+
debug_assert!(len > 0);
387+
address_data.truncate(len);
388+
}
389+
348390
Ok(address_data.into())
349391
}
350392

@@ -650,7 +692,7 @@ mod tests {
650692
"5e865d3e45ad3e961f77fd77d46543417ced44d924dc3e079b5415ff6775f847"
651693
));
652694
assert_eq!(
653-
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg1).unwrap(),
695+
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg1, 32).unwrap(),
654696
expected
655697
);
656698

@@ -659,7 +701,7 @@ mod tests {
659701
"0995499608947a5281e2c7ebd71bdb26a1ad981946dad57f6c4d3ee35de77835"
660702
));
661703
assert_eq!(
662-
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg2).unwrap(),
704+
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg2, 32).unwrap(),
663705
expected
664706
);
665707

@@ -668,7 +710,7 @@ mod tests {
668710
"83326e554723b15bac664ceabc8a5887e27003abe9fbd992af8c7bcea4745167"
669711
));
670712
assert_eq!(
671-
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg3).unwrap(),
713+
instantiate2_address_impl(&checksum1, &creator1, &salt1, msg3, 32).unwrap(),
672714
expected
673715
);
674716

@@ -677,40 +719,101 @@ mod tests {
677719
"9384c6248c0bb171e306fd7da0993ec1e20eba006452a3a9e078883eb3594564"
678720
));
679721
assert_eq!(
680-
instantiate2_address_impl(&checksum1, &creator1, &salt2, b"").unwrap(),
722+
instantiate2_address_impl(&checksum1, &creator1, &salt2, b"", 32).unwrap(),
681723
expected
682724
);
683725

684726
// Salt too short or too long
685727
let empty = Vec::<u8>::new();
686728
assert!(matches!(
687-
instantiate2_address_impl(&checksum1, &creator1, &empty, b"").unwrap_err(),
729+
instantiate2_address_impl(&checksum1, &creator1, &empty, b"", 32).unwrap_err(),
688730
Instantiate2AddressError::InvalidSaltLength
689731
));
690732
let too_long = vec![0x11; 65];
691733
assert!(matches!(
692-
instantiate2_address_impl(&checksum1, &creator1, &too_long, b"").unwrap_err(),
734+
instantiate2_address_impl(&checksum1, &creator1, &too_long, b"", 32).unwrap_err(),
693735
Instantiate2AddressError::InvalidSaltLength
694736
));
695737

696738
// invalid checksum length
697739
let broken_cs = hex!("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2");
698740
assert!(matches!(
699-
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"").unwrap_err(),
741+
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"", 32).unwrap_err(),
700742
Instantiate2AddressError::InvalidChecksumLength
701743
));
702744
let broken_cs = hex!("");
703745
assert!(matches!(
704-
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"").unwrap_err(),
746+
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"", 32).unwrap_err(),
705747
Instantiate2AddressError::InvalidChecksumLength
706748
));
707749
let broken_cs = hex!("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2aaaa");
708750
assert!(matches!(
709-
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"").unwrap_err(),
751+
instantiate2_address_impl(&broken_cs, &creator1, &salt1, b"", 32).unwrap_err(),
710752
Instantiate2AddressError::InvalidChecksumLength
711753
));
712754
}
713755

756+
#[test]
757+
fn instantiate2_address_impl_truncates_address_data_to_first_len_bytes() {
758+
// test data from above
759+
let checksum =
760+
HexBinary::from_hex("13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5")
761+
.unwrap();
762+
let creator = CanonicalAddr::from(hex!("9999999999aaaaaaaaaabbbbbbbbbbcccccccccc"));
763+
let salt = hex!("61");
764+
765+
let data = [
766+
(
767+
32,
768+
"5e865d3e45ad3e961f77fd77d46543417ced44d924dc3e079b5415ff6775f847",
769+
),
770+
(
771+
31,
772+
"5e865d3e45ad3e961f77fd77d46543417ced44d924dc3e079b5415ff6775f8",
773+
),
774+
(
775+
30,
776+
"5e865d3e45ad3e961f77fd77d46543417ced44d924dc3e079b5415ff6775",
777+
),
778+
(21, "5e865d3e45ad3e961f77fd77d46543417ced44d924"),
779+
(20, "5e865d3e45ad3e961f77fd77d46543417ced44d9"),
780+
(19, "5e865d3e45ad3e961f77fd77d46543417ced44"),
781+
(16, "5e865d3e45ad3e961f77fd77d4654341"),
782+
(8, "5e865d3e45ad3e96"),
783+
(1, "5e"),
784+
];
785+
786+
for (len, expected) in data {
787+
let expected = CanonicalAddr::from(HexBinary::from_hex(expected).unwrap());
788+
assert_eq!(
789+
instantiate2_address_impl(&checksum, &creator, &salt, b"", len).unwrap(),
790+
expected
791+
);
792+
}
793+
}
794+
795+
#[test]
796+
fn instantiate2_address_impl_matches_wasmd_for_len_24() {
797+
// Manual test data generated with wasmd and bech32 CLI as follows
798+
// 1. Install https://github.com/cmoog/bech32
799+
// 2. Check out wasmd main and change to `var ContractAddrLen = 24`
800+
// 3. Run `make build`
801+
// 4. Run `./build/wasmd q wasm build-address 1122112211221122112211221122112211221122112211221122112211221122 wasm1xvenxvenxvenxvenxvenxvenxvenxvenkz5vxp aabbaabb | bech32 -d | xxd -p`
802+
803+
let checksum =
804+
HexBinary::from_hex("1122112211221122112211221122112211221122112211221122112211221122")
805+
.unwrap();
806+
let creator = CanonicalAddr::from(hex!("3333333333333333333333333333333333333333"));
807+
let salt = hex!("aabbaabb");
808+
809+
let expected =
810+
CanonicalAddr::from(hex!["da1aaec9d0ddc75b873079eb1b4f7ddd73a0e3170225fec4"]);
811+
assert_eq!(
812+
instantiate2_address_impl(&checksum, &creator, &salt, b"", 24).unwrap(),
813+
expected
814+
);
815+
}
816+
714817
#[test]
715818
fn instantiate2_address_impl_works_for_cosmjs_test_vectors() {
716819
// Test data from https://github.com/cosmos/cosmjs/pull/1253
@@ -774,6 +877,7 @@ mod tests {
774877
&input.creator_data.into(),
775878
&input.salt,
776879
&msg,
880+
32,
777881
)
778882
.unwrap();
779883
assert_eq!(addr, intermediate.address_data);

packages/std/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ pub(crate) mod prelude;
5252
/// contract devs to use it directly.
5353
pub mod storage_keys;
5454

55-
pub use crate::addresses::{instantiate2_address, Addr, CanonicalAddr, Instantiate2AddressError};
55+
pub use crate::addresses::{
56+
instantiate2_address, instantiate2_address_impl, Addr, CanonicalAddr, Instantiate2AddressError,
57+
};
5658
pub use crate::binary::Binary;
5759
pub use crate::checksum::{Checksum, ChecksumError};
5860
pub use crate::coin::{coin, coins, has_coins, Coin};

0 commit comments

Comments
 (0)