Skip to content

Commit b2cd604

Browse files
committed
Merge branch 'lowhung/introduce-combined-hash-file' into lowhung/unify-hash-types
# Conflicts: # codec/src/map_parameters.rs # common/src/hash.rs # common/src/snapshot/pool_params.rs # common/src/snapshot/streaming_snapshot.rs # common/src/types.rs # modules/chain_store/src/chain_store.rs # modules/chain_store/src/stores/fjall.rs # modules/chain_store/src/stores/mod.rs # modules/epochs_state/src/state.rs # modules/genesis_bootstrapper/src/genesis_bootstrapper.rs # modules/mithril_snapshot_fetcher/src/mithril_snapshot_fetcher.rs # modules/tx_unpacker/src/tx_unpacker.rs # modules/upstream_chain_fetcher/src/body_fetcher.rs
2 parents 0432374 + 21f5388 commit b2cd604

File tree

11 files changed

+69
-125
lines changed

11 files changed

+69
-125
lines changed

codec/src/map_parameters.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ pub fn map_gov_action_id(pallas_action_id: &conway::GovActionId) -> Result<GovAc
202202
};
203203

204204
Ok(GovActionId {
205-
transaction_id: TxHash::new(*pallas_action_id.transaction_id),
205+
transaction_id: TxHash::from(*pallas_action_id.transaction_id),
206206
action_index: act_idx_u8,
207207
})
208208
}

common/src/hash.rs

Lines changed: 36 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,13 @@
11
use serde::{Deserialize, Deserializer, Serialize, Serializer};
22
use std::{fmt, ops::Deref, str::FromStr};
33

4-
/// Data that is a cryptographic hash of `BYTES` long.
5-
///
6-
/// This is a generic wrapper around a fixed-size byte array that provides:
7-
/// - Hexadecimal serialization/deserialization
8-
/// - CBOR encoding/decoding via minicbor
9-
/// - Type-safe conversions from various byte representations
10-
/// - Display and debug formatting
4+
/// data that is a cryptographic [`struct@Hash`] of `BYTES` long.
115
///
126
/// # Common Hash Sizes in Cardano
137
///
148
/// - **32 bytes**: Block hashes, transaction hashes
159
/// - **28 bytes**: Script hashes, address key hashes
1610
///
17-
/// # Examples
18-
///
19-
/// ```ignore
20-
/// use your_crate::Hash;
21-
///
22-
/// // Parse from hex string
23-
/// let hash: Hash<32> = "0d8d00cdd4657ac84d82f0a56067634a7adfdf43da41cb534bcaa45060973d21"
24-
/// .parse()
25-
/// .unwrap();
26-
///
27-
/// // Create from byte array
28-
/// let bytes = [0u8; 28];
29-
/// let hash = Hash::new(bytes);
30-
///
31-
/// // Convert to hex string
32-
/// let hex_string = hash.to_string();
3311
/// ```
3412
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
3513
pub struct Hash<const BYTES: usize>([u8; BYTES]);
@@ -61,48 +39,16 @@ impl<'de, const BYTES: usize> Deserialize<'de> for Hash<BYTES> {
6139
}
6240

