From 458b42b5d611eb0dd7cfa13558468dce75ce2acf Mon Sep 17 00:00:00 2001 From: Thomas Marchand Date: Thu, 31 Oct 2024 15:59:55 +0000 Subject: [PATCH] feat: use raito digest --- src/bitcoin/block.cairo | 3 +- src/bitcoin/block_height.cairo | 2 +- src/bitcoin/transactions/coinbase.cairo | 4 +- src/interfaces.cairo | 4 +- src/lib.cairo | 2 +- src/tests/fork_resolutions.cairo | 3 +- src/tests/utils.cairo | 4 +- src/utils/digest.cairo | 30 +++ src/utils/double_sha256.cairo | 5 +- src/utils/hash.cairo | 255 ------------------------ src/utils/hex.cairo | 4 +- src/utu_relay.cairo | 3 +- 12 files changed, 52 insertions(+), 267 deletions(-) create mode 100644 src/utils/digest.cairo delete mode 100644 src/utils/hash.cairo diff --git a/src/bitcoin/block.cairo b/src/bitcoin/block.cairo index 796535e..b8ae382 100644 --- a/src/bitcoin/block.cairo +++ b/src/bitcoin/block.cairo @@ -1,6 +1,7 @@ use crate::utils::{ - pow2::pow2_u128, numeric::u32_byte_reverse, hash::Digest, double_sha256::double_sha256_u32_array + pow2::pow2_u128, numeric::u32_byte_reverse, double_sha256::double_sha256_u32_array }; +use utils::hash::Digest; use core::traits::DivRem; /// Bitcoin block header structure based on: diff --git a/src/bitcoin/block_height.cairo b/src/bitcoin/block_height.cairo index 62d5101..b354139 100644 --- a/src/bitcoin/block_height.cairo +++ b/src/bitcoin/block_height.cairo @@ -1,7 +1,7 @@ use crate::interfaces::HeightProof; use crate::bitcoin::transactions::coinbase::get_coinbase_data; use crate::utils::double_sha256::double_sha256_parent; - +use utils::hash::Digest; /// Returns the block height given a block header, coinbase raw data, and an array of transaction /// hashes. diff --git a/src/bitcoin/transactions/coinbase.cairo b/src/bitcoin/transactions/coinbase.cairo index 9d2e1b7..80b5c81 100644 --- a/src/bitcoin/transactions/coinbase.cairo +++ b/src/bitcoin/transactions/coinbase.cairo @@ -1,4 +1,6 @@ -use crate::utils::{hash::Digest, double_sha256::double_sha256_byte_array}; +use crate::utils::double_sha256::double_sha256_byte_array; +use utils::hash::Digest; + #[derive(Drop)] pub struct CoinbaseData { diff --git a/src/interfaces.cairo b/src/interfaces.cairo index 1dab090..5948f36 100644 --- a/src/interfaces.cairo +++ b/src/interfaces.cairo @@ -1,5 +1,7 @@ -use crate::{utils::hash::Digest, bitcoin::block::BlockHeader}; +use crate::{bitcoin::block::BlockHeader, utils::digest::DigestStore}; use starknet::get_block_timestamp; +use utils::hash::Digest; + #[derive(Drop, Serde, Debug, Default, PartialEq, starknet::Store)] pub struct BlockStatus { diff --git a/src/lib.cairo b/src/lib.cairo index dfc186a..16daca3 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -10,7 +10,7 @@ pub mod bitcoin { } pub mod utils { pub mod hex; - pub mod hash; + pub mod digest; pub mod numeric; pub mod double_sha256; pub mod pow2; diff --git a/src/tests/fork_resolutions.cairo b/src/tests/fork_resolutions.cairo index fd14029..57dea6d 100644 --- a/src/tests/fork_resolutions.cairo +++ b/src/tests/fork_resolutions.cairo @@ -1,9 +1,10 @@ use super::super::interfaces::{HeightProof, IUtuRelayDispatcherTrait}; use crate::{ - interfaces::BlockStatus, utils::{hex::{from_hex, hex_to_hash_rev}, hash::Digest}, + interfaces::BlockStatus, utils::{hex::{from_hex, hex_to_hash_rev}}, bitcoin::block::{BlockHeader, BlockHeaderTrait}, tests::utils::{deploy_utu, BlockStatusIntoSpan, DigestIntoSpan}, }; +use utils::hash::Digest; use snforge_std::{start_cheat_block_timestamp, store}; diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 61fe4a3..7e881d8 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -1,6 +1,8 @@ -use crate::{interfaces::{BlockStatus, IUtuRelayDispatcher}, utils::hash::Digest}; +use crate::{interfaces::{BlockStatus, IUtuRelayDispatcher}}; use starknet::{ContractAddress, contract_address_const}; use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; +use utils::hash::Digest; + pub impl BlockStatusIntoSpan of Into> { fn into(self: BlockStatus) -> Span { diff --git a/src/utils/digest.cairo b/src/utils/digest.cairo new file mode 100644 index 0000000..5900d1e --- /dev/null +++ b/src/utils/digest.cairo @@ -0,0 +1,30 @@ +use starknet::storage_access::{Store, StorageBaseAddress}; +use utils::hash::Digest; + +pub impl DigestStore of starknet::Store { + fn read(address_domain: u32, base: StorageBaseAddress) -> starknet::SyscallResult { + Store::read(address_domain, base) + } + + fn write( + address_domain: u32, base: StorageBaseAddress, value: Digest + ) -> starknet::SyscallResult<()> { + Store::write(address_domain, base, value) + } + + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> starknet::SyscallResult { + Store::read_at_offset(address_domain, base, offset) + } + + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: Digest + ) -> starknet::SyscallResult<()> { + Store::write_at_offset(address_domain, base, offset, value) + } + + fn size() -> u8 { + Self::size() + } +} diff --git a/src/utils/double_sha256.cairo b/src/utils/double_sha256.cairo index c9883d5..a3ea0fd 100644 --- a/src/utils/double_sha256.cairo +++ b/src/utils/double_sha256.cairo @@ -4,7 +4,7 @@ //! Helpers for calculating double SHA256 hash digest. -use super::hash::{Digest, DigestTrait}; +use utils::hash::{Digest, DigestTrait}; use core::sha256::{compute_sha256_byte_array, compute_sha256_u32_array}; /// Calculates double sha256 digest of a concatenation of two hashes. @@ -40,7 +40,8 @@ pub fn double_sha256_u32_array(words: Array) -> Digest { #[cfg(test)] mod tests { - use crate::utils::{hex::from_hex, hash::Digest}; + use crate::utils::{hex::from_hex}; + use utils::hash::Digest; use super::{double_sha256_byte_array, double_sha256_u32_array, double_sha256_parent}; #[test] diff --git a/src/utils/hash.cairo b/src/utils/hash.cairo deleted file mode 100644 index a715931..0000000 --- a/src/utils/hash.cairo +++ /dev/null @@ -1,255 +0,0 @@ -// from: -// https://github.com/keep-starknet-strange/raito/blob/1d2a115d872ea3b6e68eab345b4dde4d50f20d1a/packages/utils/src/hash.cairo - -//! Digest digest struct and trait implementations. - -use core::fmt::{Display, Formatter, Error}; -use core::to_byte_array::AppendFormattedToByteArray; -use core::integer::u128_byte_reverse; -use core::hash::{Hash, HashStateTrait}; -use core::num::traits::zero::Zero; - -/// 256-bit hash digest. -/// Represented as an array of 4-byte words. -#[derive(Copy, Drop, Debug, Default, Serde, starknet::Store)] -pub struct Digest { - pub value: [u32; 8] -} - -#[generate_trait] -pub impl DigestImpl of DigestTrait { - #[inline(always)] - fn new(array: [u32; 8]) -> Digest { - Digest { value: array } - } -} - -pub impl UtuDigestIntoRaito of Into { - fn into(self: Digest) -> utils::hash::Digest { - utils::hash::Digest { value: self.value } - } -} - -pub impl RaitoDigestIntoUtu of Into { - fn into(self: utils::hash::Digest) -> Digest { - Digest { value: self.value } - } -} - -impl DigestZero of Zero { - fn zero() -> Digest { - Digest { value: [0_u32; 8] } - } - - fn is_zero(self: @Digest) -> bool { - self.value == @[0_u32; 8] - } - - fn is_non_zero(self: @Digest) -> bool { - !self.is_zero() - } -} - -/// Formats a `Digest` value for display. -impl DigestDisplay of Display { - fn fmt(self: @Digest, ref f: Formatter) -> Result<(), Error> { - let hash: u256 = (*self).into(); - hash.append_formatted_to_byte_array(ref f.buffer, 16); - Result::Ok(()) - } -} - -/// Compares two `Digest` values for equality. -impl DigestPartialEq of PartialEq { - fn eq(lhs: @Digest, rhs: @Digest) -> bool { - lhs.value == rhs.value - } -} - -/// Converts a `Digest` value into a `ByteArray`. -pub impl DigestIntoByteArray of Into { - fn into(self: Digest) -> ByteArray { - let mut bytes: ByteArray = Default::default(); - for word in self.value.span() { - bytes.append_word((*word).into(), 4); - }; - bytes - } -} - -const POW_2_32: u128 = 0x100000000; -const POW_2_64: u128 = 0x10000000000000000; -const POW_2_96: u128 = 0x1000000000000000000000000; -const NZ_POW2_32_128: NonZero = 0x100000000; -const NZ_POW2_32_64: NonZero = 0x100000000; - -/// Converts a `u256` value into a `Digest` type and reverse bytes order. -/// u256 is big-endian like in explorer, while Digest is little-endian order. -pub impl U256IntoDigest of Into { - fn into(self: u256) -> Digest { - let low: u128 = u128_byte_reverse(self.high); - let high: u128 = u128_byte_reverse(self.low); - - let (q_96, high_32_0) = DivRem::div_rem(high, NZ_POW2_32_128); - let (q_64, high_64_32) = DivRem::div_rem(q_96, NZ_POW2_32_128); - let q_64_t: u64 = q_64.try_into().unwrap(); - let (high_128_96, high_96_64) = DivRem::div_rem(q_64_t, NZ_POW2_32_64); - - let (q_96, low_32_0) = DivRem::div_rem(low, NZ_POW2_32_128); - let (q_64, low_64_32) = DivRem::div_rem(q_96, NZ_POW2_32_128); - let q_64_t: u64 = q_64.try_into().unwrap(); - let (low_128_96, low_96_64) = DivRem::div_rem(q_64_t, NZ_POW2_32_64); - - Digest { - value: [ - high_128_96.try_into().unwrap(), - high_96_64.try_into().unwrap(), - high_64_32.try_into().unwrap(), - high_32_0.try_into().unwrap(), - low_128_96.try_into().unwrap(), - low_96_64.try_into().unwrap(), - low_64_32.try_into().unwrap(), - low_32_0.try_into().unwrap(), - ] - } - } -} - - -/// Converts a `Digest` value into a `u256` type and reverse bytes order. -/// Digest is little-endian order, while u256 is big-endian like in explorer. -pub impl DigestIntoU256 of Into { - fn into(self: Digest) -> u256 { - let [a, b, c, d, e, f, g, h] = self.value; - - let low: u128 = h.into() + g.into() * POW_2_32 + f.into() * POW_2_64 + e.into() * POW_2_96; - let high: u128 = d.into() + c.into() * POW_2_32 + b.into() * POW_2_64 + a.into() * POW_2_96; - - u256 { low: u128_byte_reverse(high), high: u128_byte_reverse(low) } - } -} - - -pub impl DigestHash, +Drop> of Hash { - fn update_state(state: S, value: Digest) -> S { - let u256_digest: u256 = value.into(); - - let state = state.update(u256_digest.low.into()); - let state = state.update(u256_digest.high.into()); - state - } -} - -#[cfg(test)] -mod tests { - use crate::utils::hex::from_hex; - use super::Digest; - - #[test] - fn test_u256_into_hash() { - let u256_value = u256 { - low: 0x1234567890abcdef1234567890abcdef_u128, - high: 0xfedcba0987654321fedcba0987654321_u128, - }; - - let result_hash = u256_value.into(); - - let expected_hash = Digest { - value: [ - 0xefcdab90, - 0x78563412, - 0xefcdab90, - 0x78563412, - 0x21436587, - 0x09badcfe, - 0x21436587, - 0x09badcfe - ], - }; - - assert(result_hash == expected_hash, 'u256 to hash conversion failed'); - } - - #[test] - fn test_hash_to_u256() { - let hash_value = Digest { - value: [ - 0xfedcba09, - 0x87654321, - 0xfedcba09, - 0x87654321, - 0x12345678, - 0x90abcdef, - 0x12345678, - 0x90abcdef, - ], - }; - - let result_u256 = hash_value.into(); - - let expected_u256 = u256 { - high: 0xefcdab9078563412efcdab9078563412_u128, - low: 0x2143658709badcfe2143658709badcfe_u128, - }; - - assert(result_u256 == expected_u256, 'hash to u256 conversion failed'); - } - - #[test] - fn test_hash_to_u256_to_hash() { - let hash_value = Digest { - value: [ - 0xfedcba09, - 0x87654321, - 0xfedcba09, - 0x87654321, - 0x12345678, - 0x90abcdef, - 0x12345678, - 0x90abcdef, - ], - }; - - let u256_value: u256 = hash_value.into(); - let result_hash: Digest = u256_value.into(); - - assert(result_hash == hash_value, 'hash->u256->hash failed'); - } - - #[test] - fn test_u256_to_hash_to_u256() { - let u256_value = u256 { - high: 0xefcdab9078563412efcdab9078563412_u128, - low: 0x00112233445566778899aabbccddeeff_u128, - }; - - let hash_value: Digest = u256_value.into(); - let result_u256: u256 = hash_value.into(); - - assert(result_u256 == u256_value, 'u256->hash->u256 failed'); - } - - #[test] - fn test_hash_into_bytearray() { - let hash = Digest { - value: [ - 0x12345678_u32, - 0x9abcdef0_u32, - 0x11223344_u32, - 0x55667788_u32, - 0xaabbccdd_u32, - 0xeeff0011_u32, - 0x22334455_u32, - 0x66778899_u32 - ] - }; - - let byte_array: ByteArray = hash.into(); - - let expected_byte_array = from_hex( - "123456789abcdef01122334455667788aabbccddeeff00112233445566778899" - ); - - assert(byte_array == expected_byte_array, 'hash to ByteArray failed'); - } -} diff --git a/src/utils/hex.cairo b/src/utils/hex.cairo index 7f48db7..124ea73 100644 --- a/src/utils/hex.cairo +++ b/src/utils/hex.cairo @@ -2,7 +2,7 @@ // https://github.com/keep-starknet-strange/raito/blob/1d2a115d872ea3b6e68eab345b4dde4d50f20d1a/packages/utils/src/hex.cairo //! Hex helpers -use crate::utils::hash::Digest; +use utils::hash::Digest; /// Get bytes from hex (base16) pub fn from_hex(hex_string: ByteArray) -> ByteArray { @@ -88,7 +88,7 @@ fn hex_char_to_nibble(hex_char: u8) -> u8 { #[cfg(test)] mod tests { use super::{from_hex, to_hex, hex_to_hash_rev}; - use crate::utils::hash::Digest; + use utils::hash::Digest; #[test] fn test_bytes_from_hex() { diff --git a/src/utu_relay.cairo b/src/utu_relay.cairo index 509d274..055ec05 100644 --- a/src/utu_relay.cairo +++ b/src/utu_relay.cairo @@ -2,17 +2,18 @@ pub mod UtuRelay { use starknet::storage::{StorageMapWriteAccess}; use crate::{ - utils::hash::Digest, bitcoin::{ block::{BlockHeader, BlockHashTrait, PowVerificationTrait, compute_pow_from_target}, block_height::get_block_height }, + utils::digest::DigestStore, interfaces::{IUtuRelay, BlockStatus, HeightProof, BlockStatusTrait} }; use starknet::storage::{ StorageMapReadAccess, StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry, Map }; + use utils::hash::Digest; use core::num::traits::zero::Zero;