Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implicit addr/pkh bytes #1512

Merged
merged 9 commits into from
Jun 14, 2023
Original file line number Diff line number Diff line change
@@ -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))
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Improve the implicit address and PKH in-memory representation.
([\#1512](https://github.com/anoma/namada/pull/1512))
4 changes: 2 additions & 2 deletions core/src/ledger/ibc/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -500,7 +500,7 @@ pub fn token_hash_from_denom(denom: impl AsRef<str>) -> Result<Option<String>> {
pub fn calc_hash(denom: impl AsRef<str>) -> 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
Expand Down
83 changes: 62 additions & 21 deletions core/src/types/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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;
Expand All @@ -17,7 +18,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();
Expand All @@ -27,7 +28,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;
Expand Down Expand Up @@ -166,10 +183,16 @@ 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<String> {
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(hash_hex)
}
Address::Implicit(ImplicitAddress(implicit)) => {
let hash_hex = HEXUPPER.encode(&implicit.0);
Some(hash_hex)
}
Address::Internal(_) => None,
}
}
Expand All @@ -178,7 +201,10 @@ impl Address {
fn to_fixed_len_string(&self) -> Vec<u8> {
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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -389,20 +429,20 @@ impl TryFrom<Signer> 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<str>) -> Self {
Self {
last_hash: seed.as_ref().to_owned(),
last_hash: Sha256::digest(seed.as_ref().as_bytes()).into(),
}
}

Expand All @@ -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 })
}
}
Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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)
})
}
Expand Down
79 changes: 50 additions & 29 deletions core/src/types/key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use data_encoding::HEXUPPER;
#[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use serde::Serialize;
use sha2::{Digest, Sha256};
use thiserror::Error;

Expand Down Expand Up @@ -301,56 +301,82 @@ pub trait SigScheme: Eq + Ord + Debug + Serialize + Default {
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
#[serde(transparent)]
pub struct PublicKeyHash(pub(crate) String);
pub struct PublicKeyHash(pub(crate) [u8; address::HASH_LEN]);

const PKH_HASH_LEN: usize = address::HASH_LEN;
impl serde::Serialize for PublicKeyHash {
fn serialize<S>(
&self,
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let encoded = self.to_string();
serde::Serialize::serialize(&encoded, serializer)
}
}

impl<'de> serde::Deserialize<'de> for PublicKeyHash {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let encoded: String = serde::Deserialize::deserialize(deserializer)?;
Self::from_str(&encoded).map_err(D::Error::custom)
}
}

const PKH_HEX_LEN: usize = address::HASH_HEX_LEN;
const PKH_LEN: usize = address::HASH_LEN;

impl From<PublicKeyHash> for String {
fn from(pkh: PublicKeyHash) -> Self {
pkh.0
pkh.to_string()
}
}

impl Display for PublicKeyHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
write!(f, "{}", HEXUPPER.encode(&self.0))
}
}

impl FromStr for PublicKeyHash {
type Err = PkhFromStringError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != PKH_HASH_LEN {
if s.len() != PKH_HEX_LEN {
return Err(Self::Err::UnexpectedLen(s.len()));
}
Ok(Self(s.to_owned()))
let raw_bytes = HEXUPPER
.decode(s.as_bytes())
.map_err(Self::Err::DecodeUpperHex)?;
let mut bytes: [u8; PKH_LEN] = Default::default();
bytes.copy_from_slice(&raw_bytes);
Ok(Self(bytes))
}
}

#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum PkhFromStringError {
#[error("Wrong PKH len. Expected {PKH_HASH_LEN}, got {0}")]
#[error("Wrong PKH len. Expected {PKH_HEX_LEN}, got {0}")]
UnexpectedLen(usize),
#[error("Failed decoding upper hex with {0}")]
DecodeUpperHex(data_encoding::DecodeError),
}

impl<PK: PublicKey> From<&PK> for PublicKeyHash {
fn from(pk: &PK) -> Self {
let pk_bytes =
pk.try_to_vec().expect("Public key encoding shouldn't fail");
let mut hasher = Sha256::new();
hasher.update(pk_bytes);
// hex of the first 40 chars of the hash
PublicKeyHash(format!(
"{:.width$X}",
hasher.finalize(),
width = PKH_HASH_LEN
))
let full_hash = Sha256::digest(&pk_bytes);
// take first 20 bytes of the hash
let mut hash: [u8; PKH_LEN] = Default::default();
hash.copy_from_slice(&full_hash[..PKH_LEN]);
PublicKeyHash(hash)
}
}

