From c9ea7554698db0cbb7fb5c69c93cfb8cc01c3f7a Mon Sep 17 00:00:00 2001 From: Tristan <122918260+TAdev0@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:43:08 +0100 Subject: [PATCH] chore: improve comments + clean codebase (#291) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Maciej KamiƄski @ StarkWare --- Scarb.toml | 2 +- packages/client/src/test.cairo | 20 ++++---- packages/consensus/src/codec.cairo | 46 +++++++++++-------- packages/consensus/src/lib.cairo | 18 ++++---- packages/consensus/src/types/block.cairo | 17 ++++--- .../consensus/src/types/chain_state.cairo | 11 ++--- .../consensus/src/types/transaction.cairo | 26 ++++++----- packages/consensus/src/types/utxo_set.cairo | 15 +++--- packages/consensus/src/validation/block.cairo | 18 ++++---- .../consensus/src/validation/coinbase.cairo | 46 ++++++++++--------- .../consensus/src/validation/difficulty.cairo | 30 ++++++------ .../consensus/src/validation/locktime.cairo | 26 +++++------ .../consensus/src/validation/script.cairo | 23 ++++++++-- .../consensus/src/validation/timestamp.cairo | 22 +++++---- .../src/validation/transaction.cairo | 38 +++++++-------- packages/consensus/src/validation/work.cairo | 17 +++---- packages/utils/src/bit_shifts.cairo | 16 +++---- packages/utils/src/bytearray.cairo | 5 +- packages/utils/src/hash.cairo | 26 +++++------ packages/utils/src/hex.cairo | 13 ++++-- packages/utils/src/lib.cairo | 5 +- packages/utils/src/merkle_tree.cairo | 2 +- packages/utils/src/numeric.cairo | 6 ++- packages/utils/src/sha256.cairo | 6 +-- packages/utils/src/sort.cairo | 5 +- packages/utreexo/src/lib.cairo | 16 +++---- packages/utreexo/src/stump/proof.cairo | 9 ++-- packages/utreexo/src/stump/state.cairo | 2 + packages/utreexo/src/test.cairo | 2 +- .../utreexo/src/vanilla/accumulator.cairo | 2 +- .../src/vanilla/accumulator_tests.cairo | 25 +++++----- packages/utreexo/src/vanilla/proof.cairo | 3 +- packages/utreexo/src/vanilla/state.cairo | 2 + 33 files changed, 285 insertions(+), 235 deletions(-) diff --git a/Scarb.toml b/Scarb.toml index 6eaa82ee..e602349d 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -10,7 +10,7 @@ repository = "https://github.com/keep-starknet-strange/raito" license-file = "LICENSE" [workspace.dependencies] -cairo_test = "2.8.0" +cairo_test = "2.8.4" shinigami_engine = { git = "https://github.com/keep-starknet-strange/shinigami.git", rev = "3415ed6" } [profile.cairo1-run.cairo] diff --git a/packages/client/src/test.cairo b/packages/client/src/test.cairo index cdaa7b98..5607dde0 100644 --- a/packages/client/src/test.cairo +++ b/packages/client/src/test.cairo @@ -1,37 +1,37 @@ +use core::serde::Serde; +use core::testing::get_available_gas; use consensus::types::block::Block; use consensus::types::chain_state::{ChainState, BlockValidatorImpl}; use consensus::types::utxo_set::{UtxoSet, UtxoSetTrait}; use utreexo::stump::accumulator::StumpUtreexoAccumulator; use utreexo::stump::state::UtreexoStumpState; use utreexo::stump::proof::UtreexoBatchProof; -use core::testing::get_available_gas; -use core::serde::Serde; /// Integration testing program arguments. #[derive(Drop)] struct Args { - /// Current (initial) chain state + /// Current (initial) chain state. chain_state: ChainState, - /// Batch of blocks that have to be applied to the current chain state + /// Batch of blocks that have to be applied to the current chain state. blocks: Array, - /// Expected chain state (that we want to compare the result with) + /// Expected chain state (that we want to compare the result with). expected_chain_state: ChainState, - /// Optional Utreexo arguments + /// Optional Utreexo arguments. utreexo_args: Option, /// If this flag is set, locking scripts will be executed execute_script: bool, } -/// Utreexo arguments necessary for constraining the UTXO set +/// Utreexo arguments necessary for constraining the UTXO set. #[derive(Drop, Serde)] struct UtreexoArgs { - /// Current (initial) accumulator state + /// Current (initial) accumulator state. state: UtreexoStumpState, /// Batch inclusion proof for TXOs spent during the current block. /// Note that it doesn't support flow with multiple blocks applied /// in a single program run. proof: UtreexoBatchProof, - /// Expected accumulator state at the end of the execution + /// Expected accumulator state at the end of the execution. expected_state: UtreexoStumpState, } @@ -105,7 +105,7 @@ fn main(arguments: Array) -> Array { } /// Workaround for handling missing `utreexo_args` field. -/// Rough analogue of `#[serde(default)]` +/// Rough analogue of `#[serde(default)]`. impl ArgsSerde of Serde { fn serialize(self: @Args, ref output: Array) { panic!("not implemented"); diff --git a/packages/consensus/src/codec.cairo b/packages/consensus/src/codec.cairo index 7f5795e0..cdbbf987 100644 --- a/packages/consensus/src/codec.cairo +++ b/packages/consensus/src/codec.cairo @@ -4,10 +4,10 @@ use super::types::transaction::{Transaction, TxIn, TxOut, OutPoint}; use utils::hash::Digest; pub trait Encode { - /// Encode using Bitcoin codec and append to the buffer. + /// Encodes using Bitcoin codec and appends to the buffer. fn encode_to(self: @T, ref dest: ByteArray); - /// Encode using Bitcoin codec and return byte array + /// Encodes using Bitcoin codec and returns a `ByteArray`. fn encode( self: @T ) -> ByteArray { @@ -17,6 +17,7 @@ pub trait Encode { } } +/// `Encode` trait implementation for `Span`. pub impl EncodeSpan> of Encode> { fn encode_to(self: @Span, ref dest: ByteArray) { let items = *self; @@ -27,6 +28,7 @@ pub impl EncodeSpan> of Encode> { } } +/// `Encode` trait implementation for `ByteArray`. pub impl EncodeByteArray of Encode { fn encode_to(self: @ByteArray, ref dest: ByteArray) { encode_compact_size(self.len(), ref dest); @@ -34,24 +36,28 @@ pub impl EncodeByteArray of Encode { } } +/// `Encode` trait implementation for `u32`. pub impl EncodeU32 of Encode { fn encode_to(self: @u32, ref dest: ByteArray) { dest.append_word_rev((*self).into(), 4); } } +/// `Encode` trait implementation for `u64`. pub impl EncodeU64 of Encode { fn encode_to(self: @u64, ref dest: ByteArray) { dest.append_word_rev((*self).into(), 8); } } +/// `Encode` trait implementation for `Digest`. pub impl EncodeHash of Encode { fn encode_to(self: @Digest, ref dest: ByteArray) { dest.append(@(*self).into()); } } +/// `Encode` trait implementation for `TxIn`. pub impl EncodeTxIn of Encode { fn encode_to(self: @TxIn, ref dest: ByteArray) { self.previous_output.encode_to(ref dest); @@ -60,6 +66,7 @@ pub impl EncodeTxIn of Encode { } } +/// `Encode` trait implementation for `TxOut`. pub impl EncodeTxOut of Encode { fn encode_to(self: @TxOut, ref dest: ByteArray) { self.value.encode_to(ref dest); @@ -67,6 +74,7 @@ pub impl EncodeTxOut of Encode { } } +/// `Encode` trait implementation for `OutPoint`. pub impl EncodeOutpoint of Encode { fn encode_to(self: @OutPoint, ref dest: ByteArray) { self.txid.encode_to(ref dest); @@ -74,6 +82,7 @@ pub impl EncodeOutpoint of Encode { } } +/// `Encode` trait implementation for `Transaction`. pub impl EncodeTransaction of Encode { fn encode_to(self: @Transaction, ref dest: ByteArray) { self.version.encode_to(ref dest); @@ -85,7 +94,7 @@ pub impl EncodeTransaction of Encode { #[generate_trait] pub impl TransactionCodecImpl of TransactionCodec { - /// Reencode transaction with witness fields (for computing wtxid) given the legacy encoded + /// Reencodes transaction with witness fields (for computing wtxid) given the legacy encoded /// bytes. /// We use this method to avoid double serialization. fn encode_with_witness(self: @Transaction, legacy_bytes: @ByteArray) -> ByteArray { @@ -117,9 +126,9 @@ pub impl TransactionCodecImpl of TransactionCodec { /// /// https://learnmeabitcoin.com/technical/general/compact-size/ pub fn encode_compact_size(len: usize, ref dest: ByteArray) { - // first covert the len into the felt252 + // First convert the len into a `felt252` let val: felt252 = len.try_into().unwrap(); - // then append as the reverse word is this correct i think + if (len < 253) { dest.append_word_rev(val, 1); } else if (len < 65536) { @@ -131,11 +140,12 @@ pub fn encode_compact_size(len: usize, ref dest: ByteArray) { } // Note: `usize` is a `u32` alias, so lens >= 4,294,967,296 are not handled. } + #[cfg(test)] mod tests { - use utils::hex::{from_hex, hex_to_hash_rev}; use crate::types::transaction::{Transaction, TxIn, TxOut, OutPoint}; use super::{Encode, TransactionCodec, encode_compact_size}; + use utils::hex::{from_hex, hex_to_hash_rev}; #[test] fn test_encode_compact_size1() { @@ -182,7 +192,7 @@ mod tests { #[test] fn test_encode_txout() { - // block 170 coinbase tx + // Block 170 coinbase tx let txout = @TxOut { value: 5000000000_u64, pk_script: @from_hex( @@ -201,7 +211,7 @@ mod tests { #[test] fn test_encode_outpoint() { - // block 170 coinbase tx b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082 + // Block 170 coinbase tx b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082 let outpoint = OutPoint { txid: hex_to_hash_rev( "0000000000000000000000000000000000000000000000000000000000000000" @@ -222,7 +232,7 @@ mod tests { #[test] fn test_encode_outpoint2() { - //block 170 tx f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16 + //Block 170 tx f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16 let outpoint = OutPoint { txid: hex_to_hash_rev( "0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9" @@ -243,7 +253,7 @@ mod tests { #[test] fn test_encode_txin1() { - // tx b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082 + // Tx b1fea52486ce0c62bb442b530a3f0132b826c74e473d1f2c220bfa78111c5082 let txin = @TxIn { script: @from_hex("04ffff001d0102"), sequence: 0xffffffff, @@ -269,7 +279,7 @@ mod tests { #[test] fn test_encode_txin2() { - // tx 4ff32a7e58200897220ce4615e30e3e414991222d7eda27e693116abea8b8f33, + // Tx 4ff32a7e58200897220ce4615e30e3e414991222d7eda27e693116abea8b8f33, // input 2 let txin = @TxIn { script: @from_hex( @@ -298,7 +308,7 @@ mod tests { #[test] fn test_encode_tx1() { - // tx 4ff32a7e58200897220ce4615e30e3e414991222d7eda27e693116abea8b8f33 + // Tx 4ff32a7e58200897220ce4615e30e3e414991222d7eda27e693116abea8b8f33 let tx = @Transaction { version: 1_u32, is_segwit: false, @@ -383,7 +393,7 @@ mod tests { #[test] fn test_encode_tx_many_inputs() { - // tx 23d5c86600b72cd512aecebd68a7274f611cd96eb9106125f4ef2502f54effa5 + // Tx 23d5c86600b72cd512aecebd68a7274f611cd96eb9106125f4ef2502f54effa5 let tx = @Transaction { version: 1, is_segwit: false, @@ -603,7 +613,7 @@ mod tests { #[test] fn test_encode_tx_many_outputs() { - // tx 3e6cc776f588a464c98e8f701cdcde651c7b3620c44c65099fb3d2f4d8ea260e + // Tx 3e6cc776f588a464c98e8f701cdcde651c7b3620c44c65099fb3d2f4d8ea260e let tx = @Transaction { version: 1, is_segwit: false, @@ -711,7 +721,7 @@ mod tests { #[test] fn test_encode_tx_witness1() { - // tx 65d8bd45f01bd6209d8695d126ba6bb4f2936501c12b9a1ddc9e38600d35aaa2 + // Tx 65d8bd45f01bd6209d8695d126ba6bb4f2936501c12b9a1ddc9e38600d35aaa2 let tx = @Transaction { version: 2, is_segwit: true, @@ -775,7 +785,7 @@ mod tests { #[test] fn test_encode_tx_witness2() { - // tx 7ee8997b455d8231c162277943a9a2d2d98800faa51da79c17eeb5156739a628, + // Tx 7ee8997b455d8231c162277943a9a2d2d98800faa51da79c17eeb5156739a628, let tx = @Transaction { version: 2, is_segwit: true, @@ -861,8 +871,8 @@ mod tests { } #[test] fn test_encode_tx_witness3() { - /// tx c06aaaa2753dc4e74dd4fe817522dc3c126fd71792dd9acfefdaff11f8ff954d - /// data from example https://learnmeabitcoin.com/technical/transaction/wtxid/ + /// Tx c06aaaa2753dc4e74dd4fe817522dc3c126fd71792dd9acfefdaff11f8ff954d + /// Data from example https://learnmeabitcoin.com/technical/transaction/wtxid/ let tx = @Transaction { version: 1, is_segwit: true, diff --git a/packages/consensus/src/lib.cairo b/packages/consensus/src/lib.cairo index 2d01f994..5c2815e7 100644 --- a/packages/consensus/src/lib.cairo +++ b/packages/consensus/src/lib.cairo @@ -1,17 +1,17 @@ +pub mod codec; +pub mod types { + pub mod block; + pub mod chain_state; + pub mod transaction; + pub mod utxo_set; +} pub mod validation { - pub mod difficulty; + pub mod block; pub mod coinbase; + pub mod difficulty; pub mod locktime; pub mod script; pub mod timestamp; pub mod transaction; pub mod work; - pub mod block; -} -pub mod codec; -pub mod types { - pub mod chain_state; - pub mod block; - pub mod transaction; - pub mod utxo_set; } diff --git a/packages/consensus/src/types/block.cairo b/packages/consensus/src/types/block.cairo index fe6294e2..91e885ae 100644 --- a/packages/consensus/src/types/block.cairo +++ b/packages/consensus/src/types/block.cairo @@ -2,11 +2,11 @@ //! //! The data is expected to be prepared in advance and passed as program arguments. +use core::fmt::{Display, Formatter, Error}; +use super::transaction::Transaction; use utils::hash::Digest; use utils::double_sha256::double_sha256_u32_array; use utils::numeric::u32_byte_reverse; -use super::transaction::Transaction; -use core::fmt::{Display, Formatter, Error}; /// Represents a block in the blockchain. #[derive(Drop, Copy, Debug, PartialEq, Default, Serde)] @@ -54,7 +54,7 @@ pub struct Header { #[generate_trait] pub impl BlockHashImpl of BlockHash { - /// Compute hash of the block header given the missing fields. + /// Computes the hash of the block header given the missing fields. fn hash(self: @Header, prev_block_hash: Digest, merkle_root: Digest) -> Digest { let mut header_data_u32: Array = array![]; @@ -70,13 +70,14 @@ pub impl BlockHashImpl of BlockHash { } } -/// Empty transaction data +/// `Default` trait implementation of `TransactionData`, i.e., empty transaction data. pub impl TransactionDataDefault of Default { fn default() -> TransactionData { TransactionData::Transactions(array![].span()) } } +/// `Display` trait implementation for `Block`. impl BlockDisplay of Display { fn fmt(self: @Block, ref f: Formatter) -> Result<(), Error> { let data = match *self.data { @@ -89,6 +90,7 @@ impl BlockDisplay of Display { } } +/// `Display` trait implementation for `Header`. impl HeaderDisplay of Display
{ fn fmt(self: @Header, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!( @@ -103,6 +105,7 @@ impl HeaderDisplay of Display
{ } } +/// `Display` trait implementation for `TransactionData`. impl TransactionDataDisplay of Display { fn fmt(self: @TransactionData, ref f: Formatter) -> Result<(), Error> { match *self { @@ -117,8 +120,8 @@ impl TransactionDataDisplay of Display { #[cfg(test)] mod tests { - use super::{Header, BlockHash}; use crate::types::chain_state::ChainState; + use super::{Header, BlockHash}; use utils::hash::Digest; #[test] @@ -128,7 +131,7 @@ mod tests { .best_block_hash = 0x000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55_u256 .into(); - // block 170 + // Block 170 let header = Header { version: 1_u32, time: 1231731025_u32, bits: 0x1d00ffff_u32, nonce: 1889418792_u32 }; @@ -153,7 +156,7 @@ mod tests { .best_block_hash = 0x000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55_u256 .into(); - // block 170 + // Block 170 let header = Header { version: 1_u32, time: 1231731025_u32, bits: 0x1d00ffff_u32, nonce: 1889418792_u32 }; diff --git a/packages/consensus/src/types/chain_state.cairo b/packages/consensus/src/types/chain_state.cairo index a2fc4222..e31a5e17 100644 --- a/packages/consensus/src/types/chain_state.cairo +++ b/packages/consensus/src/types/chain_state.cairo @@ -1,5 +1,5 @@ //! Chain state is a minimal subset of data required to unambiguosly -//! define a particular block chain starting at the genesis. +//! define a particular blockchain starting at the genesis. //! //! Chain state alone is not enough to do full block validation, however //! it is sufficient to validate block headers. @@ -11,8 +11,7 @@ use crate::validation::{ work::{validate_proof_of_work, compute_total_work}, block::compute_and_validate_tx_data, script::validate_scripts }; -use super::block::{BlockHash, Block, TransactionData}; -use super::utxo_set::UtxoSet; +use super::{block::{BlockHash, Block, TransactionData}, utxo_set::UtxoSet}; use utils::hash::Digest; /// Represents the state of the blockchain. @@ -34,10 +33,9 @@ pub struct ChainState { /// it's possible that one block could have an earlier timestamp /// than a block that came before it in the chain. pub prev_timestamps: Span, - /// Median Time Past (MTP) of the current block } -/// Represents the initial state after genesis block. +/// `Default` implementation of `ChainState` representing the initial state after genesis block. /// https://github.com/bitcoin/bitcoin/blob/ee367170cb2acf82b6ff8e0ccdbc1cce09730662/src/kernel/chainparams.cpp#L99 impl ChainStateDefault of Default { fn default() -> ChainState { @@ -55,7 +53,7 @@ impl ChainStateDefault of Default { } } -/// Full block validator (w/o bitcoin script checks and utxo inclusion verification for now). +/// Full block validator (w/o Bitcoin script checks and UTXO inclusion verification for now). #[generate_trait] pub impl BlockValidatorImpl of BlockValidator { fn validate_and_apply( @@ -111,6 +109,7 @@ pub impl BlockValidatorImpl of BlockValidator { } } +/// `Display` trait implementation for `ChainState`. impl ChainStateDisplay of Display { fn fmt(self: @ChainState, ref f: Formatter) -> Result<(), Error> { let mut prev_ts: ByteArray = Default::default(); diff --git a/packages/consensus/src/types/transaction.cairo b/packages/consensus/src/types/transaction.cairo index 96ad8662..d18eea74 100644 --- a/packages/consensus/src/types/transaction.cairo +++ b/packages/consensus/src/types/transaction.cairo @@ -3,10 +3,10 @@ //! Types are extended with extra information required for validation. //! The data is expected to be prepared in advance and passed as program arguments. -use utils::{hash::Digest, bytearray::{ByteArraySnapHash, ByteArraySnapSerde}}; use core::fmt::{Display, Formatter, Error}; use core::hash::{HashStateTrait, HashStateExTrait, Hash}; use core::poseidon::PoseidonTrait; +use utils::{hash::Digest, bytearray::{ByteArraySnapHash, ByteArraySnapSerde}}; /// Represents a transaction. /// https://learnmeabitcoin.com/technical/transaction/ @@ -44,7 +44,7 @@ pub struct TxIn { pub previous_output: OutPoint, /// The witness data for transactions. /// A list of items (of different size) pushed onto stack before sig script execution. - /// Can be empty if this particular inputs spends a non-segwit output. + /// Can be empty if this particular input spends a non-segwit output. /// NOTE that this field actually belongs to the transaction, but we store it in the input for /// convenience. pub witness: Span, @@ -90,7 +90,7 @@ pub struct OutPoint { pub data: TxOut, /// The height of the block that contains this output (meta field). /// Used to validate coinbase tx spending (not sooner than 100 blocks) and relative timelocks - /// (it has been more than X block since the transaction containing this output was mined). + /// (it has been more than X blocks since the transaction containing this output was mined). pub block_height: u32, /// The median time past of the block that contains this output (meta field). /// This is the median timestamp of the previous 11 blocks. @@ -98,9 +98,7 @@ pub struct OutPoint { /// It ensures that the transaction containing this output has been mined for more than X /// seconds. pub median_time_past: u32, - // Determine if the outpoint is a coinbase transaction - // Has 100 or more block confirmation, - // is added when block are queried + /// Determines if the outpoint is a coinbase transaction. pub is_coinbase: bool } @@ -130,9 +128,7 @@ pub struct TxOut { pub cached: bool, } -/// -/// Custom implementation of the Hash trait for TxOut removed cached field. -/// +/// Custom implementation of the `Hash` trait for `TxOut`, excluding `cached` field. impl TxOutHash, +Drop> of Hash { fn update_state(state: S, value: TxOut) -> S { let state = state.update(value.value.into()); @@ -141,19 +137,22 @@ impl TxOutHash, +Drop> of Hash { } } +/// `Outpoint` Poseidon hash implementation. #[generate_trait] -pub impl OutPointImpl of OutPointTrait { +pub impl OutPointHashImpl of OutPointHashTrait { fn hash(self: @OutPoint) -> felt252 { PoseidonTrait::new().update_with(*self).finalize() } } +/// `Default` trait implementation for `TxOut`. impl TxOutDefault of Default { fn default() -> TxOut { TxOut { value: 0, pk_script: @"", cached: false, } } } +/// `Display` trait implementation for `Transaction`. impl TransactionDisplay of Display { fn fmt(self: @Transaction, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!( @@ -169,6 +168,7 @@ impl TransactionDisplay of Display { } } +/// `Display` trait implementation for `TxIn`. impl TxInDisplay of Display { fn fmt(self: @TxIn, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!( @@ -183,6 +183,7 @@ impl TxInDisplay of Display { } } +/// `Display` trait implementation for `OutPoint`. impl OutPointDisplay of Display { fn fmt(self: @OutPoint, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!( @@ -206,6 +207,7 @@ impl OutPointDisplay of Display { } } +/// `Display` trait implementation for `TxOut`. impl TxOutDisplay of Display { fn fmt(self: @TxOut, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!( @@ -221,9 +223,9 @@ impl TxOutDisplay of Display { #[cfg(test)] mod tests { - use super::{OutPoint, TxOut, HashStateTrait, HashStateExTrait, OutPointTrait}; - use utils::hex::{hex_to_hash_rev, from_hex}; use core::poseidon::PoseidonTrait; + use utils::hex::{hex_to_hash_rev, from_hex}; + use super::{OutPoint, TxOut, HashStateTrait, HashStateExTrait, OutPointHashTrait}; fn hash(tx: @TxOut) -> felt252 { PoseidonTrait::new().update_with(*tx).finalize() diff --git a/packages/consensus/src/types/utxo_set.cairo b/packages/consensus/src/types/utxo_set.cairo index 73a7f1ff..3537fa70 100644 --- a/packages/consensus/src/types/utxo_set.cairo +++ b/packages/consensus/src/types/utxo_set.cairo @@ -10,8 +10,8 @@ //! Utreexo accumulator or local cache. use core::dict::Felt252Dict; -use super::transaction::{OutPoint, OutPointTrait}; use consensus::validation::transaction::is_pubscript_unspendable; +use super::transaction::{OutPoint, OutPointHashTrait}; pub const TX_OUTPUT_STATUS_NONE: u8 = 0; pub const TX_OUTPUT_STATUS_UNSPENT: u8 = 1; @@ -26,13 +26,14 @@ pub struct UtxoSet { /// Number of pending cached UTXOs that must be spent within the current block(s). pub num_cached: i32, /// Statuses of UTXOs created or spent within the current block(s). - /// Note that to preserve the ordering, statuses has to be updated right after a + /// Note that to preserve the ordering, statuses have to be updated right after a /// particular output is created or spent. pub cache: Felt252Dict, } #[generate_trait] pub impl UtxoSetImpl of UtxoSetTrait { + /// Adds an outpoint to the UTXO set. fn add(ref self: UtxoSet, outpoint: OutPoint) -> Result<(), ByteArray> { let hash = outpoint.hash(); @@ -51,18 +52,19 @@ pub impl UtxoSetImpl of UtxoSetTrait { } } + /// Spends an outpoint contained in the UTXO set. fn spend(ref self: UtxoSet, outpoint: @OutPoint) -> Result<(), ByteArray> { let hash = outpoint.hash(); let status = self.cache.get(hash); if status == TX_OUTPUT_STATUS_NONE { - // Extra check that can be removed later. + // Extra check that can be removed later assert!(!*outpoint.data.cached, "cached output was not cached"); self.cache.insert(hash, TX_OUTPUT_STATUS_SPENT); self.leaves_to_delete.append(hash); Result::Ok(()) } else if status == TX_OUTPUT_STATUS_UNSPENT { - // Extra check that can be removed later. + // Extra check that can be removed later assert!(*outpoint.data.cached, "non-cached output was cached"); self.cache.insert(hash, TX_OUTPUT_STATUS_SPENT); @@ -73,6 +75,7 @@ pub impl UtxoSetImpl of UtxoSetTrait { } } + /// Ensures all outpoints in the UTXO set have been processed. fn finalize(ref self: UtxoSet) -> Result<(), ByteArray> { if self.num_cached != 0 { Result::Err("There are unprocessed cached outputs") @@ -189,7 +192,7 @@ mod tests { } } - /// block 170 tx1 v0 -> block9 tx coinbase v0 + // Block 170 tx1 v0 -> block9 tx coinbase v0 fn get_outpoint() -> OutPoint { OutPoint { txid: hex_to_hash_rev( @@ -209,7 +212,7 @@ mod tests { } } - /// outpoint hash of first output spent block 170 + // Outpoint hash of first output spent block 170 #[test] fn test_poseidon1() { let outpoint: OutPoint = get_outpoint(); diff --git a/packages/consensus/src/validation/block.cairo b/packages/consensus/src/validation/block.cairo index 71a134ae..da088d4e 100644 --- a/packages/consensus/src/validation/block.cairo +++ b/packages/consensus/src/validation/block.cairo @@ -1,17 +1,17 @@ //! Block validation helpers. + +use core::num::traits::zero::Zero; use crate::types::utxo_set::{UtxoSet, UtxoSetTrait}; use crate::types::transaction::{OutPoint, Transaction}; use crate::codec::{Encode, TransactionCodec}; -use crate::validation::coinbase::is_coinbase_txid_duplicated; +use crate::validation::{coinbase::is_coinbase_txid_duplicated, transaction::validate_transaction}; use utils::{hash::Digest, merkle_tree::merkle_root, double_sha256::double_sha256_byte_array,}; -use super::transaction::validate_transaction; -use core::num::traits::zero::Zero; const MAX_BLOCK_WEIGHT_LEGACY: usize = 1_000_000; const MAX_BLOCK_WEIGHT: usize = 4_000_000; const SEGWIT_BLOCK: usize = 481_824; -/// Validate block weight. +/// Validates block weight. /// Blocks after Segwit upgrade have a limit of 4,000,000 weight units. pub fn validate_block_weight(weight: usize) -> Result<(), ByteArray> { if (weight > MAX_BLOCK_WEIGHT) { @@ -21,13 +21,14 @@ pub fn validate_block_weight(weight: usize) -> Result<(), ByteArray> { ) ); } + Result::Ok(()) } /// Validates transactions and aggregates: /// - Total fee /// - TXID merkle root -/// - wTXID commitment (only for blocks after Segwit upgrade, otherwise return zero hash) +/// - wTXID commitment (only for blocks after Segwit upgrade, otherwise returns zero hash) /// - Block weight pub fn compute_and_validate_tx_data( txs: Span, @@ -52,8 +53,8 @@ pub fn compute_and_validate_tx_data( .encode_with_witness(tx_bytes_legacy); // SegWit transaction encoding /// The wTXID for the coinbase transaction must be set to all zeros. This is because - /// it's eventually going to contain the commitment inside it - /// see https://learnmeabitcoin.com/technical/transaction/wtxid/#commitment + /// it's eventually going to contain the commitment inside it. + /// See https://learnmeabitcoin.com/technical/transaction/wtxid/#commitment let wtxid = if is_coinbase { Zero::zero() } else { @@ -72,10 +73,11 @@ pub fn compute_and_validate_tx_data( txids.append(txid); if (is_coinbase) { - // skip duplicated txid (it's not possible to spend these coinbase outputs) + // Skip duplicated txid (it's not possible to spend these coinbase outputs) if (is_coinbase_txid_duplicated(txid, block_height)) { continue; } + let mut vout = 0; for output in *tx .outputs { diff --git a/packages/consensus/src/validation/coinbase.cairo b/packages/consensus/src/validation/coinbase.cairo index 570bc5b4..34f59ac8 100644 --- a/packages/consensus/src/validation/coinbase.cairo +++ b/packages/consensus/src/validation/coinbase.cairo @@ -12,7 +12,6 @@ const WTNS_PK_SCRIPT_PREFIX: felt252 = 116705705699821; // 0x6a24aa21a9ed const FIRST_DUP_TXID: u256 = 0xe3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468; const SECOND_DUP_TXID: u256 = 0xd5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599; - /// Validates coinbase transaction. pub fn validate_coinbase( tx: @Transaction, total_fees: u64, block_height: u32, wtxid_root: Digest, @@ -35,10 +34,10 @@ pub fn validate_coinbase( let block_reward = compute_block_reward(block_height); assert(total_output_amount <= total_fees + block_reward, 'total output > block rwd + fees'); - // validate BIP-141 segwit output + // Validate BIP-141 segwit output if block_height >= BIP_141_BLOCK_HEIGHT { if *tx.is_segwit { - // calculate expected wtxid commitment and validate segwit output + // Calculate expected wtxid commitment and validate segwit output validate_coinbase_outputs(*tx.outputs, calculate_wtxid_commitment(wtxid_root))?; } } @@ -46,7 +45,7 @@ pub fn validate_coinbase( Result::Ok(()) } -/// Validates first and the only coinbase input +/// Validates the first and only coinbase input. fn validate_coinbase_input(input: @TxIn, block_height: u32) -> Result<(), ByteArray> { // Ensure the input's vout is 0xFFFFFFFF if *input.previous_output.vout != 0xFFFFFFFF { @@ -71,7 +70,7 @@ fn validate_coinbase_input(input: @TxIn, block_height: u32) -> Result<(), ByteAr Result::Ok(()) } -/// Validate coinbase sig script (BIP-34) +/// Validates coinbase sig script (BIP-34). fn validate_coinbase_sig_script(script: @ByteArray, block_height: u32) -> Result<(), ByteArray> { let script_len = script.len(); @@ -97,7 +96,7 @@ fn validate_coinbase_sig_script(script: @ByteArray, block_height: u32) -> Result Result::Ok(()) } -/// Validate coinbase witness +/// Validates coinbase witness. fn validate_coinbase_witness(witness: Span) -> Result<(), ByteArray> { if witness.len() != 1 { return Result::Err("Expected single witness item"); @@ -111,7 +110,7 @@ fn validate_coinbase_witness(witness: Span) -> Result<(), ByteArray> Result::Ok(()) } -/// Return BTC reward in SATS +/// Returns BTC reward in SATS. fn compute_block_reward(block_height: u32) -> u64 { let mut result: u64 = 5_000_000_000; @@ -122,29 +121,29 @@ fn compute_block_reward(block_height: u32) -> u64 { result } -/// Calculate wtxid commitment +/// Calculates wtxid commitment. fn calculate_wtxid_commitment(wtxid_root: Digest) -> Digest { - // construct witness reserved value + // Construct witness reserved value // 0000000000000000000000000000000000000000000000000000000000000000 let witness_value_bytes: ByteArray = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - // convert wtxid_root to ByteArray + // Convert wtxid_root to ByteArray let wtxid_root_bytes: ByteArray = wtxid_root.into(); - // concat (witness root hash | witness reserved value) + // Concat (witness root hash | witness reserved value) let res = ByteArrayTrait::concat(@wtxid_root_bytes, @witness_value_bytes); double_sha256_byte_array(@res) } -/// validate segwit output (BIP-141) +/// validates segwit output (BIP-141). fn validate_coinbase_outputs( mut outputs: Span, wtxid_commitment: Digest ) -> Result<(), ByteArray> { let mut is_wtxid_commitment_present: bool = false; - // construct expected witness script combining prefix and wtxid commitment + // Construct expected witness script combining prefix and wtxid commitment let mut expected_witness_script: ByteArray = ""; expected_witness_script.append_word(WTNS_PK_SCRIPT_PREFIX, 6); expected_witness_script.append(@wtxid_commitment.into()); @@ -152,15 +151,15 @@ fn validate_coinbase_outputs( while let Option::Some(output) = outputs.pop_back() { let pk_script = *output.pk_script; - // check for pk_script with at least 38 bytes commitment length + // Check for pk_script with at least 38 bytes commitment length if pk_script.len() >= WTNS_PK_SCRIPT_LEN { - // extract witness script containing wtxid commitment + // Extract witness script containing wtxid commitment let mut extracted_witness_script: ByteArray = ""; for i in 0..WTNS_PK_SCRIPT_LEN { extracted_witness_script.append_byte(pk_script[i]); }; - // compare expected and extracted witness script + // Compare expected and extracted witness script if expected_witness_script == extracted_witness_script { is_wtxid_commitment_present = true; break; @@ -175,13 +174,13 @@ fn validate_coinbase_outputs( Result::Ok(()) } -/// (BIP-30) Skip coinbase tx for duplicated txids -/// Only the first tx is valid, the duplicated tx is ignored +/// (BIP-30) Skip coinbase tx for duplicated txids. +/// Only the first tx is valid, the duplicated tx is ignored. /// /// First txid e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468 -/// at blocks 91722 and 91880 +/// at blocks 91722 and 91880. /// Second txid d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599 -/// at blocks 91812 and 91842 +/// at blocks 91812 and 91842. pub fn is_coinbase_txid_duplicated(txid: Digest, block_height: u32) -> bool { // TODO: allow duplicate transactions in case the previous instance of the transaction had no // spendable outputs left. @@ -189,6 +188,7 @@ pub fn is_coinbase_txid_duplicated(txid: Digest, block_height: u32) -> bool { || (txid.into() == SECOND_DUP_TXID && block_height == 91842)) { return true; } + false } @@ -222,7 +222,8 @@ mod tests { fn test_compute_block_reward() { let max_halvings: u32 = 64; let reward_initial: u256 = 5000000000; - let mut block_height: u32 = 210_000; // halving every 210 000 blocks + let mut block_height: u32 = 210_000; // Halving every 210 000 blocks + // Before first halving let genesis_halving_reward = compute_block_reward(0); assert_eq!(genesis_halving_reward, reward_initial.try_into().unwrap()); @@ -260,7 +261,6 @@ mod tests { assert_eq!(last_reward, 0); } - #[test] fn test_validate_coinbase_with_multiple_input() { let tx = Transaction { @@ -322,6 +322,7 @@ mod tests { }, witness: array![].span(), }; + validate_coinbase_input(@input, 1).unwrap_err(); } @@ -340,6 +341,7 @@ mod tests { }, witness: array![].span(), }; + validate_coinbase_input(@input, 1).unwrap_err(); } diff --git a/packages/consensus/src/validation/difficulty.cairo b/packages/consensus/src/validation/difficulty.cairo index 37bd251a..85fe48e4 100644 --- a/packages/consensus/src/validation/difficulty.cairo +++ b/packages/consensus/src/validation/difficulty.cairo @@ -17,7 +17,7 @@ const MIN_EPOCH_TIMESPAN: u32 = 302400; /// EXPECTED_EPOCH_TIMESPAN * 4 const MAX_EPOCH_TIMESPAN: u32 = 4838400; -/// Check if the given bits match the target difficulty. +/// Checks if the given bits match the target difficulty. pub fn validate_bits(target: u256, bits: u32) -> Result<(), ByteArray> { if bits_to_target(bits)? == target { Result::Ok(()) @@ -32,7 +32,6 @@ pub fn validate_bits(target: u256, bits: u32) -> Result<(), ByteArray> { /// Actual block time is needed to calculate the new epoch start time. /// Returns new difficulty target and new epoch start time. pub fn adjust_difficulty( - /// TODO: Split this function into smaller functions current_target: u256, epoch_start_time: u32, block_height: u32, @@ -74,6 +73,7 @@ fn reduce_target_precision(target: u256) -> u256 { num /= 256; size += 1; }; + // Extract 3 most significant bytes and round down if size > 2 { let factor = fast_pow(256, size - 3); @@ -89,7 +89,7 @@ fn reduce_target_precision(target: u256) -> u256 { } } -/// Converts difficulty target the compact form (bits) to a big integer. +/// Converts the difficulty target compact form (bits) to a big integer. fn bits_to_target(bits: u32) -> Result { // Extract exponent and mantissa let (exponent, mantissa) = core::traits::DivRem::div_rem(bits, 0x1000000); @@ -135,11 +135,11 @@ fn bits_to_target(bits: u32) -> Result { u256 { low: (mantissa.into() * 0x100000000000000000000000000), high: 0 } ); }, - // here we don't know + // Here we don't know 17 => { return Result::Ok(mantissa.into() * 0x10000000000000000000000000000); }, 18 => { return Result::Ok(mantissa.into() * 0x1000000000000000000000000000000); }, 19 => { return Result::Ok(mantissa.into() * 0x100000000000000000000000000000000); }, - // here it's only a high + // Here it's only a high 20 => { return Result::Ok(u256 { low: 0, high: mantissa.into() * 0x100 }); }, 21 => { return Result::Ok(u256 { low: 0, high: mantissa.into() * 0x10000 }); }, 22 => { return Result::Ok(u256 { low: 0, high: mantissa.into() * 0x1000000 }); }, @@ -151,11 +151,11 @@ fn bits_to_target(bits: u32) -> Result { 28 => { return Result::Ok(u256 { low: 0, high: mantissa.into() * 0x1000000000000000000 }); }, - // because 0x7FFFFF * 2**(8 * (28 - 3)) < MAX_TARGET, for these two elements we have to + // Because 0x7FFFFF * 2**(8 * (28 - 3)) < MAX_TARGET, for these two elements we have to // check the target 29 => u256 { low: 0, high: mantissa.into() * 0x100000000000000000000 }, 30 => u256 { low: 0, high: mantissa.into() * 0x10000000000000000000000 }, - // because 2^(8 * (31 - 3)) > MAX_TARGET + // Because 2^(8 * (31 - 3)) > MAX_TARGET 31 => { return Result::Err("Target exceeds maximum value"); }, 32 => { return Result::Err("Target exceeds maximum value"); }, _ => { return Result::Err("Target size cannot exceed 32 bytes"); }, @@ -174,13 +174,13 @@ mod tests { #[test] fn test_adjust_difficulty_block_2016_no_retargeting() { - // chainstate before block 2016 + // Chainstate before block 2016 let current_target: u256 = 0x00000000ffff0000000000000000000000000000000000000000000000000000_u256; let epoch_start_time: u32 = 1231006505; let prev_block_time: u32 = 1233061996; - // block 2016 + // Block 2016 let block_time: u32 = 1233063531; let block_height: u32 = 2016; @@ -196,13 +196,13 @@ mod tests { #[test] fn test_adjust_difficulty_block_2017_no_retargeting_no_new_epoch() { - // chainstate before block 2017 + // Chainstate before block 2017 let current_target: u256 = 0x00000000ffff0000000000000000000000000000000000000000000000000000_u256; let epoch_start_time: u32 = 1233063531; let prev_block_time: u32 = 1233063531; - // block 2017 + // Block 2017 let block_time: u32 = 1233064909; let block_height: u32 = 2017; @@ -218,13 +218,13 @@ mod tests { #[test] fn test_adjust_difficulty_block_32256_decrease() { - // chainstate before block 32256 + // Chainstate before block 32256 let current_target: u256 = 0x00000000ffff0000000000000000000000000000000000000000000000000000_u256; let epoch_start_time: u32 = 1261130161; let prev_block_time: u32 = 1262152739; - // block 32256 + // Block 32256 let block_time: u32 = 1262153464; let block_height: u32 = 32256; @@ -240,13 +240,13 @@ mod tests { #[test] fn test_adjust_difficulty_block_56448_increase() { - // chainstate before block 56448 + // Chainstate before block 56448 let current_target: u256 = 0x0000000013ec5300000000000000000000000000000000000000000000000000_u256; let epoch_start_time: u32 = 1272966376; let prev_block_time: u32 = 1274278387; - // block 56448 + // Block 56448 let block_time: u32 = 1274278435; let block_height: u32 = 56448; diff --git a/packages/consensus/src/validation/locktime.cairo b/packages/consensus/src/validation/locktime.cairo index f41edcb6..eb2fecfa 100644 --- a/packages/consensus/src/validation/locktime.cairo +++ b/packages/consensus/src/validation/locktime.cairo @@ -6,26 +6,26 @@ use crate::types::transaction::TxIn; /// fully disables the locktime feature. const SEQUENCE_FINAL: u32 = 0xffffffff; -/// If this bit is set the relative locktime is disabled +/// If this bit is set, the relative locktime is disabled. const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 0x80000000; -/// If this bit is set the relative locktime is time-based, -/// otherwise block-based +/// If this bit is set, the relative locktime is time-based, +/// otherwise block-based. const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 0x00400000; -/// Relative locktime value is stored in the lowest two bytes +/// Relative locktime value is stored in the lowest two bytes. const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000ffff; /// Threshold for lock_time: below this value it is interpreted as block number, /// otherwise as UNIX timestamp. -const LOCKTIME_THRESHOLD: u32 = 500000000; // Tue Nov 5 00:53:20 1985 UTC +const LOCKTIME_THRESHOLD: u32 = 500000000; // Tue Nov 5 00:53:20 1985 UTC -/// Checks if the transaction input is final given its sequence +/// Checks if the transaction input is final given its sequence. pub fn is_input_final(sequence: u32) -> bool { return sequence == SEQUENCE_FINAL; } -/// Validate transaction absolute locktime given that it is enabled. +/// Validates transaction absolute locktime given that it is enabled. /// /// https://learnmeabitcoin.com/technical/transaction/locktime/ pub fn validate_absolute_locktime( @@ -56,7 +56,7 @@ pub fn validate_absolute_locktime( } } -/// Validate a transaction input relative locktime given that the input is not final. +/// Validates a transaction input relative locktime given that the input is not final. /// /// If relative locktime is enabled, ensure the input's locktime is respected. /// https://learnmeabitcoin.com/technical/transaction/input/sequence/ @@ -109,10 +109,8 @@ pub fn validate_relative_locktime( #[cfg(test)] mod tests { use crate::types::transaction::{TxIn, OutPoint, TxOut}; - use utils::hex::{from_hex, hex_to_hash_rev}; use super::{validate_absolute_locktime, validate_relative_locktime}; - - // TODO: tests for invalid relative locktime + use utils::hex::{from_hex, hex_to_hash_rev}; #[test] fn test_relative_locktime_disabled() { @@ -138,7 +136,7 @@ mod tests { fn test_relative_locktime_enabled_block_height() { // txid 62fb5ecd3f022a2f09b73723b56410db0545923516b611013aed5218e4979322 // input 0 - // note: only relevant fields are initialized + // Note: only relevant fields are initialized let input = TxIn { script: @from_hex(""), sequence: 144, @@ -161,7 +159,7 @@ mod tests { fn test_relative_locktime_enabled_block_time() { // txid 12fa403cb22bf08c4c5542cc00673495a0c54c9cc8181bea850a12d40d7593a2 // input 0 - // note: only relevant fields are initialized + // Note: only relevant fields are initialized let input = TxIn { script: @from_hex(""), sequence: 4194311, @@ -209,7 +207,7 @@ mod tests { fn test_relative_locktime_block_time_lt_relative_locktime() { // txid 12fa403cb22bf08c4c5542cc00673495a0c54c9cc8181bea850a12d40d7593a2 // input 0 - // note: only relevant fields are initialized + // Note: only relevant fields are initialized let input = TxIn { script: @from_hex(""), sequence: 4194311, // Time-based relative locktime (0x400007 = SEQUENCE_LOCKTIME_TYPE_FLAG + value) diff --git a/packages/consensus/src/validation/script.cairo b/packages/consensus/src/validation/script.cairo index 9b1b0f33..e8df49ed 100644 --- a/packages/consensus/src/validation/script.cairo +++ b/packages/consensus/src/validation/script.cairo @@ -1,3 +1,7 @@ +//! Shinigami Bitcoin Script VM integration helpers. + +use crate::types::transaction::{Transaction, TxIn, TxOut}; +use crate::types::block::Header; use shinigami_engine::engine::EngineTrait; use shinigami_engine::engine::EngineImpl; use shinigami_engine::hash_cache::HashCacheImpl; @@ -5,8 +9,6 @@ use shinigami_engine::flags::ScriptFlags; use shinigami_engine::transaction::{ EngineTransactionInputTrait, EngineTransactionOutputTrait, EngineTransactionTrait }; -use crate::types::transaction::{Transaction, TxIn, TxOut}; -use crate::types::block::Header; const BIP_16_BLOCK_HEIGHT: u32 = 173805; // Pay-to-Script-Hash (P2SH) const BIP_66_BLOCK_HEIGHT: u32 = 363725; // DER Signatures @@ -20,6 +22,7 @@ const POW_2_64: u128 = 0x10000000000000000; const POW_2_96: u128 = 0x1000000000000000000000000; impl EngineTransactionInputImpl of EngineTransactionInputTrait { + /// Returns the txid of the previous output that is being spent by a given input. fn get_prevout_txid(self: @TxIn) -> u256 { // TODO: hash type in Shinigami let [a, b, c, d, e, f, g, h] = *self.previous_output.txid.value; @@ -30,51 +33,62 @@ impl EngineTransactionInputImpl of EngineTransactionInputTrait { u256 { low, high } } + /// Returns the vout of the previous output that is being spent by a given input. fn get_prevout_vout(self: @TxIn) -> u32 { *self.previous_output.vout } + /// Returns the signature script of a given input. fn get_signature_script(self: @TxIn) -> @ByteArray { *self.script } + /// Returns the witness of the given input. fn get_witness(self: @TxIn) -> Span { *self.witness } + /// Returns the locktime feature sequence of the given input. fn get_sequence(self: @TxIn) -> u32 { *self.sequence } } impl EngineTransactionOutputDummyImpl of EngineTransactionOutputTrait { + /// Returns the spending script of the given output. fn get_publickey_script(self: @TxOut) -> @ByteArray { *self.pk_script } + /// Returns the value in satoshis of the given output. fn get_value(self: @TxOut) -> i64 { Into::::into(*self.value).try_into().unwrap() } } impl EngineTransactionDummyImpl of EngineTransactionTrait { + /// Returns the version of the given transaction. fn get_version(self: @Transaction) -> i32 { Into::::into(*self.version).try_into().unwrap() } + /// Returns the inputs of the given transaction. fn get_transaction_inputs(self: @Transaction) -> Span { *self.inputs } + /// Returns the outputs of the given transaction. fn get_transaction_outputs(self: @Transaction) -> Span { *self.outputs } + /// Returns the locktime (block height or time) of the given transaction. fn get_locktime(self: @Transaction) -> u32 { *self.lock_time } } +/// Computes the script flags for a given transaction. fn script_flags(header: @Header, tx: @Transaction) -> u32 { let mut script_flags = 0_u32; let block_height = tx.inputs[0].previous_output.block_height; @@ -119,6 +133,7 @@ fn script_flags(header: @Header, tx: @Transaction) -> u32 { script_flags } +/// Converts a `felt252` short string into a `ByteArray`. fn parse_short_string(short: felt252) -> ByteArray { let mut f: u256 = short.into(); let mut l = 0; @@ -131,7 +146,7 @@ fn parse_short_string(short: felt252) -> ByteArray { parsed } -/// Validates script for a given input +/// Validates script for a given input. fn validate_script(header: @Header, tx: @Transaction, tx_idx: u32) -> Result<(), ByteArray> { let cache = HashCacheImpl::new(tx); let mut result: Option = Option::None; @@ -172,6 +187,7 @@ fn validate_script(header: @Header, tx: @Transaction, tx_idx: u32) -> Result<(), } } +/// Validates scripts for one or multiple transactions. pub fn validate_scripts(header: @Header, txs: Span) -> Result<(), ByteArray> { let mut r = Result::Ok(()); let mut i = 1; @@ -182,5 +198,6 @@ pub fn validate_scripts(header: @Header, txs: Span) -> Result<(), B } i += 1; }; + r } diff --git a/packages/consensus/src/validation/timestamp.cairo b/packages/consensus/src/validation/timestamp.cairo index f77bfeb5..4657d57f 100644 --- a/packages/consensus/src/validation/timestamp.cairo +++ b/packages/consensus/src/validation/timestamp.cairo @@ -2,7 +2,7 @@ //! //! Read more: https://learnmeabitcoin.com/technical/block/time/ -/// Compute the Median Time Past (MTP) from the previous timestamps. +/// Computes the Median Time Past (MTP) from the previous timestamps. pub fn compute_median_time_past(prev_timestamps: Span) -> u32 { // Sort the last 11 timestamps // adapted from : @@ -40,24 +40,26 @@ pub fn compute_median_time_past(prev_timestamps: Span) -> u32 { *sorted_prev_timestamps.at(sorted_prev_timestamps.len() / 2) } -/// Check that the block time is greater than the Median Time Past (MTP). +/// Checks that the block time is greater than the Median Time Past (MTP). pub fn validate_timestamp(median_time_past: u32, block_time: u32) -> Result<(), ByteArray> { if block_time > median_time_past { Result::Ok(()) } else { Result::Err( - format!("Median time past: {} >= block's timestamp: {}.", median_time_past, block_time) + format!("Median time past: {} >= block's timestamp: {}", median_time_past, block_time) ) } } -/// Update the list of the recent timestamps, removing the oldest and appending the most recent one. +/// Updates the list of the recent timestamps, removing the oldest and appending the most recent +/// one. pub fn next_prev_timestamps(prev_timestamps: Span, block_time: u32) -> Span { let mut timestamps: Array = prev_timestamps.into(); if timestamps.len() == 11 { timestamps.pop_front().unwrap(); // remove the oldest timestamp (not necessarily the min) } timestamps.append(block_time); // append the most recent timestamp (not necessarily the max) + timestamps.span() } @@ -81,16 +83,16 @@ mod tests { let mtp = 6_u32; let mut block_time = 7_u32; - // new timestamp is greater than MTP + // New timestamp is greater than MTP let result = validate_timestamp(mtp, block_time); assert(result.is_ok(), 'Expected timestamp to be valid'); - // new timestamp is equal to MTP + // New timestamp is equal to MTP block_time = 6; let result = validate_timestamp(mtp, block_time); assert!(result.is_err(), "MTP is greater than or equal to block's timestamp"); - // new timestamp is less than MTP + // New timestamp is less than MTP block_time = 5; let result = validate_timestamp(mtp, block_time); assert!(result.is_err(), "MTP is greater than block's timestamp"); @@ -112,16 +114,16 @@ mod tests { let mtp = compute_median_time_past(prev_timestamps); let mut block_time = 12_u32; - // new timestamp is greater than MTP + // New timestamp is greater than MTP let result = validate_timestamp(mtp, block_time); assert(result.is_ok(), 'Expected timestamp to be valid'); - // new timestamp is equal to MTP + // New timestamp is equal to MTP block_time = 6; let result = validate_timestamp(mtp, block_time); assert!(result.is_err(), "MTP is greater than or equal to block's timestamp"); - // new timestamp is less than MTP + // New timestamp is less than MTP block_time = 5; let result = validate_timestamp(mtp, block_time); assert!(result.is_err(), "MTP is greater than block's timestamp"); diff --git a/packages/consensus/src/validation/transaction.cairo b/packages/consensus/src/validation/transaction.cairo index 3d5a032c..a88ea630 100644 --- a/packages/consensus/src/validation/transaction.cairo +++ b/packages/consensus/src/validation/transaction.cairo @@ -10,7 +10,7 @@ use utils::hash::Digest; const OP_RETURN: u8 = 0x6a; const MAX_SCRIPT_SIZE: u32 = 10000; -/// Validate transaction and return transaction fee. +/// Validates a transaction and returns the transaction fee. /// /// This does not include script checks and outpoint inclusion verification. pub fn validate_transaction( @@ -39,7 +39,7 @@ pub fn validate_transaction( for input in *tx .inputs { - // Ensures that the output is not yet spent and spends it + // Ensure that the output is not yet spent and spends it inner_result = utxo_set.spend(input.previous_output); if inner_result.is_err() { break; @@ -79,8 +79,8 @@ pub fn validate_transaction( let mut vout = 0; for output in *tx .outputs { - // Adds outpoint to the cache if the corresponding transaction output will be used - // as a transaction input in the same block(s), or adds it to the utreexo otherwise. + // Add outpoint to the cache if the corresponding transaction output will be used + // as a transaction input in the same block(s), or add it to the utreexo otherwise let outpoint = OutPoint { txid, vout, data: *output, block_height, median_time_past, is_coinbase: false, }; @@ -95,10 +95,10 @@ pub fn validate_transaction( }; inner_result?; - return compute_transaction_fee(total_input_amount, total_output_amount); + compute_transaction_fee(total_input_amount, total_output_amount) } -/// Ensure transaction fee is not negative. +/// Computes the transaction fee and ensures transaction fee is not negative. fn compute_transaction_fee( total_input_amount: u64, total_output_amount: u64 ) -> Result { @@ -107,10 +107,11 @@ fn compute_transaction_fee( format!("Negative fee (output {total_output_amount} > input {total_input_amount})") ); } - return Result::Ok(total_input_amount - total_output_amount); + + Result::Ok(total_input_amount - total_output_amount) } -/// Ensure than coinbase output is old enough to be spent. +/// Ensures than coinbase output is old enough to be spent. fn validate_coinbase_maturity(output_height: u32, block_height: u32) -> Result<(), ByteArray> { if block_height < output_height + 100 { return Result::Err( @@ -138,10 +139,10 @@ pub fn is_pubscript_unspendable(pubscript: @ByteArray) -> bool { mod tests { use core::dict::Felt252Dict; use crate::codec::Encode; - use crate::types::transaction::{Transaction, TxIn, TxOut, OutPoint, OutPointTrait}; + use crate::types::transaction::{Transaction, TxIn, TxOut, OutPoint, OutPointHashTrait}; use crate::types::utxo_set::{UtxoSet, TX_OUTPUT_STATUS_UNSPENT}; - use utils::{hex::{from_hex, hex_to_hash_rev}, double_sha256::double_sha256_byte_array}; use super::{validate_transaction, is_pubscript_unspendable, MAX_SCRIPT_SIZE}; + use utils::{hex::{from_hex, hex_to_hash_rev}, double_sha256::double_sha256_byte_array}; #[test] fn test_tx_fee() { @@ -215,8 +216,7 @@ mod tests { let mut utxo_set: UtxoSet = Default::default(); let result = validate_transaction(@tx, 0, 0, 0, txid, ref utxo_set); - assert!(result.is_err()); - // assert_eq!(result.unwrap_err(), "transaction inputs are empty"); + assert_eq!(result.unwrap_err(), "transaction inputs are empty"); } #[test] @@ -251,8 +251,7 @@ mod tests { let mut utxo_set: UtxoSet = Default::default(); let result = validate_transaction(@tx, 0, 0, 0, txid, ref utxo_set); - assert!(result.is_err()); - // assert_eq!(result.unwrap_err(), "transaction outputs are empty"); + assert_eq!(result.unwrap_err(), "transaction outputs are empty"); } #[test] @@ -263,7 +262,7 @@ mod tests { inputs: array![ TxIn { script: @from_hex(""), - sequence: 0xfffffffe, // absolute locktime + sequence: 0xfffffffe, // Absolute locktime previous_output: OutPoint { txid: hex_to_hash_rev( "0000000000000000000000000000000000000000000000000000000000000000" @@ -295,7 +294,6 @@ mod tests { // Transaction should be invalid when current block height is less than locktime let result = validate_transaction(@tx, 500000, 0, 0, txid, ref utxo_set); - assert!(result.is_err()); assert_eq!( result.unwrap_err().into(), "Transaction locktime 500000 is not lesser than current block height 500000" @@ -349,7 +347,6 @@ mod tests { // Transaction should be invalid when current block time is not greater than locktime let result = validate_transaction(@tx, 0, 1600000000, 1600000000, txid, ref utxo_set); - assert!(result.is_err()); assert_eq!( result.unwrap_err().into(), "Transaction locktime 1600000000 is not lesser than current block time 1600000000" @@ -370,7 +367,7 @@ mod tests { inputs: array![ TxIn { script: @from_hex(""), - sequence: 0xffffffff, // final + sequence: 0xffffffff, // Final previous_output: OutPoint { txid: hex_to_hash_rev( "0000000000000000000000000000000000000000000000000000000000000000" @@ -419,7 +416,7 @@ mod tests { inputs: array![ TxIn { script: @from_hex(""), - sequence: 0xffffffff, // final + sequence: 0xffffffff, // Final previous_output: OutPoint { txid: hex_to_hash_rev( "0000000000000000000000000000000000000000000000000000000000000000" @@ -738,7 +735,6 @@ mod tests { let mut utxo_set = UtxoSet { cache, ..Default::default() }; let result = validate_transaction(@tx, block_height, 0, 0, txid, ref utxo_set); - assert!(result.is_err()); assert_eq!(result.unwrap_err(), "The output has already been added"); } @@ -802,7 +798,6 @@ mod tests { let mut utxo_set = UtxoSet { cache, ..Default::default() }; let result = validate_transaction(@tx, block_height, 0, 0, txid, ref utxo_set); - assert!(result.is_err()); assert_eq!(result.unwrap_err(), "The output has already been spent"); } @@ -862,7 +857,6 @@ mod tests { let mut utxo_set: UtxoSet = Default::default(); let result = validate_transaction(@tx, block_height, 0, 0, txid, ref utxo_set); - assert!(result.is_err()); assert_eq!(result.unwrap_err(), "The output has already been spent"); } diff --git a/packages/consensus/src/validation/work.cairo b/packages/consensus/src/validation/work.cairo index 3b41f909..0d2c32ba 100644 --- a/packages/consensus/src/validation/work.cairo +++ b/packages/consensus/src/validation/work.cairo @@ -2,14 +2,14 @@ use utils::hash::Digest; -/// Check if the work done (by calculating the block hash) satisfies the difficulty target. +/// Checks if the work done (by calculating the block hash) satisfies the difficulty target. pub fn validate_proof_of_work(target: u256, block_hash: Digest) -> Result<(), ByteArray> { if block_hash.into() <= target { Result::Ok(()) } else { Result::Err( format!( - "Insufficient proof of work. Expected block hash {:?} to be less than or equal to {}.", + "Insufficient proof of work. Expected block hash {:?} to be less than or equal to {}", block_hash, target ) @@ -17,11 +17,12 @@ pub fn validate_proof_of_work(target: u256, block_hash: Digest) -> Result<(), By } } -/// Compute total chain work based on the previous value and current difficulty target. +/// Computes total chain work based on the previous value and current difficulty target. pub fn compute_total_work(current_total_work: u256, target: u256) -> u256 { current_total_work + compute_work_from_target(target) } +/// Computes the chain work given a target. // Need to compute 2**256 / (target+1), but we can't represent 2**256 // as it's too large for an u256. However, as 2**256 is at least as large // as target+1, it is equal to ((2**256 - target - 1) / (target+1)) + 1, @@ -36,23 +37,23 @@ mod tests { #[test] fn test_validate_proof_of_work() { - // target is less than prev block hash + // Target is less than prev block hash let result = validate_proof_of_work(0, 1_u256.into()); assert!(result.is_err(), "Expect target less than prev block hash"); - // target is greater than prev block hash + // Target is greater than prev block hash let result = validate_proof_of_work(2, 1_u256.into()); assert!(result.is_ok(), "Expect target gt prev block hash"); - // target is equal to prev block hash + // Target is equal to prev block hash let result = validate_proof_of_work(1, 1_u256.into()); assert!(result.is_ok(), "Expect target equal to prev block hash"); - // block prev block hash is greater than target + // Block prev block hash is greater than target let result = validate_proof_of_work(1, 2_u256.into()); assert!(result.is_err(), "Expect prev block hash gt target"); - // block prev block hash is less than target + // Block prev block hash is less than target let result = validate_proof_of_work( 0x00000000ffff0000000000000000000000000000000000000000000000000000_u256, 0x000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55_u256.into() diff --git a/packages/utils/src/bit_shifts.cairo b/packages/utils/src/bit_shifts.cairo index 663fc55d..5d801947 100644 --- a/packages/utils/src/bit_shifts.cairo +++ b/packages/utils/src/bit_shifts.cairo @@ -1,12 +1,12 @@ -//! Bit shifts. +//! Bit shifts and pow helpers. use core::num::traits::{Zero, One}; -/// Performs a bitwise right shift on a u64 value by a specified number of bits. -/// This specialized version offers optimal performance for u64 types. +/// Performs a bitwise right shift on a `u64` value by a specified number of bits. +/// This specialized version offers optimal performance for `u64` types. /// /// # Arguments -/// * `self` - The u64 value to be shifted +/// * `self` - The `u64` value to be shifted /// * `shift` - The number of bits to shift right /// /// # Returns @@ -19,8 +19,7 @@ pub fn shr_u64(self: u64, shift: u32) -> u64 { self / pow2(shift) } - -// Fast exponentiation using the square-and-multiply algorithm +// Fast exponentiation using the square-and-multiply algorithm. // Reference: // https://github.com/keep-starknet-strange/alexandria/blob/bcdca70afdf59c9976148e95cebad5cf63d75a7f/packages/math/src/fast_power.cairo#L12 pub fn fast_pow< @@ -64,14 +63,15 @@ pub fn fast_pow< } } - -/// Fast power of 2 using lookup tables +/// Fast power of 2 using lookup tables. /// Reference: https://github.com/keep-starknet-strange/alexandria/pull/336 /// /// # Arguments /// * `exponent` - The exponent to raise 2 to +/// /// # Returns /// * `u64` - The result of 2^exponent +/// /// # Panics /// * If `exponent` is greater than 63 (out of the supported range) pub fn pow2(exponent: u32) -> u64 { diff --git a/packages/utils/src/bytearray.cairo b/packages/utils/src/bytearray.cairo index 929dc5db..462d9baa 100644 --- a/packages/utils/src/bytearray.cairo +++ b/packages/utils/src/bytearray.cairo @@ -1,7 +1,8 @@ -//! ByteArray extensions. +//! `ByteArray` helpers. use core::hash::{Hash, HashStateTrait}; +/// `Serde` trait implementation for `ByteArray`. pub impl ByteArraySnapSerde of Serde<@ByteArray> { fn serialize(self: @@ByteArray, ref output: Array) { (*self).serialize(ref output); @@ -15,6 +16,7 @@ pub impl ByteArraySnapSerde of Serde<@ByteArray> { } } +/// `Hash` trait implementation for `ByteArray`. pub impl ByteArraySnapHash, +Drop> of Hash<@ByteArray, S> { fn update_state(mut state: S, value: @ByteArray) -> S { let mut serialized_bytearray: Array = array![]; @@ -23,6 +25,7 @@ pub impl ByteArraySnapHash, +Drop> of Hash<@ByteArray, for felt in serialized_bytearray { state = state.update(felt); }; + state } } diff --git a/packages/utils/src/hash.cairo b/packages/utils/src/hash.cairo index 20e1b8dc..d38c7e19 100644 --- a/packages/utils/src/hash.cairo +++ b/packages/utils/src/hash.cairo @@ -1,10 +1,10 @@ -//! Digest digest struct and trait implementations. +//! `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::integer::u128_byte_reverse; use core::num::traits::zero::Zero; +use core::to_byte_array::AppendFormattedToByteArray; /// 256-bit hash digest. /// Represented as an array of 4-byte words. @@ -21,6 +21,7 @@ pub impl DigestImpl of DigestTrait { } } +/// `Zero` trait implementation for `Digest`. impl DigestZero of Zero { fn zero() -> Digest { Digest { value: [0_u32; 8] } @@ -35,7 +36,7 @@ impl DigestZero of Zero { } } -/// Formats a `Digest` value for display. +/// `Display` trait implementation for `Digest`. impl DigestDisplay of Display { fn fmt(self: @Digest, ref f: Formatter) -> Result<(), Error> { let hash: u256 = (*self).into(); @@ -44,14 +45,14 @@ impl DigestDisplay of Display { } } -/// Compares two `Digest` values for equality. +/// `PartialEq` trait implementation for `Digest`. impl DigestPartialEq of PartialEq { fn eq(lhs: @Digest, rhs: @Digest) -> bool { lhs.value == rhs.value } } -/// Converts a `Digest` value into a `ByteArray`. +/// `Into` implementation that converts a `Digest` value into a `ByteArray`. pub impl DigestIntoByteArray of Into { fn into(self: Digest) -> ByteArray { let mut bytes: ByteArray = Default::default(); @@ -62,15 +63,14 @@ pub impl DigestIntoByteArray of Into { } } - 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. +/// Converts a `u256` value into a `Digest` type and reverses 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); @@ -101,9 +101,8 @@ pub impl U256IntoDigest of Into { } } - -/// 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. +/// `Into` implementation that 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; @@ -115,7 +114,7 @@ pub impl DigestIntoU256 of Into { } } - +/// `Hash` trait implementation for `Digest`. pub impl DigestHash, +Drop> of Hash { fn update_state(state: S, value: Digest) -> S { let u256_digest: u256 = value.into(); @@ -181,7 +180,6 @@ mod tests { assert_eq!(result_u256, expected_u256, "invalid results"); } - #[test] fn test_hash_to_u256_to_hash() { let hash_value = Digest { diff --git a/packages/utils/src/hex.cairo b/packages/utils/src/hex.cairo index ef51e051..7d681a12 100644 --- a/packages/utils/src/hex.cairo +++ b/packages/utils/src/hex.cairo @@ -1,7 +1,8 @@ -//! Hex helpers +//! Hex helpers. + use crate::hash::Digest; -/// Get bytes from hex (base16) +/// Gets bytes from hex (base16). pub fn from_hex(hex_string: ByteArray) -> ByteArray { let num_characters = hex_string.len(); assert!(num_characters & 1 == 0, "Invalid hex string length"); @@ -19,7 +20,7 @@ pub fn from_hex(hex_string: ByteArray) -> ByteArray { bytes } -/// Convert bytes to hex (base16) +/// Converts bytes to hex (base16). pub fn to_hex(data: @ByteArray) -> ByteArray { let alphabet: @ByteArray = @"0123456789abcdef"; let mut result: ByteArray = Default::default(); @@ -32,10 +33,11 @@ pub fn to_hex(data: @ByteArray) -> ByteArray { result.append_byte(alphabet.at(r).unwrap()); i += 1; }; + result } -// Get `Digest` form `ByteArray` reversed +// Gets `Digest` from reversed `ByteArray`. pub fn hex_to_hash_rev(hex_string: ByteArray) -> Digest { let mut result: Array = array![]; let mut i = 0; @@ -52,6 +54,7 @@ pub fn hex_to_hash_rev(hex_string: ByteArray) -> Digest { i += 2; }; result.append(unit); + Digest { value: [ *result[0], @@ -84,8 +87,8 @@ 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::hash::Digest; + use super::{from_hex, to_hex, hex_to_hash_rev}; #[test] fn test_bytes_from_hex() { diff --git a/packages/utils/src/lib.cairo b/packages/utils/src/lib.cairo index e578f757..cce1dec4 100644 --- a/packages/utils/src/lib.cairo +++ b/packages/utils/src/lib.cairo @@ -6,9 +6,10 @@ pub mod merkle_tree; pub mod numeric; pub mod sort; + +// pub mod sha256; // Let's use core non provable functions for now. Much faster. -pub mod sha256; -// pub use core::sha256; +pub use core::sha256; #[cfg(target: 'test')] pub mod hex; diff --git a/packages/utils/src/merkle_tree.cairo b/packages/utils/src/merkle_tree.cairo index 1df5d1e5..4d4e84a8 100644 --- a/packages/utils/src/merkle_tree.cairo +++ b/packages/utils/src/merkle_tree.cairo @@ -2,7 +2,7 @@ use super::{double_sha256::double_sha256_parent, hash::Digest}; -/// Calculate Merkle tree root given the array of leaves. +/// Calculates Merkle tree root given the array of leaves. pub fn merkle_root(hashes: Span) -> Digest { let len = hashes.len(); diff --git a/packages/utils/src/numeric.cairo b/packages/utils/src/numeric.cairo index 815aa086..c52f4874 100644 --- a/packages/utils/src/numeric.cairo +++ b/packages/utils/src/numeric.cairo @@ -1,3 +1,5 @@ +//! Numeric helpers. + use crate::bit_shifts::shr_u64; /// Reverses the byte order of a `u32`. @@ -12,8 +14,8 @@ pub fn u32_byte_reverse(word: u32) -> u32 { return byte0 + byte1 + byte2 + byte3; } -/// Computes the next power of two of a u64 variable. -/// returns 2^x, where x is the smallest integer such that 2^x >= n. +/// Computes the next power of two of a `u64` variable. +/// Returns 2^x, where x is the smallest integer such that 2^x >= n. pub fn u64_next_power_of_two(mut n: u64) -> u64 { if n == 0 { return 1; diff --git a/packages/utils/src/sha256.cairo b/packages/utils/src/sha256.cairo index 85af97d6..eecaef43 100644 --- a/packages/utils/src/sha256.cairo +++ b/packages/utils/src/sha256.cairo @@ -50,7 +50,7 @@ pub fn compute_sha256_u32_array( /// 1. Append a single bit with value 1 to the end of the array. /// 2. Append zeros until the length of the array is 448 mod 512. /// 3. Append the length of the array in bits as a 64-bit number. -/// use last_input_word when the number of bytes in the last input word is less than 4. +/// Use last_input_word when the number of bytes in the last input word is less than 4. fn add_sha256_padding(ref arr: Array, last_input_word: u32, last_input_num_bytes: u32) { let len = arr.len(); if last_input_num_bytes == 0 { @@ -344,9 +344,9 @@ const k: [ #[cfg(test)] mod tests { - use super::compute_sha256_byte_array; use crate::hash::DigestTrait; use crate::hex::from_hex; + use super::compute_sha256_byte_array; #[test] fn test_cairo_sha256() { @@ -364,7 +364,7 @@ mod tests { digest.into() ); - // Following tests have been inspired by the test suite + // Following tests have been inspired by the following test suite: // https://github.com/SystemsCyber/CAN-Logger-3/blob/master/tests/sha256-test/sha256-test.ino let input: ByteArray = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; let digest = DigestTrait::new(compute_sha256_byte_array(@input)); diff --git a/packages/utils/src/sort.cairo b/packages/utils/src/sort.cairo index 063956aa..ad53f006 100644 --- a/packages/utils/src/sort.cairo +++ b/packages/utils/src/sort.cairo @@ -1,4 +1,6 @@ -/// Bubble sort from +//! Sorting helpers. + +/// Bubble sort from: /// https://github.com/keep-starknet-strange/alexandria/blob/main/packages/sorting/src/bubble_sort.cairo pub fn bubble_sort, +Drop, +PartialOrd>(mut array: Span) -> Array { if array.len() == 0 { @@ -35,5 +37,6 @@ pub fn bubble_sort, +Drop, +PartialOrd>(mut array: Span) -> } }; }; + sorted_array } diff --git a/packages/utreexo/src/lib.cairo b/packages/utreexo/src/lib.cairo index 402e16a5..052b7fb4 100644 --- a/packages/utreexo/src/lib.cairo +++ b/packages/utreexo/src/lib.cairo @@ -1,23 +1,23 @@ -pub mod vanilla { - pub mod state; - pub mod proof; +pub mod stump { pub mod accumulator; + pub mod proof; + pub mod state; #[cfg(test)] mod accumulator_tests; } -pub mod stump { - pub mod state; - pub mod proof; +pub mod vanilla { pub mod accumulator; + pub mod proof; + pub mod state; #[cfg(test)] mod accumulator_tests; } pub mod test; -use core::poseidon::PoseidonTrait; use core::hash::{HashStateTrait, HashStateExTrait}; +use core::poseidon::PoseidonTrait; -/// Parent hash of two Utreexo nodes +/// Parent hash of two Utreexo nodes. fn parent_hash(left: felt252, right: felt252) -> felt252 { return PoseidonTrait::new().update_with(left).update_with(right).finalize(); } diff --git a/packages/utreexo/src/stump/proof.cairo b/packages/utreexo/src/stump/proof.cairo index 832ceecc..346e4743 100644 --- a/packages/utreexo/src/stump/proof.cairo +++ b/packages/utreexo/src/stump/proof.cairo @@ -13,6 +13,7 @@ pub struct UtreexoBatchProof { pub targets: Span, } +/// `Display` implementation for `UtreexoBatchProof`. impl UtreexoBatchProofDisplay of Display { fn fmt(self: @UtreexoBatchProof, ref f: Formatter) -> Result<(), Error> { let mut targets: ByteArray = Default::default(); @@ -466,7 +467,7 @@ pub impl UtreexoBatchProofImpl of UtreexoBatchProofTrait { } /// Extracts all nodes with absolute positions in [row_start; row_end) -/// and transforms their positions to relative +/// and transforms their positions to relative. fn extract_row, +Drop>( ref nodes: Array<(u64, T)>, row_start: u64, row_end: u64 ) -> Array<(u64, T)> { @@ -482,7 +483,7 @@ fn extract_row, +Drop>( row } -/// Merges two sorted arrays into a single sorted array +/// Merges two sorted arrays into a single sorted array. fn merge_sorted>( ref arr1: Array<(u64, T)>, ref arr2: Array<(u64, T)> ) -> Array<(u64, T)> { @@ -504,7 +505,7 @@ fn merge_sorted>( } /// Takes two nodes containing two values each: (L1, L2) and (R1, R2), and calculates -/// a parent node, that also contains two values (P1 = h(L1, R1), P2 = h(L2, R2)) +/// a parent node, that also contains two values (P1 = h(L1, R1), P2 = h(L2, R2)). fn parent_hash_pair( left: (felt252, Option), right: (felt252, Option) ) -> (felt252, Option) { @@ -520,7 +521,7 @@ fn parent_hash_pair( (old_parent, new_parent) } -/// PartialOrd implementation for tuple (u64, T). +/// `PartialOrd` trait implementation for tuple (u64, T). impl PositionPartialOrd> of PartialOrd<(u64, T)> { fn lt(lhs: (u64, T), rhs: (u64, T)) -> bool { let (l, _) = lhs; diff --git a/packages/utreexo/src/stump/state.cairo b/packages/utreexo/src/stump/state.cairo index f9cef32c..96dde38d 100644 --- a/packages/utreexo/src/stump/state.cairo +++ b/packages/utreexo/src/stump/state.cairo @@ -12,12 +12,14 @@ pub struct UtreexoStumpState { pub num_leaves: u64, } +/// `Default` trait implementation for `UtreexoStumpState`. pub impl UtreexoStumpStateDefault of Default { fn default() -> UtreexoStumpState { UtreexoStumpState { roots: array![Option::None].span(), num_leaves: 0, } } } +/// `Display` trait implementation for `UtreexoStumpState`. impl UtreexoStumpStateDisplay of Display { fn fmt(self: @UtreexoStumpState, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!( diff --git a/packages/utreexo/src/test.cairo b/packages/utreexo/src/test.cairo index 24266f28..c43fe522 100644 --- a/packages/utreexo/src/test.cairo +++ b/packages/utreexo/src/test.cairo @@ -1,7 +1,7 @@ +use core::testing::get_available_gas; use crate::stump::state::UtreexoStumpState; use crate::stump::proof::UtreexoBatchProof; use crate::stump::accumulator::StumpUtreexoAccumulator; -use core::testing::get_available_gas; #[derive(Drop, Serde, Debug)] struct Args { diff --git a/packages/utreexo/src/vanilla/accumulator.cairo b/packages/utreexo/src/vanilla/accumulator.cairo index 04d7f19a..af07e9e2 100644 --- a/packages/utreexo/src/vanilla/accumulator.cairo +++ b/packages/utreexo/src/vanilla/accumulator.cairo @@ -26,7 +26,7 @@ pub impl VanillaUtreexoAccumulatorImpl of VanillaUtreexoAccumulator { } }; - // Checks if terminates with Option::None + // Check if terminates with `Option::None` if (new_roots[new_roots.len() - 1].is_some()) { new_roots.append(Option::None); } diff --git a/packages/utreexo/src/vanilla/accumulator_tests.cairo b/packages/utreexo/src/vanilla/accumulator_tests.cairo index 71818e4a..a6235cbb 100644 --- a/packages/utreexo/src/vanilla/accumulator_tests.cairo +++ b/packages/utreexo/src/vanilla/accumulator_tests.cairo @@ -2,7 +2,7 @@ use super::accumulator::VanillaUtreexoAccumulator; use super::proof::UtreexoProof; use super::state::UtreexoState; -/// Test the basic functionality of the Utreexo accumulator +/// Tests the basic functionality of the Utreexo accumulator. /// /// This test covers the following scenarios: /// 1. Adding a single leaf and verifying it @@ -18,6 +18,7 @@ use super::state::UtreexoState; /// /// The test uses predefined txid values (0x111111..., 0x222222..., etc.) for simplicity. /// It checks the correct root values at each stage of the Utreexo tree's growth. + #[test] fn test_verify_inclusion() { // Add the first leaf (0x111111111111111111111111) @@ -141,7 +142,7 @@ fn test_utreexo_add() { let mut utreexo_state: UtreexoState = Default::default(); let outpoint: felt252 = 0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C; - // add first leave to empty utreexo + // Add first leave to empty utreexo utreexo_state = utreexo_state.add(outpoint); let expected: Span> = array![ @@ -151,7 +152,7 @@ fn test_utreexo_add() { .span(); assert_eq!(utreexo_state.roots, expected, "cannot add first leave"); - // add second leave + // Add second leave utreexo_state = utreexo_state.add(outpoint); let expected: Span> = array![ @@ -162,7 +163,7 @@ fn test_utreexo_add() { .span(); assert_eq!(utreexo_state.roots, expected, "cannot add second leave"); - // add thirdth leave + // Add thirdth leave utreexo_state = utreexo_state.add(outpoint); let expected: Span> = array![ @@ -173,7 +174,7 @@ fn test_utreexo_add() { .span(); assert_eq!(utreexo_state.roots, expected, "cannot add thirdth leave"); - // add fourth leave + // Add fourth leave utreexo_state = utreexo_state.add(outpoint); let expected: Span> = array![ @@ -185,7 +186,7 @@ fn test_utreexo_add() { .span(); assert_eq!(utreexo_state.roots, expected, "cannot add fourth leave"); - // add fifth leave + // Add fifth leave utreexo_state = utreexo_state.add(outpoint); let expected: Span> = array![ @@ -197,7 +198,7 @@ fn test_utreexo_add() { .span(); assert_eq!(utreexo_state.roots, expected, "cannot add fifth leave"); - // add 3 leaves + // Add 3 leaves for _ in 1..4_u8 { utreexo_state = utreexo_state.add(outpoint); }; @@ -212,7 +213,7 @@ fn test_utreexo_add() { .span(); assert_eq!(utreexo_state.roots, expected, "cannot add 3 leaves"); - // add 22 leaves + // Add 22 leaves for _ in 1..23_u8 { utreexo_state = utreexo_state.add(outpoint); }; @@ -234,7 +235,7 @@ fn test_utreexo_delete() { let mut utreexo_state: UtreexoState = Default::default(); - // adds 16 leaves to empty utreexo + // Adds 16 leaves to empty utreexo utreexo_state = utreexo_state .add(0x111111111111111111111111) .add(0x222222222222222222222222) @@ -275,7 +276,7 @@ fn test_utreexo_delete() { .span() }; - // deletes the 3rd leaf + // Deletes the 3rd leaf utreexo_state = utreexo_state.delete(@proof_for_3rd_leaf); let expected: Span> = array![ @@ -297,7 +298,7 @@ fn test_utreexo_delete_2() { let mut utreexo_state: UtreexoState = Default::default(); - // adds 7 leaves to empty utreexo + // Adds 7 leaves to empty utreexo utreexo_state = utreexo_state .add(0x111111111111111111111111) .add(0x222222222222222222222222) @@ -318,7 +319,7 @@ fn test_utreexo_delete_2() { let proof: UtreexoProof = UtreexoProof { leaf_index: 6, proof: array![].span() }; - // deletes the last added leaf which corresponds to the root at h=0 + // Deletes the last added leaf which corresponds to the root at h=0 utreexo_state = utreexo_state.delete(@proof); let expected: Span> = array![ diff --git a/packages/utreexo/src/vanilla/proof.cairo b/packages/utreexo/src/vanilla/proof.cairo index 479925d1..4c439ec7 100644 --- a/packages/utreexo/src/vanilla/proof.cairo +++ b/packages/utreexo/src/vanilla/proof.cairo @@ -11,6 +11,7 @@ pub struct UtreexoProof { pub leaf_index: u64, } +/// `Display` trait implementation for `UtreexoProof`. impl UtreexoProofDisplay of Display { fn fmt(self: @UtreexoProof, ref f: Formatter) -> Result<(), Error> { let mut proofs: ByteArray = Default::default(); @@ -47,7 +48,7 @@ pub impl UtreexoProofImpl of UtreexoProofTrait { curr_node = parent_hash(left, right); node_index = next_node_index; }; - // Returns the computed root (or the node itself if the proof is empty). + // Return the computed root (or the node itself if the proof is empty). curr_node } } diff --git a/packages/utreexo/src/vanilla/state.cairo b/packages/utreexo/src/vanilla/state.cairo index 637442a5..e8067e2e 100644 --- a/packages/utreexo/src/vanilla/state.cairo +++ b/packages/utreexo/src/vanilla/state.cairo @@ -10,12 +10,14 @@ pub struct UtreexoState { pub roots: Span>, } +/// `Default` trait implement for `UtreexoState`. pub impl UtreexoStateDefault of Default { fn default() -> UtreexoState { UtreexoState { roots: array![Option::None].span() } } } +/// `Display` trait implement for `UtreexoState`. impl UtreexoStateDisplay of Display { fn fmt(self: @UtreexoState, ref f: Formatter) -> Result<(), Error> { let str: ByteArray = format!("UtreexoState {{ roots: {} }}", (*self.roots).len(),);