diff --git a/packages/consensus/src/validation/block.cairo b/packages/consensus/src/validation/block.cairo index 99d1259f..aad2d1cd 100644 --- a/packages/consensus/src/validation/block.cairo +++ b/packages/consensus/src/validation/block.cairo @@ -96,10 +96,10 @@ pub fn compute_and_validate_tx_data( validate_block_weight(total_weight)?; let wtxid_root = if block_height >= SEGWIT_BLOCK { - merkle_root(ref wtxids) + merkle_root(wtxids.span()) } else { Zero::zero() }; - Result::Ok((total_fee, merkle_root(ref txids), wtxid_root)) + Result::Ok((total_fee, merkle_root(txids.span()), wtxid_root)) } diff --git a/packages/consensus/src/validation/coinbase.cairo b/packages/consensus/src/validation/coinbase.cairo index 6c4bc1a4..416cc7f7 100644 --- a/packages/consensus/src/validation/coinbase.cairo +++ b/packages/consensus/src/validation/coinbase.cairo @@ -3,9 +3,7 @@ //! https://learnmeabitcoin.com/technical/mining/coinbase-transaction/ use crate::types::transaction::{Transaction, TxIn, TxOut}; -use utils::{ - bit_shifts::shr, hash::{Digest, DigestIntoByteArray}, double_sha256::{double_sha256_byte_array} -}; +use utils::{hash::{Digest, DigestIntoByteArray}, double_sha256::{double_sha256_byte_array}}; const BIP_34_BLOCK_HEIGHT: u32 = 227_836; const BIP_141_BLOCK_HEIGHT: u32 = 481_824; @@ -112,7 +110,13 @@ fn validate_coinbase_witness(witness: Span) -> Result<(), ByteArray> /// Return BTC reward in SATS fn compute_block_reward(block_height: u32) -> u64 { - shr(5000000000_u64, (block_height / 210000_u32)) + let mut result: u64 = 5_000_000_000; + + for _ in 0..block_height / 210_000 { + result /= 2; + }; + + result } /// Calculate wtxid commitment diff --git a/packages/utils/src/hex.cairo b/packages/utils/src/hex.cairo index 736a403c..ef51e051 100644 --- a/packages/utils/src/hex.cairo +++ b/packages/utils/src/hex.cairo @@ -9,7 +9,7 @@ pub fn from_hex(hex_string: ByteArray) -> ByteArray { let mut bytes: ByteArray = Default::default(); let mut i = 0; - while i < num_characters { + while i != num_characters { let hi = hex_char_to_nibble(hex_string[i]); let lo = hex_char_to_nibble(hex_string[i + 1]); bytes.append_byte(hi * 16 + lo); @@ -25,10 +25,9 @@ pub fn to_hex(data: @ByteArray) -> ByteArray { let mut result: ByteArray = Default::default(); let mut i = 0; - while i < data.len() { - let value = data[i]; - let l: u32 = (value / 16).into(); - let r: u32 = (value % 16).into(); + while i != data.len() { + let value: u32 = data[i].into(); + let (l, r) = core::traits::DivRem::div_rem(value, 16); result.append_byte(alphabet.at(l).unwrap()); result.append_byte(alphabet.at(r).unwrap()); i += 1; @@ -39,16 +38,16 @@ pub fn to_hex(data: @ByteArray) -> ByteArray { // Get `Digest` form `ByteArray` reversed pub fn hex_to_hash_rev(hex_string: ByteArray) -> Digest { let mut result: Array = array![]; - let mut i = 1; + let mut i = 0; let mut unit: u32 = 0; let len = hex_string.len(); - while (i < len) { - if ((i - 1) % 8 == 0 && i - 1 > 0) { + while i != len { + if (i != 0 && i % 8 == 0) { result.append(unit); unit = 0; } - let hi = hex_char_to_nibble(hex_string[len - i - 1]); - let lo = hex_char_to_nibble(hex_string[len - i]); + let hi = hex_char_to_nibble(hex_string[len - i - 2]); + let lo = hex_char_to_nibble(hex_string[len - i - 1]); unit = (unit * 256) + (hi * 16 + lo).into(); i += 2; }; diff --git a/packages/utils/src/merkle_tree.cairo b/packages/utils/src/merkle_tree.cairo index f770939a..1df5d1e5 100644 --- a/packages/utils/src/merkle_tree.cairo +++ b/packages/utils/src/merkle_tree.cairo @@ -3,28 +3,31 @@ use super::{double_sha256::double_sha256_parent, hash::Digest}; /// Calculate Merkle tree root given the array of leaves. -pub fn merkle_root(ref hashes: Array) -> Digest { +pub fn merkle_root(hashes: Span) -> Digest { let len = hashes.len(); if len == 1 { return *hashes[0]; } - if len % 2 == 1 { - hashes.append(*hashes[len - 1]); - } else { - // CVE-2012-2459 bug fix - assert!(*hashes[len - 1] != *hashes[len - 2], "unexpected node duplication in merkle tree"); - } - + let (_, is_odd) = core::traits::DivRem::div_rem(len, 2); + let mut arr = hashes; let mut next_hashes: Array = array![]; - let mut i = 0; - while i < len { - next_hashes.append(double_sha256_parent(hashes[i], hashes[i + 1])); - i += 2; + + while let Option::Some(v) = arr.multi_pop_front::<2>() { + let [a, b] = (*v).unbox(); + next_hashes.append(double_sha256_parent(@a, @b)); }; - merkle_root(ref next_hashes) + if (is_odd == 1) { + let last = *arr.pop_front().unwrap(); + next_hashes.append(double_sha256_parent(@last, @last)); + } else if (*hashes[len - 1] == *hashes[len - 2]) { + // CVE-2012-2459 bug fix + panic!("unexpected node duplication in merkle tree"); + } + + merkle_root(next_hashes.span()) } #[cfg(test)] @@ -42,8 +45,7 @@ mod tests { let expected_merkle_root: Digest = hex_to_hash_rev( "acd9825be8bece7782ec746a80b52f44d6a8af41c63dbab59b03e29558469682" ); - - assert_eq!(merkle_root(ref txids), expected_merkle_root); + assert_eq!(merkle_root(txids.span()), expected_merkle_root); } #[test] @@ -57,8 +59,7 @@ mod tests { let expected_merkle_root: Digest = hex_to_hash_rev( "7dac2c5666815c17a3b36427de37bb9d2e2c5ccec3f8633eb91a4205cb4c10ff" ); - - assert_eq!(merkle_root(ref txids), expected_merkle_root); + assert_eq!(merkle_root(txids.span()), expected_merkle_root); } #[test] @@ -72,8 +73,7 @@ mod tests { let expected_merkle_root: Digest = hex_to_hash_rev( "035dff4bfc62ff255ddac842dd31be4d28756b3625b0c4fecade7011f8dada20" ); - - assert_eq!(merkle_root(ref txids), expected_merkle_root); + assert_eq!(merkle_root(txids.span()), expected_merkle_root); } #[test] @@ -88,8 +88,7 @@ mod tests { let expected_merkle_root: Digest = hex_to_hash_rev( "222ae86adb1f65c0458f53f4c4c5d70966e12f122ef00bfdf2eac04022865013" ); - - assert_eq!(merkle_root(ref txids), expected_merkle_root); + assert_eq!(merkle_root(txids.span()), expected_merkle_root); } #[test] @@ -105,8 +104,7 @@ mod tests { let expected_merkle_root = hex_to_hash_rev( "968bd407fe881936f5140e3794c85962db7e8614c1bb9894bb78b59e56be4555" ); - - assert_eq!(merkle_root(ref txids), expected_merkle_root); + assert_eq!(merkle_root(txids.span()), expected_merkle_root); } #[test] @@ -136,8 +134,7 @@ mod tests { let expected_merkle_root: Digest = hex_to_hash_rev( "af77d9974359ae0699e62990b300d1e4663d03996176528bfa92aa24a65a45e1" ); - - assert_eq!(merkle_root(ref txids), expected_merkle_root); + assert_eq!(merkle_root(txids.span()), expected_merkle_root); } #[test] @@ -183,7 +180,6 @@ mod tests { let expected_merkle_root: Digest = hex_to_hash_rev( "c78e335cb8908ecda32ff5dd44e9985099572692761f7809a400f60ec58d452c" ); - - assert_eq!(merkle_root(ref txids), expected_merkle_root); + assert_eq!(merkle_root(txids.span()), expected_merkle_root); } } diff --git a/packages/utils/src/sha256.cairo b/packages/utils/src/sha256.cairo index 8094a6a3..85af97d6 100644 --- a/packages/utils/src/sha256.cairo +++ b/packages/utils/src/sha256.cairo @@ -201,12 +201,12 @@ fn compression(w: Span, i: usize, k: Span, mut h: Span) -> Span, i: usize) -> Span { let mut j = 0; let mut result = array![]; - while (j < 16) { + while (j != 16) { result.append(*data[i * 16 + j]); j += 1; }; let mut i = 16; - while (i < 64) { + while (i != 64) { let s0 = ssig0(*result[i - 15]); let s1 = ssig1(*result[i - 2]);