Expand All @@ -369,16 +395,11 @@ impl PublicKeyTmRawHash for common::PublicKey {
/// Convert validator's consensus key into address raw hash that is compatible
/// with Tendermint
pub fn tm_consensus_key_raw_hash(pk: &common::PublicKey) -> String {
match pk {
common::PublicKey::Ed25519(pk) => {
let pkh = PublicKeyHash::from(pk);
pkh.0
}
common::PublicKey::Secp256k1(pk) => {
let pkh = PublicKeyHash::from(pk);
pkh.0
}
}
let pkh = match pk {
common::PublicKey::Ed25519(pk) => PublicKeyHash::from(pk),
common::PublicKey::Secp256k1(pk) => PublicKeyHash::from(pk),
};
pkh.to_string()
}

/// Convert Tendermint validator's raw hash bytes to Namada raw hash string
Expand Down
4 changes: 2 additions & 2 deletions core/src/types/masp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}

Expand Down
38 changes: 19 additions & 19 deletions wasm/checksums.json
Original file line number Diff line number Diff line change
@@ -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.4aea6bf93eb7ca64a3162ca86fd2c8d9c8d324bcff16fdc4d48391aa67563049.wasm",
"tx_change_validator_commission.wasm": "tx_change_validator_commission.c3d7e58d32a0e9302fdf2e5722f163ee2dec422a62503842af6f7e57045d3a4c.wasm",
"tx_ibc.wasm": "tx_ibc.6d26fe5abea04632386e446588667cad1666c38c8cdd5da104f4a8da2a285523.wasm",
"tx_init_account.wasm": "tx_init_account.422badd147072d19c37485bb9af7c59384146ee6178a42f57d90a108d777daa7.wasm",
"tx_init_proposal.wasm": "tx_init_proposal.f6d80d0b0528489cbdd4a5d585de28c4960ca3763fab44db3036e144c5f9743e.wasm",
"tx_init_validator.wasm": "tx_init_validator.ce607a01ae70bfe4a1c55a34a7f5dc6e85254ef35ef18a9f7744c93f3c143f76.wasm",
"tx_reveal_pk.wasm": "tx_reveal_pk.de69f0787e0bf5947444259a75ebaa2ecbed37532b3cbd33a9ddfb27f43e6ea2.wasm",
"tx_transfer.wasm": "tx_transfer.53abc1752f23dda8c085d42e808d971515b90d3781617d45386538c8cc2ff8ba.wasm",
"tx_unbond.wasm": "tx_unbond.5abef3af0d388a84d129b838a367a9ade3303e1e8a2a86969f99cda95cfdbdfe.wasm",
"tx_unjail_validator.wasm": "tx_unjail_validator.f9463fd719f1f93cc5fc900f1e48bd81c0192dcdc378c115cd49618d5baf1d99.wasm",
"tx_update_vp.wasm": "tx_update_vp.cefd67a267f34d8e93b207a39de9e920616f3d4e5e629086bbcb88b079ce6856.wasm",
"tx_vote_proposal.wasm": "tx_vote_proposal.71da34c54ece175129519120bd597b0226896827cfe0e1c76f435489cd899617.wasm",
"tx_withdraw.wasm": "tx_withdraw.a3254ac54957870653255518b980e0f1696e80f1f9a736f5cd8ac19b9ba0788b.wasm",
"vp_implicit.wasm": "vp_implicit.8f8e643cfb1c7d11e15f8a17517935ef159ab9a6a8da747cad4a403c9e15cbc7.wasm",
"vp_masp.wasm": "vp_masp.cd7fae1152e765ad4e3985fbdd0b8b2cb1cea13e2fff812291ebaf484f8676c9.wasm",
"vp_testnet_faucet.wasm": "vp_testnet_faucet.1dc87f66e7a378df435a20f97592e3a7746a3ccb322ca11889f11822d312ee22.wasm",
"vp_token.wasm": "vp_token.56c0f16a5c67e36a10716b10fe392271e205b9c095c7d9bf161dc646dd0644ff.wasm",
"vp_user.wasm": "vp_user.c623820cd01bacc740070d364209abf9f1d83a80c4bda79660df7f3a3624bdcd.wasm",
"vp_validator.wasm": "vp_validator.842e60005998c6c8572d19d48182e916d6ee86784059c61166adc25c0e5707ed.wasm"
}
Loading