1use crate::{ComputeHash, OriginalHash};
2use pallas_codec::utils::KeepRaw;
3use pallas_crypto::{
4 hash::{Hash, Hasher},
5 key::ed25519::PublicKey,
6};
7use pallas_primitives::{alonzo, babbage, byron, conway};
8
9impl ComputeHash<32> for byron::EbbHead {
10 fn compute_hash(&self) -> Hash<32> {
11 Hasher::<256>::hash_cbor(&(0, self))
13 }
14}
15
16impl OriginalHash<32> for KeepRaw<'_, byron::EbbHead> {
17 fn original_hash(&self) -> Hash<32> {
18 Hasher::<256>::hash_cbor(&(0, self))
20 }
21}
22
23impl ComputeHash<32> for byron::BlockHead {
24 fn compute_hash(&self) -> Hash<32> {
25 Hasher::<256>::hash_cbor(&(1, self))
27 }
28}
29
30impl OriginalHash<32> for KeepRaw<'_, byron::BlockHead> {
31 fn original_hash(&self) -> Hash<32> {
32 Hasher::<256>::hash_cbor(&(1, self))
34 }
35}
36
37impl ComputeHash<32> for byron::Tx {
38 fn compute_hash(&self) -> Hash<32> {
39 Hasher::<256>::hash_cbor(self)
40 }
41}
42
43impl OriginalHash<32> for KeepRaw<'_, byron::Tx> {
44 fn original_hash(&self) -> Hash<32> {
45 Hasher::<256>::hash(self.raw_cbor())
46 }
47}
48
49impl ComputeHash<32> for alonzo::Header {
50 fn compute_hash(&self) -> pallas_crypto::hash::Hash<32> {
51 Hasher::<256>::hash_cbor(self)
52 }
53}
54
55impl OriginalHash<32> for KeepRaw<'_, alonzo::MintedHeader<'_>> {
56 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
57 Hasher::<256>::hash(self.raw_cbor())
58 }
59}
60
61impl OriginalHash<32> for KeepRaw<'_, babbage::MintedHeader<'_>> {
62 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
63 Hasher::<256>::hash(self.raw_cbor())
64 }
65}
66
67impl ComputeHash<32> for alonzo::AuxiliaryData {
68 fn compute_hash(&self) -> pallas_crypto::hash::Hash<32> {
69 Hasher::<256>::hash_cbor(self)
70 }
71}
72
73impl ComputeHash<28> for alonzo::NativeScript {
74 fn compute_hash(&self) -> Hash<28> {
75 Hasher::<224>::hash_tagged_cbor(self, 0)
76 }
77}
78
79impl OriginalHash<28> for KeepRaw<'_, alonzo::NativeScript> {
80 fn original_hash(&self) -> Hash<28> {
81 Hasher::<224>::hash_tagged(self.raw_cbor(), 0)
82 }
83}
84
85impl<const VERSION: usize> ComputeHash<28> for alonzo::PlutusScript<VERSION> {
86 fn compute_hash(&self) -> Hash<28> {
87 Hasher::<224>::hash_tagged(&self.0, VERSION as u8)
88 }
89}
90
91impl ComputeHash<32> for alonzo::PlutusData {
92 fn compute_hash(&self) -> Hash<32> {
93 Hasher::<256>::hash_cbor(self)
94 }
95}
96
97impl OriginalHash<32> for KeepRaw<'_, alonzo::PlutusData> {
98 fn original_hash(&self) -> Hash<32> {
99 Hasher::<256>::hash(self.raw_cbor())
100 }
101}
102
103impl ComputeHash<32> for alonzo::TransactionBody {
104 fn compute_hash(&self) -> Hash<32> {
105 Hasher::<256>::hash_cbor(self)
106 }
107}
108
109impl OriginalHash<32> for KeepRaw<'_, alonzo::TransactionBody> {
110 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
111 Hasher::<256>::hash(self.raw_cbor())
112 }
113}
114
115impl ComputeHash<32> for babbage::Header {
116 fn compute_hash(&self) -> pallas_crypto::hash::Hash<32> {
117 Hasher::<256>::hash_cbor(self)
118 }
119}
120
121impl OriginalHash<32> for KeepRaw<'_, babbage::Header> {
122 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
123 Hasher::<256>::hash(self.raw_cbor())
124 }
125}
126
127impl ComputeHash<32> for babbage::TransactionBody {
128 fn compute_hash(&self) -> Hash<32> {
129 Hasher::<256>::hash_cbor(self)
130 }
131}
132
133impl OriginalHash<32> for KeepRaw<'_, babbage::TransactionBody> {
134 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
135 Hasher::<256>::hash(self.raw_cbor())
136 }
137}
138
139impl OriginalHash<32> for KeepRaw<'_, babbage::MintedTransactionBody<'_>> {
140 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
141 Hasher::<256>::hash(self.raw_cbor())
142 }
143}
144
145impl ComputeHash<32> for babbage::DatumOption {
146 fn compute_hash(&self) -> Hash<32> {
147 match self {
148 babbage::DatumOption::Hash(hash) => *hash,
149 babbage::DatumOption::Data(data) => data.compute_hash(),
150 }
151 }
152}
153
154impl ComputeHash<32> for conway::TransactionBody {
157 fn compute_hash(&self) -> Hash<32> {
158 Hasher::<256>::hash_cbor(self)
159 }
160}
161
162impl OriginalHash<32> for KeepRaw<'_, conway::TransactionBody> {
163 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
164 Hasher::<256>::hash(self.raw_cbor())
165 }
166}
167
168impl OriginalHash<32> for KeepRaw<'_, conway::MintedTransactionBody<'_>> {
169 fn original_hash(&self) -> pallas_crypto::hash::Hash<32> {
170 Hasher::<256>::hash(self.raw_cbor())
171 }
172}
173
174impl ComputeHash<28> for PublicKey {
175 fn compute_hash(&self) -> Hash<28> {
176 Hasher::<224>::hash(&Into::<[u8; PublicKey::SIZE]>::into(*self))
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use crate::{Era, MultiEraTx};
183
184 use super::{ComputeHash, OriginalHash};
185 use pallas_codec::utils::{Int, MaybeIndefArray};
186 use pallas_codec::{minicbor, utils::Bytes};
187 use pallas_crypto::hash::Hash;
188 use pallas_crypto::key::ed25519::PublicKey;
189 use pallas_primitives::babbage::MintedDatumOption;
190 use pallas_primitives::{alonzo, babbage, byron};
191 use std::str::FromStr;
192
193 #[test]
194 fn byron_transaction_hash_works() {
195 type BlockWrapper<'b> = (u16, byron::MintedBlock<'b>);
196
197 let block_str = include_str!("../../test_data/byron1.block");
199
200 let block_bytes = hex::decode(block_str).expect("bad block file");
201 let (_, block_model): BlockWrapper =
202 minicbor::decode(&block_bytes[..]).expect("error decoding cbor for file");
203
204 let computed_hash = block_model.header.original_hash();
205
206 assert_eq!(
207 hex::encode(computed_hash),
208 "5c196e7394ace0449ba5a51c919369699b13896e97432894b4f0354dce8670b6"
209 )
210 }
211
212 #[test]
213 fn alonzo_transaction_hash_works() {
214 type BlockWrapper<'b> = (u16, alonzo::MintedBlock<'b>);
215
216 let block_str = include_str!("../../test_data/alonzo1.block");
218
219 let block_bytes = hex::decode(block_str).expect("bad block file");
220 let (_, block_model): BlockWrapper =
221 minicbor::decode(&block_bytes[..]).expect("error decoding cbor for file");
222
223 let valid_hashes = [
224 "8ae0cd531635579a9b52b954a840782d12235251fb1451e5c699e864c677514a",
225 "bb5bb4e1c09c02aa199c60e9f330102912e3ef977bb73ecfd8f790945c6091d4",
226 "8cdd88042ddb6c800714fb1469fb1a1a93152aae3c87a81f2a3016f2ee5c664a",
227 "10add6bdaa7ade06466bdd768456e756709090846b58bf473f240c484db517fa",
228 "8838f5ab27894a6543255aeaec086f7b3405a6db6e7457a541409cdbbf0cd474",
229 ];
230
231 for (tx_idx, tx) in block_model.transaction_bodies.iter().enumerate() {
232 let original_hash = tx.original_hash();
233 let expected_hash = valid_hashes[tx_idx];
234 assert_eq!(hex::encode(original_hash), expected_hash)
235 }
236 }
237
238 #[test]
239 fn babbage_transaction_hash_works() {
240 type BlockWrapper<'b> = (u16, babbage::MintedBlock<'b>);
241
242 let block_idx = 1;
244 let block_str = include_str!("../../test_data/babbage1.block");
245
246 let block_bytes =
247 hex::decode(block_str).unwrap_or_else(|_| panic!("bad block file {block_idx}"));
248 let (_, block_model): BlockWrapper = minicbor::decode(&block_bytes[..])
249 .unwrap_or_else(|_| panic!("error decoding cbor for file {block_idx}"));
250
251 let valid_hashes = ["3fad302595665b004971a6b76909854a39a0a7ecdbff3692f37b77ae37dbe882"];
252
253 for (tx_idx, tx) in block_model.transaction_bodies.iter().enumerate() {
254 let original_hash = tx.original_hash();
255 let expected_hash = valid_hashes[tx_idx];
256 assert_eq!(hex::encode(original_hash), expected_hash)
257 }
258 }
259
260 #[test]
261 fn native_script_hashes_as_cardano_cli() {
262 let ns = alonzo::NativeScript::ScriptAll(vec![
264 alonzo::NativeScript::ScriptPubkey(
265 Hash::<28>::from_str("4d04380dcb9fbad5aff8e2f4e19394ef4e5e11b37932838f01984a12")
266 .unwrap(),
267 ),
268 alonzo::NativeScript::InvalidBefore(112500819),
269 ]);
270
271 let cardano_cli_output = "d6a8ced01ecdfbb26c90850010a06fbc20a7c23632fc92f531667f36";
273
274 assert_eq!(
275 ns.compute_hash(),
276 Hash::<28>::from_str(cardano_cli_output).unwrap()
277 )
278 }
279
280 #[test]
281 fn plutus_data_hashes_as_cardano_cli() {
282 let pd = alonzo::PlutusData::Constr(alonzo::Constr::<alonzo::PlutusData> {
284 tag: 1280,
285 any_constructor: None,
286 fields: MaybeIndefArray::Indef(vec![
287 alonzo::PlutusData::BigInt(alonzo::BigInt::Int(Int::from(4))),
288 alonzo::PlutusData::Constr(alonzo::Constr::<alonzo::PlutusData> {
289 tag: 124,
290 any_constructor: None,
291 fields: MaybeIndefArray::Indef(vec![
292 alonzo::PlutusData::BigInt(alonzo::BigInt::Int(Int::from(-4))),
293 alonzo::PlutusData::Constr(alonzo::Constr::<alonzo::PlutusData> {
294 tag: 102,
295 any_constructor: Some(453),
296 fields: MaybeIndefArray::Indef(vec![
297 alonzo::PlutusData::BigInt(alonzo::BigInt::Int(Int::from(2))),
298 alonzo::PlutusData::BigInt(alonzo::BigInt::Int(Int::from(3434))),
299 ]),
300 }),
301 alonzo::PlutusData::BigInt(alonzo::BigInt::Int(Int::from(-11828293))),
302 ]),
303 }),
304 alonzo::PlutusData::BigInt(alonzo::BigInt::Int(Int::from(11828293))),
305 ]),
306 });
307
308 let cardano_cli_output = "d9bc0eb6ac664286155f70d720cafd2af16277fbd9014a930997431a2ffbe554";
314
315 assert_eq!(
316 pd.compute_hash(),
317 Hash::<32>::from_str(cardano_cli_output).unwrap()
318 )
319 }
320
321 #[test]
322 fn plutus_v1_script_hashes_as_cardano_cli() {
323 let bytecode_hex = include_str!("../../test_data/jpgstore.plutus");
324 let bytecode = hex::decode(bytecode_hex).unwrap();
325 let script = alonzo::PlutusScript::<1>(Bytes::from(bytecode));
326
327 let generated = script.compute_hash().to_string();
328
329 assert_eq!(
330 generated,
331 "4a59ebd93ea53d1bbf7f82232c7b012700a0cf4bb78d879dabb1a20a"
334 );
335 }
336
337 #[test]
338 fn plutus_v2_script_hashes_as_cardano_cli() {
339 let bytecode_hex = include_str!("../../test_data/v2script.plutus");
340 let bytecode = hex::decode(bytecode_hex).unwrap();
341 let script = babbage::PlutusScript::<2>(Bytes::from(bytecode));
342
343 let generated = script.compute_hash().to_string();
344
345 assert_eq!(
346 generated,
347 "2616f3e9edb51f98ef04dbaefd042b5c731e86616e8e9172c63c39be"
350 );
351 }
352
353 #[test]
354 fn tx_wits_plutus_v1_script_hashes_as_cli() {
355 let tx_bytecode_hex = include_str!("../../test_data/scriptwit.tx");
356 let bytecode = hex::decode(tx_bytecode_hex).unwrap();
357 let tx = MultiEraTx::decode_for_era(Era::Babbage, &bytecode).unwrap();
358
359 let generated = tx
360 .plutus_v1_scripts()
361 .first()
362 .unwrap()
363 .compute_hash()
364 .to_string();
365
366 assert_eq!(
367 generated,
368 "62bdc3d04d04376d516d31664944b25ce3affa76d17f8b5e1279b49d"
369 );
370 }
371
372 #[test]
373 fn test_witness_datum_hash_respects_original_cbor() {
374 let expected = [
375 "54ad3c112d58e8946480e21d6a35b2a215d1a9a8f540c13714ded86e4b0b6aea",
376 "831a557bc2948e1b8c9f5e8e594d62299abff4eb1a11dc19da38bfaf9f2da407",
377 "923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec",
378 "b0ea85f16a443da7f60704a427923ae1d89a7dc2d6621d805d9dd441431ed700",
379 "c695868b4bfbf4c95714e707c69da1823bcf8cfc7c4b14b92c3645d4e1943be3",
380 "ed33125018c5cbc9ae1b242a3ff8f3db2e108e4a63866d0b5238a34502c723ed",
381 ];
382
383 let tx_hex = include_str!("../../test_data/babbage1.tx");
384 let tx_bytes = hex::decode(tx_hex).unwrap();
385 let tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes).unwrap();
386 let data = tx.plutus_data();
387
388 for (datum, expected_hash) in data.iter().zip(expected) {
389 assert_eq!(datum.original_hash().to_string(), expected_hash);
390 }
391 }
392
393 #[test]
394 fn test_inline_datum_hash_respects_original_cbor() {
395 let expected = "7607117edd3189347a2898defbb9042e9ea3bf094466718cdaf65f7f9bfeefdb";
396
397 let tx_hex = include_str!("../../test_data/babbage2.tx");
398 let tx_bytes = hex::decode(tx_hex).unwrap();
399 let tx = MultiEraTx::decode_for_era(Era::Babbage, &tx_bytes).unwrap();
400
401 for output in tx.outputs() {
402 if let Some(MintedDatumOption::Data(datum)) = output.datum() {
403 assert_eq!(datum.original_hash().to_string(), expected);
404 }
405 }
406 }
407
408 #[test]
409 fn test_public_key_hash() {
410 let key: [u8; 32] =
411 hex::decode("2354bc4e1ae230e3a9047b568848fdd4bccd8d9aa60e6d1426baa730908e662d")
412 .unwrap()
413 .try_into()
414 .unwrap();
415 let pk = PublicKey::from(key);
416
417 assert_eq!(
418 pk.compute_hash().to_vec(),
419 hex::decode("2b6b3949d380fea6cb1c1cf88490ea40b2c1ce87717df7869cb1c38e").unwrap()
420 )
421 }
422}