diff --git a/.changelog/unreleased/improvements/1510-established-addr-bytes.md b/.changelog/unreleased/improvements/1510-established-addr-bytes.md new file mode 100644 index 0000000000..4f25dfff9f --- /dev/null +++ b/.changelog/unreleased/improvements/1510-established-addr-bytes.md @@ -0,0 +1,3 @@ +- Improve the established address in-memory representation + and use a full SHA-256 digest for their generation. + ([\#1510](https://github.com/anoma/namada/pull/1510)) \ No newline at end of file diff --git a/core/src/ledger/ibc/storage.rs b/core/src/ledger/ibc/storage.rs index fab224e755..bee874bb0e 100644 --- a/core/src/ledger/ibc/storage.rs +++ b/core/src/ledger/ibc/storage.rs @@ -16,7 +16,7 @@ use crate::ibc::core::ics24_host::path::{ ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; use crate::ibc::core::ics24_host::Path; -use crate::types::address::{Address, InternalAddress, HASH_LEN}; +use crate::types::address::{Address, InternalAddress, HASH_HEX_LEN}; use crate::types::storage::{self, DbKeySeg, Key, KeySeg}; const CLIENTS_COUNTER: &str = "clients/counter"; @@ -500,7 +500,7 @@ pub fn token_hash_from_denom(denom: impl AsRef) -> Result> { pub fn calc_hash(denom: impl AsRef) -> String { let mut hasher = Sha256::new(); hasher.update(denom.as_ref()); - format!("{:.width$x}", hasher.finalize(), width = HASH_LEN) + format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN) } /// Key's prefix of the received token over IBC diff --git a/core/src/types/address.rs b/core/src/types/address.rs index 20e3e6f372..b4184b304a 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -1,6 +1,7 @@ //! Implements transparent addresses as described in [Accounts //! Addresses](docs/src/explore/design/ledger/accounts.md#addresses). +use std::borrow::Cow; use std::collections::HashMap; use std::fmt::{Debug, Display}; use std::hash::Hash; @@ -8,6 +9,7 @@ use std::str::FromStr; use bech32::{self, FromBase32, ToBase32, Variant}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use data_encoding::HEXUPPER; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thiserror::Error; @@ -17,7 +19,7 @@ use crate::types::key; use crate::types::key::PublicKeyHash; /// The length of an established [`Address`] encoded with Borsh. -pub const ESTABLISHED_ADDRESS_BYTES_LEN: usize = 45; +pub const ESTABLISHED_ADDRESS_BYTES_LEN: usize = 21; /// The length of [`Address`] encoded with Bech32m. pub const ADDRESS_LEN: usize = 79 + ADDRESS_HRP.len(); @@ -27,7 +29,23 @@ pub const ADDRESS_LEN: usize = 79 + ADDRESS_HRP.len(); const ADDRESS_HRP: &str = "atest"; /// We're using "Bech32m" variant pub const BECH32M_VARIANT: bech32::Variant = Variant::Bech32m; -pub(crate) const HASH_LEN: usize = 40; + +/// Length of a hash of an address as a hexadecimal string +pub(crate) const HASH_HEX_LEN: usize = 40; + +/// Length of a trimmed hash of an address. +pub(crate) const HASH_LEN: usize = 20; + +/// SHA-256 hash len +/// +/// ``` +/// use sha2::Digest; +/// assert_eq!( +/// sha2::Sha256::output_size(), +/// namada_core::types::address::SHA_HASH_LEN +/// ); +/// ``` +pub const SHA_HASH_LEN: usize = 32; /// An address string before bech32m encoding must be this size. pub const FIXED_LEN_STRING_BYTES: usize = 45; @@ -166,10 +184,15 @@ impl Address { /// Try to get a raw hash of an address, only defined for established and /// implicit addresses. - pub fn raw_hash(&self) -> Option<&str> { + pub fn raw_hash(&self) -> Option> { match self { - Address::Established(established) => Some(&established.hash), - Address::Implicit(ImplicitAddress(implicit)) => Some(&implicit.0), + Address::Established(established) => { + let hash_hex = HEXUPPER.encode(&established.hash); + Some(Cow::Owned(hash_hex)) + } + Address::Implicit(ImplicitAddress(implicit)) => { + Some(Cow::Borrowed(&implicit.0)) + } Address::Internal(_) => None, } } @@ -178,7 +201,10 @@ impl Address { fn to_fixed_len_string(&self) -> Vec { let mut string = match self { Address::Established(EstablishedAddress { hash }) => { - format!("{}::{}", PREFIX_ESTABLISHED, hash) + // The bech32m's data is a hex of the first 40 chars of the hash + let hash_hex = HEXUPPER.encode(hash); + debug_assert_eq!(hash_hex.len(), HASH_HEX_LEN); + format!("{}::{}", PREFIX_ESTABLISHED, hash_hex) } Address::Implicit(ImplicitAddress(pkh)) => { format!("{}::{}", PREFIX_IMPLICIT, pkh) @@ -233,10 +259,24 @@ impl Address { } match string.split_once("::") { Some((PREFIX_ESTABLISHED, hash)) => { - if hash.len() == HASH_LEN { - Ok(Address::Established(EstablishedAddress { - hash: hash.to_string(), - })) + if hash.len() == HASH_HEX_LEN { + let raw = + HEXUPPER.decode(hash.as_bytes()).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + e, + ) + })?; + if raw.len() != HASH_LEN { + return Err(Error::new( + ErrorKind::InvalidData, + "Established address hash must be 40 characters \ + long", + )); + } + let mut hash: [u8; HASH_LEN] = Default::default(); + hash.copy_from_slice(&raw); + Ok(Address::Established(EstablishedAddress { hash })) } else { Err(Error::new( ErrorKind::InvalidData, @@ -285,7 +325,7 @@ impl Address { internal::IBC_MINT => { Ok(Address::Internal(InternalAddress::IbcMint)) } - _ if raw.len() == HASH_LEN => Ok(Address::Internal( + _ if raw.len() == HASH_HEX_LEN => Ok(Address::Internal( InternalAddress::IbcToken(raw.to_string()), )), _ => Err(Error::new( @@ -389,20 +429,20 @@ impl TryFrom for Address { Deserialize, )] pub struct EstablishedAddress { - hash: String, + hash: [u8; HASH_LEN], } /// A generator of established addresses #[derive(Debug, Clone, PartialEq, BorshSerialize, BorshDeserialize)] pub struct EstablishedAddressGen { - last_hash: String, + last_hash: [u8; SHA_HASH_LEN], } impl EstablishedAddressGen { /// Initialize a new address generator with a given randomness seed. pub fn new(seed: impl AsRef) -> Self { Self { - last_hash: seed.as_ref().to_owned(), + last_hash: Sha256::digest(seed.as_ref().as_bytes()).into(), } } @@ -416,12 +456,12 @@ impl EstablishedAddressGen { let gen_bytes = self .try_to_vec() .expect("Encoding established addresses generator shouldn't fail"); - let mut hasher = Sha256::new(); let bytes = [&gen_bytes, rng_source.as_ref()].concat(); - hasher.update(bytes); - // hex of the first 40 chars of the hash - let hash = format!("{:.width$X}", hasher.finalize(), width = HASH_LEN); - self.last_hash = hash.clone(); + let full_hash = Sha256::digest(&bytes); + // take first 20 bytes of the hash + let mut hash: [u8; HASH_LEN] = Default::default(); + hash.copy_from_slice(&full_hash[..HASH_LEN]); + self.last_hash = full_hash.into(); Address::Established(EstablishedAddress { hash }) } } @@ -507,7 +547,8 @@ impl InternalAddress { let mut hasher = Sha256::new(); let s = format!("{}/{}/{}", port_id, channel_id, token); hasher.update(&s); - let hash = format!("{:.width$x}", hasher.finalize(), width = HASH_LEN); + let hash = + format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN); InternalAddress::IbcToken(hash) } } @@ -831,7 +872,7 @@ pub mod testing { ); hasher.update(&s); let hash = - format!("{:.width$x}", hasher.finalize(), width = HASH_LEN); + format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN); InternalAddress::IbcToken(hash) }) } diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 8d698d50ae..66938fbcdc 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -307,7 +307,7 @@ pub trait SigScheme: Eq + Ord + Debug + Serialize + Default { #[serde(transparent)] pub struct PublicKeyHash(pub(crate) String); -const PKH_HASH_LEN: usize = address::HASH_LEN; +const PKH_HASH_LEN: usize = address::HASH_HEX_LEN; impl From for String { fn from(pkh: PublicKeyHash) -> Self { diff --git a/core/src/types/masp.rs b/core/src/types/masp.rs index 5c2b3fb2d4..487dddf3e3 100644 --- a/core/src/types/masp.rs +++ b/core/src/types/masp.rs @@ -9,7 +9,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use sha2::{Digest, Sha256}; use crate::types::address::{ - masp, Address, DecodeError, BECH32M_VARIANT, HASH_LEN, + masp, Address, DecodeError, BECH32M_VARIANT, HASH_HEX_LEN, }; /// human-readable part of Bech32m encoded address @@ -153,7 +153,7 @@ impl PaymentAddress { let mut hasher = Sha256::new(); hasher.update(bytes); // hex of the first 40 chars of the hash - format!("{:.width$X}", hasher.finalize(), width = HASH_LEN) + format!("{:.width$X}", hasher.finalize(), width = HASH_HEX_LEN) } } diff --git a/wasm/checksums.json b/wasm/checksums.json index 3e8c6aece6..57869c4ce1 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.e72837fe265346308458d4e5f2399633bcf553ea13679434ea54fa578c13a912.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.4006b3a3251970b40290e1c898809ac914f9450c1f82e3026fdadca96eb42810.wasm", - "tx_ibc.wasm": "tx_ibc.012d3f1da7532f3ff92bb56499c349ea3e5b6abaf4bdb7b8faa0177bf7ea2b93.wasm", - "tx_init_account.wasm": "tx_init_account.6359caa0d6fda1d896b84a881351c6827aca66240988b438697c966dec231be2.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.339f4454df0be8ae670c5df84645c7b156f478726dc1e964a93cea4f8b6a4651.wasm", - "tx_init_validator.wasm": "tx_init_validator.a45ef2fc87cf2760107cd682a34772207da3ee96de778e5e83f22256df99ff7d.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.b7541013221fedb42d9bdd4be6e972deac147b8c930b03db282f0b7811198554.wasm", - "tx_transfer.wasm": "tx_transfer.8b6fb1f418fea7d8909ed45160b9614f6ff50421fd766ce7ac9c1767af848aa7.wasm", - "tx_unbond.wasm": "tx_unbond.7f4c904a13ec2d4716457290a5af1f2f8853ccb817276583fccb33a63add07bd.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.aadc50cd196eb818dd3f743c1be3f2973e9fdcbe9f1d7334dedbfee38f822ccc.wasm", - "tx_update_vp.wasm": "tx_update_vp.e5d25854a23186925caa2169869b1505e29132b8a8b396e911379d53ce5cf418.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.22b3358af6611d1786f6d49ccbf0757e3065a43fb37e8df0055f2173c8e14653.wasm", - "tx_withdraw.wasm": "tx_withdraw.04b5fe0ffb4c873a8d839ededcc616658e88af346a5c6a709c37eec8088486b4.wasm", - "vp_implicit.wasm": "vp_implicit.1e8355b50da06a5dcf9d2a8ab8ddc440f486ce039538dee415ab8625b1b11bae.wasm", - "vp_masp.wasm": "vp_masp.7487613c4391eef753360c55e63979fad3855259f2fdb0e945fe702342ef5979.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.d3cafc4d31f1081a8cedfbfdb45e674235231716668a6b1309beece16fe5ea5d.wasm", - "vp_token.wasm": "vp_token.495362b8949a5f77da12c5562c38e25584a7477cf6e1b229a8b209a67e6504d0.wasm", - "vp_user.wasm": "vp_user.c7b65fe31adb835d341ef82a6f02f72ee6c6bf457c22ad24422acb3d54387517.wasm", - "vp_validator.wasm": "vp_validator.64283182a94de72f35364414040f780c2d51d629e73e915f1803c9e71a2f5fd2.wasm" + "tx_bond.wasm": "tx_bond.a98b5b38666d7b28ffb83208086bbffdbf03c8e9d3199ae89db880ea784bc3ab.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.606a71c0f1dc1e7c2abb54789e4a99787a7c4718c5bd31e9283c02db9c7f42fb.wasm", + "tx_ibc.wasm": "tx_ibc.cd9d2dd7b04f16bf461138491ef2e75fa815ce1e2e6b474f0dfe563fea490a3b.wasm", + "tx_init_account.wasm": "tx_init_account.ce8a35ddc8959b0a73e4a1a6fd226fd0135d7ff0ef4ba517de1cfe53cfce93c0.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.14c1b8b5141101789623fd9ad826b6c3f29cbea4cfad6dec392e6a51fc4e7e66.wasm", + "tx_init_validator.wasm": "tx_init_validator.91ab5b995f11572d043a8f78670dc0f1e3a275c84807f3745eca28757c4c71a3.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.e0301b0d4246687110d2f321f158f2465fb6cac1e23c013e88aac89550d26261.wasm", + "tx_transfer.wasm": "tx_transfer.3d5136351aef729e48eb7b3767ec735175b115556abc34f0fb73791bce3fbce9.wasm", + "tx_unbond.wasm": "tx_unbond.9dfdc31ad694e0041b43e5e2230a3afbed0eb0ea121d89af4c850d531e22cf85.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.e5d034b4ec784939264dc8fa257a0358ccd2ecb94655f87767d791c446e3dca8.wasm", + "tx_update_vp.wasm": "tx_update_vp.15b6f09d6677c46ab74c4b034ede1d198a42015efe6d78f7fab96e503854ac46.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.ff34e5d98abcdf46a9091d81939bfda17cdd118258788c1668d2256e1db4de12.wasm", + "tx_withdraw.wasm": "tx_withdraw.01a1e491a16d7b99317b9127dde518969cf469a2bf76b62fe11dbfc7205e9918.wasm", + "vp_implicit.wasm": "vp_implicit.44390149aa21937fe91db593b242c2810abd316eb4e767eb4eece121f59941cf.wasm", + "vp_masp.wasm": "vp_masp.2502bdf824ce741688d7c153158a56aa49b84d00c6b4835138cfb3506d376f3d.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.3e222a8ecae546c06e29b056049e9251c99a6d7ec8bd430dce5775be8a4cb268.wasm", + "vp_token.wasm": "vp_token.b34f0386d05f44372368ddd59a463ed2de016be421bd130afb035500b84229f8.wasm", + "vp_user.wasm": "vp_user.bdc2def99d10d3fda732ac2a79639537a2703a496d921f9239776ad08fa66bc2.wasm", + "vp_validator.wasm": "vp_validator.d3b03ce3cab72a0365b380baf972df891154bf42b87a021ec8ac03c888afd731.wasm" } \ No newline at end of file diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 938c27be95..77268673c3 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -870,7 +870,7 @@ mod tests { tx_env.store_wasm_code(vp_code); // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec!["2AC0BCB5D9E2019180F99BEB84A77E32728CDABAAD8C4F0EF3762594EC836A9D".to_string()])); + tx_env.init_parameters(None, None, Some(vec!["944A06740903B2EAB1FECAC4BFA25DD48330689A0F9D69C79C05B151C039CBAC".to_string()])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index a09f1ab946..09ab948d1c 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -906,7 +906,7 @@ mod tests { tx_env.store_wasm_code(vp_code); // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec!["2AC0BCB5D9E2019180F99BEB84A77E32728CDABAAD8C4F0EF3762594EC836A9D".to_string()])); + tx_env.init_parameters(None, None, Some(vec!["944A06740903B2EAB1FECAC4BFA25DD48330689A0F9D69C79C05B151C039CBAC".to_string()])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index c2142423f5..1de8f0436c 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -927,7 +927,7 @@ mod tests { tx_env.store_wasm_code(vp_code); // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec!["2AC0BCB5D9E2019180F99BEB84A77E32728CDABAAD8C4F0EF3762594EC836A9D".to_string()])); + tx_env.init_parameters(None, None, Some(vec!["944A06740903B2EAB1FECAC4BFA25DD48330689A0F9D69C79C05B151C039CBAC".to_string()])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index 84fb2b57d0..c2f72e00ba 100755 Binary files a/wasm_for_tests/tx_memory_limit.wasm and b/wasm_for_tests/tx_memory_limit.wasm differ diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index d0156bf1bd..2f0cad959b 100755 Binary files a/wasm_for_tests/tx_mint_tokens.wasm and b/wasm_for_tests/tx_mint_tokens.wasm differ diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index 68b3a96f34..f2124d4665 100755 Binary files a/wasm_for_tests/tx_no_op.wasm and b/wasm_for_tests/tx_no_op.wasm differ diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index fb5d8d5d28..6a24749814 100755 Binary files a/wasm_for_tests/tx_proposal_code.wasm and b/wasm_for_tests/tx_proposal_code.wasm differ diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index 06145bc7e8..122cac4908 100755 Binary files a/wasm_for_tests/tx_read_storage_key.wasm and b/wasm_for_tests/tx_read_storage_key.wasm differ diff --git a/wasm_for_tests/tx_write.wasm b/wasm_for_tests/tx_write.wasm index cfbf02e639..4bd5056b9a 100755 Binary files a/wasm_for_tests/tx_write.wasm and b/wasm_for_tests/tx_write.wasm differ diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index a0fb758ae9..5d600d185f 100755 Binary files a/wasm_for_tests/tx_write_storage_key.wasm and b/wasm_for_tests/tx_write_storage_key.wasm differ diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index a9e047cba4..030b8f1691 100755 Binary files a/wasm_for_tests/vp_always_false.wasm and b/wasm_for_tests/vp_always_false.wasm differ diff --git a/wasm_for_tests/vp_always_true.wasm b/wasm_for_tests/vp_always_true.wasm index be671df8d0..1f52599cb4 100755 Binary files a/wasm_for_tests/vp_always_true.wasm and b/wasm_for_tests/vp_always_true.wasm differ diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index ffcc0a32b4..6047738916 100755 Binary files a/wasm_for_tests/vp_eval.wasm and b/wasm_for_tests/vp_eval.wasm differ diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index 1ef6e12489..a20bc1331d 100755 Binary files a/wasm_for_tests/vp_memory_limit.wasm and b/wasm_for_tests/vp_memory_limit.wasm differ diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index ee2e0fc3b0..804a7de4d7 100755 Binary files a/wasm_for_tests/vp_read_storage_key.wasm and b/wasm_for_tests/vp_read_storage_key.wasm differ