@@ -313,18 +313,50 @@ pub fn instantiate2_address(
313
313
// Non-empty msg values are discouraged.
314
314
// See https://medium.com/cosmwasm/dev-note-3-limitations-of-instantiate2-and-how-to-deal-with-them-a3f946874230.
315
315
let msg = b"" ;
316
- instantiate2_address_impl ( checksum, creator, salt, msg)
316
+ let len = 32 ;
317
+ instantiate2_address_impl ( checksum, creator, salt, msg, len)
317
318
}
318
319
319
320
/// The instantiate2 address derivation implementation. This API is used for
320
321
/// testing purposes only. The `msg` field is discouraged and should not be used.
321
322
/// 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
+ /// ```
322
352
#[ 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 (
324
355
checksum : & [ u8 ] ,
325
356
creator : & CanonicalAddr ,
326
357
salt : & [ u8 ] ,
327
358
msg : & [ u8 ] ,
359
+ len : usize ,
328
360
) -> Result < CanonicalAddr , Instantiate2AddressError > {
329
361
if checksum. len ( ) != 32 {
330
362
return Err ( Instantiate2AddressError :: InvalidChecksumLength ) ;
@@ -344,7 +376,17 @@ fn instantiate2_address_impl(
344
376
key. extend_from_slice ( salt) ;
345
377
key. extend_from_slice ( & ( msg. len ( ) as u64 ) . to_be_bytes ( ) ) ;
346
378
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
+
348
390
Ok ( address_data. into ( ) )
349
391
}
350
392
@@ -650,7 +692,7 @@ mod tests {
650
692
"5e865d3e45ad3e961f77fd77d46543417ced44d924dc3e079b5415ff6775f847"
651
693
) ) ;
652
694
assert_eq ! (
653
- instantiate2_address_impl( & checksum1, & creator1, & salt1, msg1) . unwrap( ) ,
695
+ instantiate2_address_impl( & checksum1, & creator1, & salt1, msg1, 32 ) . unwrap( ) ,
654
696
expected
655
697
) ;
656
698
@@ -659,7 +701,7 @@ mod tests {
659
701
"0995499608947a5281e2c7ebd71bdb26a1ad981946dad57f6c4d3ee35de77835"
660
702
) ) ;
661
703
assert_eq ! (
662
- instantiate2_address_impl( & checksum1, & creator1, & salt1, msg2) . unwrap( ) ,
704
+ instantiate2_address_impl( & checksum1, & creator1, & salt1, msg2, 32 ) . unwrap( ) ,
663
705
expected
664
706
) ;
665
707
@@ -668,7 +710,7 @@ mod tests {
668
710
"83326e554723b15bac664ceabc8a5887e27003abe9fbd992af8c7bcea4745167"
669
711
) ) ;
670
712
assert_eq ! (
671
- instantiate2_address_impl( & checksum1, & creator1, & salt1, msg3) . unwrap( ) ,
713
+ instantiate2_address_impl( & checksum1, & creator1, & salt1, msg3, 32 ) . unwrap( ) ,
672
714
expected
673
715
) ;
674
716
@@ -677,40 +719,101 @@ mod tests {
677
719
"9384c6248c0bb171e306fd7da0993ec1e20eba006452a3a9e078883eb3594564"
678
720
) ) ;
679
721
assert_eq ! (
680
- instantiate2_address_impl( & checksum1, & creator1, & salt2, b"" ) . unwrap( ) ,
722
+ instantiate2_address_impl( & checksum1, & creator1, & salt2, b"" , 32 ) . unwrap( ) ,
681
723
expected
682
724
) ;
683
725
684
726
// Salt too short or too long
685
727
let empty = Vec :: < u8 > :: new ( ) ;
686
728
assert ! ( matches!(
687
- instantiate2_address_impl( & checksum1, & creator1, & empty, b"" ) . unwrap_err( ) ,
729
+ instantiate2_address_impl( & checksum1, & creator1, & empty, b"" , 32 ) . unwrap_err( ) ,
688
730
Instantiate2AddressError :: InvalidSaltLength
689
731
) ) ;
690
732
let too_long = vec ! [ 0x11 ; 65 ] ;
691
733
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( ) ,
693
735
Instantiate2AddressError :: InvalidSaltLength
694
736
) ) ;
695
737
696
738
// invalid checksum length
697
739
let broken_cs = hex ! ( "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2" ) ;
698
740
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( ) ,
700
742
Instantiate2AddressError :: InvalidChecksumLength
701
743
) ) ;
702
744
let broken_cs = hex ! ( "" ) ;
703
745
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( ) ,
705
747
Instantiate2AddressError :: InvalidChecksumLength
706
748
) ) ;
707
749
let broken_cs = hex ! ( "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2aaaa" ) ;
708
750
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( ) ,
710
752
Instantiate2AddressError :: InvalidChecksumLength
711
753
) ) ;
712
754
}
713
755
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
+
714
817
#[ test]
715
818
fn instantiate2_address_impl_works_for_cosmjs_test_vectors ( ) {
716
819
// Test data from https://github.com/cosmos/cosmjs/pull/1253
@@ -774,6 +877,7 @@ mod tests {
774
877
& input. creator_data . into ( ) ,
775
878
& input. salt ,
776
879
& msg,
880
+ 32 ,
777
881
)
778
882
. unwrap ( ) ;
779
883
assert_eq ! ( addr, intermediate. address_data) ;
0 commit comments