6341
impl<const BYTES: usize> Hash<BYTES> {
64-
/// Creates a new hash from a byte array.
65-
///
66-
/// This is a const function, allowing hashes to be created at compile time.
67-
///
68-
/// # Examples
69-
///
70-
/// ```ignore
71-
/// use your_crate::Hash;
72-
///
73-
/// const MY_HASH: Hash<32> = Hash::new([0u8; 32]);
74-
/// ```
7542
#[inline]
7643
pub const fn new(bytes: [u8; BYTES]) -> Self {
7744
Self(bytes)
7845
}
7946

80-
/// Converts the hash to a `Vec<u8>`.
81-
///
82-
/// # Examples
83-
///
84-
/// ```ignore
85-
/// use your_crate::Hash;
86-
///
87-
/// let hash = Hash::new([1u8; 28]);
88-
/// let vec = hash.to_vec();
89-
/// assert_eq!(vec.len(), 28);
90-
/// ```
9147
#[inline]
9248
pub fn to_vec(&self) -> Vec<u8> {
9349
self.0.to_vec()
9450
}
9551

96-
/// Consumes the hash and returns the inner byte array.
97-
///
98-
/// # Examples
99-
///
100-
/// ```ignore
101-
/// use your_crate::Hash;
102-
///
103-
/// let hash = Hash::new([1u8; 28]);
104-
/// let bytes: [u8; 28] = hash.into_inner();
105-
/// ```
10652
#[inline]
10753
pub fn into_inner(self) -> [u8; BYTES] {
10854
self.0
@@ -119,11 +65,6 @@ impl<const BYTES: usize> From<[u8; BYTES]> for Hash<BYTES> {
11965
impl<const BYTES: usize> TryFrom<&[u8]> for Hash<BYTES> {
12066
type Error = std::array::TryFromSliceError;
12167

122-
/// Attempts to create a hash from a byte slice.
123-
///
124-
/// # Errors
125-
///
126-
/// Returns an error if the slice length does not match `BYTES`.
12768
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
12869
let hash: [u8; BYTES] = value.try_into()?;
12970
Ok(Self::new(hash))
@@ -133,25 +74,19 @@ impl<const BYTES: usize> TryFrom<&[u8]> for Hash<BYTES> {
13374
impl<const BYTES: usize> TryFrom<Vec<u8>> for Hash<BYTES> {
13475
type Error = Vec<u8>;
13576

136-
/// Attempts to create a hash from a `Vec<u8>`.
137-
///
138-
/// # Errors
139-
///
140-
/// Returns the original vector if its length does not match `BYTES`.
14177
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
142-
let hash: [u8; BYTES] = value.try_into()?;
143-
Ok(Self::new(hash))
78+
Self::try_from(value.as_slice()).map_err(|_| value)
14479
}
14580
}
14681

147-
impl<const BYTES: usize> From<Hash<BYTES>> for Vec<u8> {
148-
fn from(hash: Hash<BYTES>) -> Self {
82+
impl<const BYTES: usize> From<&Hash<BYTES>> for Vec<u8> {
83+
fn from(hash: &Hash<BYTES>) -> Self {
14984
hash.0.to_vec()
15085
}
15186
}
15287

153-
impl<const BYTES: usize> From<Hash<BYTES>> for [u8; BYTES] {
154-
fn from(hash: Hash<BYTES>) -> Self {
88+
impl<const BYTES: usize> From<&Hash<BYTES>> for [u8; BYTES] {
89+
fn from(hash: &Hash<BYTES>) -> Self {
15590
hash.0
15691
}
15792
}
@@ -185,7 +120,6 @@ impl<const BYTES: usize> fmt::Debug for Hash<BYTES> {
185120
}
186121

187122
impl<const BYTES: usize> fmt::Display for Hash<BYTES> {
188-
/// Formats the hash as a lowercase hexadecimal string.
189123
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190124
f.write_str(&hex::encode(self))
191125
}
@@ -194,23 +128,6 @@ impl<const BYTES: usize> fmt::Display for Hash<BYTES> {
194128
impl<const BYTES: usize> FromStr for Hash<BYTES> {
195129
type Err = hex::FromHexError;
196130

197-
/// Parses a hash from a hexadecimal string.
198-
///
199-
/// # Errors
200-
///
201-
/// Returns an error if:
202-
/// - The string is not valid hexadecimal
203-
/// - The decoded bytes do not match the expected length `BYTES`
204-
///
205-
/// # Examples
206-
///
207-
/// ```ignore
208-
/// use your_crate::Hash;
209-
///
210-
/// let hash: Hash<28> = "276fd18711931e2c0e21430192dbeac0e458093cd9d1fcd7210f64b3"
211-
/// .parse()
212-
/// .unwrap();
213-
/// ```
214131
fn from_str(s: &str) -> Result<Self, Self::Err> {
215132
let mut bytes = [0; BYTES];
216133
hex::decode_to_slice(s, &mut bytes)?;
@@ -221,11 +138,6 @@ impl<const BYTES: usize> FromStr for Hash<BYTES> {
221138
impl<const BYTES: usize> hex::FromHex for Hash<BYTES> {
222139
type Error = hex::FromHexError;
223140

224-
/// Decodes a hash from hexadecimal bytes.
225-
///
226-
/// # Errors
227-
///
228-
/// Returns an error if the decoded length does not match `BYTES`.
229141
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
230142
match Self::try_from(Vec::<u8>::from_hex(hex)?) {
231143
Ok(h) => Ok(h),
@@ -262,14 +174,6 @@ impl<'a, C, const BYTES: usize> minicbor::Decode<'a, C> for Hash<BYTES> {
262174
}
263175
}
264176

265-
/// Declares a type alias for a hash with optional documentation.
266-
///
267-
/// # Examples
268-
///
269-
/// ```ignore
270-
/// declare_hash_type!(BlockHash, 32);
271-
/// declare_hash_type!(TxHash, 32);
272-
/// ```
273177
#[macro_export]
274178
macro_rules! declare_hash_type {
275179
($name:ident, $size:expr) => {
@@ -341,22 +245,26 @@ macro_rules! declare_hash_type_with_bech32 {
341245
}
342246

343247
impl TryFrom<Vec<u8>> for $name {
344-
type Error = Vec<u8>;
248+
type Error = anyhow::Error;
345249
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
346-
Ok(Self(Hash::try_from(vec)?))
250+
Ok(Self(
251+
Hash::try_from(vec).map_err(|e| anyhow::anyhow!("{}", hex::encode(e)))?,
252+
))
347253
}
348254
}
349255

350256
impl TryFrom<&[u8]> for $name {
351-
type Error = std::array::TryFromSliceError;
257+
type Error = anyhow::Error;
352258
fn try_from(arr: &[u8]) -> Result<Self, Self::Error> {
353-
Ok(Self(Hash::try_from(arr)?))
259+
Ok(Self(
260+
Hash::try_from(arr).map_err(|e| anyhow::anyhow!("{}", e))?,
261+
))
354262
}
355263
}
356264

357265
impl AsRef<[u8]> for $name {
358266
fn as_ref(&self) -> &[u8] {
359-
self.0.as_ref()
267+
&self.0.as_ref()
360268
}
361269
}
362270

@@ -370,7 +278,7 @@ macro_rules! declare_hash_type_with_bech32 {
370278
impl std::str::FromStr for $name {
371279
type Err = hex::FromHexError;
372280
fn from_str(s: &str) -> Result<Self, Self::Err> {
373-
Ok(Self(s.parse()?))
281+
Ok(Self(s.parse::<Hash<$size>>()?))
374282
}
375283
}
376284

@@ -402,14 +310,30 @@ macro_rules! declare_hash_type_with_bech32 {
402310
impl crate::serialization::Bech32Conversion for $name {
403311
fn to_bech32(&self) -> Result<String, anyhow::Error> {
404312
use crate::serialization::Bech32WithHrp;
405-
self.0.to_vec().to_bech32_with_hrp($hrp)
313+
use anyhow::Context;
314+
315+
self.as_ref().to_bech32_with_hrp($hrp).with_context(|| {
316+
format!(
317+
"Failed to encode {} to bech32 with HRP '{}'",
318+
stringify!($name),
319+
$hrp
320+
)
321+
})
406322
}
407323

408324
fn from_bech32(s: &str) -> Result<Self, anyhow::Error> {
409325
use crate::serialization::Bech32WithHrp;
410-
let v = Vec::<u8>::from_bech32_with_hrp(s, $hrp)?;
326+
use anyhow::Context;
327+
328+
let v = Vec::<u8>::from_bech32_with_hrp(s, $hrp).with_context(|| {
329+
format!("Failed to decode {} from bech32", stringify!($name))
330+
})?;
331+
411332
Self::try_from(v).map_err(|_| {
412-
anyhow::Error::msg(format!("Bad vector input to {}", stringify!($name)))
333+
anyhow::anyhow!(
334+
"Failed to create {} from decoded bech32 data",
335+
stringify!($name)
336+
)
413337
})
414338
}
415339
}
@@ -459,7 +383,7 @@ mod tests {
459383
fn into_vec() {
460384
let bytes = [0u8; 28];
461385
let hash = Hash::new(bytes);
462-
let vec: Vec<u8> = hash.into();
386+
let vec: Vec<u8> = hash.as_ref().into();
463387
assert_eq!(vec, bytes.to_vec());
464388
}
465389

common/src/serialization.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,23 @@ impl Bech32WithHrp for Vec<u8> {
115115
Ok(data.to_vec())
116116
}
117117
}
118+
119+
impl Bech32WithHrp for [u8] {
120+
fn to_bech32_with_hrp(&self, hrp: &str) -> Result<String, anyhow::Error> {
121+
let hrp = Hrp::parse(hrp).map_err(|e| anyhow!("Bech32 HRP parse error: {e}"))?;
122+
123+
bech32::encode::<Bech32>(hrp, self).map_err(|e| anyhow!("Bech32 encoding error: {e}"))
124+
}
125+
126+
fn from_bech32_with_hrp(s: &str, expected_hrp: &str) -> Result<Vec<u8>, anyhow::Error> {
127+
let (hrp, data) = bech32::decode(s).map_err(|e| anyhow!("Invalid Bech32 string: {e}"))?;
128+
129+
if hrp != Hrp::parse(expected_hrp)? {
130+
return Err(anyhow!(
131+
"Invalid HRP, expected '{expected_hrp}', got '{hrp}'"
132+
));
133+
}
134+
135+
Ok(data.to_vec())
136+
}
137+
}

modules/chain_store/src/chain_store.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ impl ChainStore {
400400
block_info.push(BlockInfo {
401401
timestamp: block.extra.timestamp,
402402
number: header.number(),
403-
hash: BlockHash::new(*header.hash()),
403+
hash: BlockHash::from(*header.hash()),
404404
slot: header.slot(),
405405
epoch: block.extra.epoch,
406406
epoch_slot: block.extra.epoch_slot,
@@ -416,8 +416,8 @@ impl ChainStore {
416416
block_vrf: header.vrf_vkey().map(|key| VrfKeyHash::try_from(key).ok().unwrap()),
417417
op_cert,
418418
op_cert_counter,
419-
previous_block: header.previous_hash().map(|h| BlockHash::new(*h)),
420-
next_block: next_hash.map(|h| BlockHash::new(*h)),
419+
previous_block: header.previous_hash().map(|h| BlockHash::from(*h)),
420+
next_block: next_hash.map(|h| BlockHash::from(*h)),
421421
confirmations: latest_number - header.number(),
422422
});
423423

modules/chain_store/src/stores/fjall.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ mod tests {
263263
status: acropolis_common::BlockStatus::Immutable,
264264
slot: block.slot(),
265265
number: block.number(),
266-
hash: BlockHash::new(*block.hash()),
266+
hash: BlockHash::from(*block.hash()),
267267
epoch,
268268
epoch_slot,
269269
new_epoch: false,

modules/chain_store/src/stores/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ pub struct ExtraBlockData {
3434

3535
pub(crate) fn extract_tx_hashes(block: &[u8]) -> Result<Vec<TxHash>> {
3636
let block = pallas_traverse::MultiEraBlock::decode(block).context("could not decode block")?;
37-
Ok(block.txs().into_iter().map(|tx| TxHash::new(*tx.hash())).collect())
37+
Ok(block.txs().into_iter().map(|tx| TxHash::from(*tx.hash())).collect())
3838
}

modules/epochs_state/src/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl State {
134134
let evolving = Nonces::evolve(&current_nonces.evolving, &nonce_vrf_output)?;
135135

136136
// there must be parent hash
137-
let Some(parent_hash) = header.previous_hash().map(|h| BlockHash::new(*h)) else {
137+
let Some(parent_hash) = header.previous_hash().map(|h| BlockHash::from(*h)) else {
138138
return Err(anyhow::anyhow!("Header Parent hash error"));
139139
};
140140

modules/genesis_bootstrapper/src/genesis_bootstrapper.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl GenesisBootstrapper {
141141
let mut total_allocated: u64 = 0;
142142
for (tx_index, (hash, address, amount)) in gen_utxos.iter().enumerate() {
143143
let tx_identifier = TxIdentifier::new(0, tx_index as u16);
144-
let tx_ref = TxOutRef::new(TxHash::new(**hash), 0);
144+
let tx_ref = TxOutRef::new(TxHash::from(**hash), 0);
145145

146146
gen_utxo_identifiers.push((tx_ref, tx_identifier));
147147

modules/mithril_snapshot_fetcher/src/mithril_snapshot_fetcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ impl MithrilSnapshotFetcher {
325325
status: BlockStatus::Immutable,
326326
slot,
327327
number,
328-
hash: BlockHash::new(*block.hash()),
328+
hash: BlockHash::from(*block.hash()),
329329
epoch,
330330
epoch_slot,
331331
new_epoch,

modules/tx_unpacker/src/tx_unpacker.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ impl TxUnpacker {
183183
for input in inputs { // MultiEraInput
184184
// Lookup and remove UTxOIdentifier from registry
185185
let oref = input.output_ref();
186-
let tx_ref = TxOutRef::new(TxHash::new(**oref.hash()), oref.index() as u16);
186+
let tx_ref = TxOutRef::new(TxHash::from(**oref.hash()), oref.index() as u16);
187187

188188
match utxo_registry.consume(&tx_ref) {
189189
Ok(tx_identifier) => {
@@ -342,7 +342,7 @@ impl TxUnpacker {
342342
if publish_governance_procedures_topic.is_some() {
343343
if let Some(pp) = props {
344344
// Nonempty set -- governance_message.proposal_procedures will not be empty
345-
let mut proc_id = GovActionId { transaction_id: TxHash::new(*tx.hash()), action_index: 0 };
345+
let mut proc_id = GovActionId { transaction_id: tx_hash, action_index: 0 };
346346
for (action_index, pallas_governance_proposals) in pp.iter().enumerate() {
347347
match proc_id.set_action_index(action_index)
348348
.and_then (|proc_id| map_parameters::map_governance_proposals_procedures(proc_id, pallas_governance_proposals))
@@ -356,7 +356,7 @@ impl TxUnpacker {
356356
if let Some(pallas_vp) = votes {
357357
// Nonempty set -- governance_message.voting_procedures will not be empty
358358
match map_parameters::map_all_governance_voting_procedures(pallas_vp) {
359-
Ok(vp) => voting_procedures.push((TxHash::new(*tx.hash()), vp)),
359+
Ok(vp) => voting_procedures.push((tx_hash, vp)),
360360
Err(e) => error!("Cannot decode governance voting procedures in slot {}: {e}", block.slot)
361361
}
362362
}

0 commit comments

Comments
 (0)