Conversation
0990433 to
b693be2
Compare
9ad9025 to
cfc92e0
Compare
crates/contracts/Cargo.toml
Outdated
| bytes32-tr-storage = [] | ||
| array-tr-storage = [] | ||
| smt-storage = [] | ||
| swap-with-change = [] |
d15e987 to
b574a43
Compare
73349f6 to
6ff66b5
Compare
6ff66b5 to
250295e
Compare
| /// Lock collateral on the storage contract (Mint/Fund operation) | ||
| GetStorageAddress { | ||
| /// Collateral and fee UTXO | ||
| #[arg(long = "collateral-utxo")] | ||
| collateral_utxo: OutPoint, | ||
| /// Collateral amount in satoshis (LBTC) | ||
| #[arg(long = "collateral-amount")] | ||
| collateral_amount: u64, | ||
| /// Miner fee in satoshis (LBTC) | ||
| #[arg(long = "fee-amount")] | ||
| fee_amount: u64, | ||
|
|
||
| /// The initial 32-byte data payload to store in the tree at the specified path | ||
| #[arg(long = "storage-bytes", value_parser = parse_hex_32)] | ||
| storage_bytes: [u8; 32], | ||
| /// The path in the Merkle Tree use for the contract logic (e.g., "rrll...") | ||
| #[arg(long = "path", value_parser = parse_bit_path)] | ||
| path: [bool; DEPTH], | ||
|
|
||
| /// Account that will pay for transaction fees and that owns a tokens to send | ||
| #[arg(long = "account-index", default_value_t = 0)] | ||
| account_index: u32, | ||
| /// When set, broadcast the built transaction via Esplora and print txid | ||
| #[arg(long = "broadcast")] | ||
| broadcast: bool, | ||
| }, |
There was a problem hiding this comment.
This command should not broadcast anything, just print the address to which user can send money
| /// Returns an error if: | ||
| /// - The UTXO asset or amount validation fails (expects explicit amounts). | ||
| /// - Transaction extraction or amount proof verification fails. | ||
| pub fn build_smt_mint( |
There was a problem hiding this comment.
| pub fn build_smt_mint( | |
| pub fn get_storage_address( |
| ) -> Result<PartiallySignedTransaction, TransactionBuildError> { | ||
| let (collateral_out_point, collateral_tx_out) = collateral_utxo; | ||
|
|
||
| // TODO Change this validation to another func |
There was a problem hiding this comment.
Move this todo where the validate_amount is defined
| let change_recipient_script = collateral_tx_out.script_pubkey.clone(); | ||
|
|
||
| let mut pst = PartiallySignedTransaction::new_v2(); | ||
|
|
||
| let mut collateral_input = Input::from_prevout(collateral_out_point); | ||
| collateral_input.witness_utxo = Some(collateral_tx_out.clone()); | ||
| pst.add_input(collateral_input); | ||
|
|
||
| pst.add_output(Output::new_explicit( | ||
| mint_script_pubkey, | ||
| collateral_amount, | ||
| collateral_asset_id, | ||
| None, | ||
| )); | ||
|
|
||
| if change_amount > 0 { | ||
| pst.add_output(Output::new_explicit( | ||
| change_recipient_script, | ||
| change_amount, | ||
| collateral_asset_id, | ||
| None, | ||
| )); | ||
| } | ||
|
|
||
| pst.add_output(Output::from_txout(TxOut::new_fee( | ||
| fee_amount, | ||
| collateral_asset_id, | ||
| ))); | ||
|
|
||
| pst.extract_tx()? | ||
| .verify_tx_amt_proofs(secp256k1::SECP256K1, &[collateral_tx_out])?; | ||
|
|
||
| Ok(pst) |
There was a problem hiding this comment.
| let change_recipient_script = collateral_tx_out.script_pubkey.clone(); | |
| let mut pst = PartiallySignedTransaction::new_v2(); | |
| let mut collateral_input = Input::from_prevout(collateral_out_point); | |
| collateral_input.witness_utxo = Some(collateral_tx_out.clone()); | |
| pst.add_input(collateral_input); | |
| pst.add_output(Output::new_explicit( | |
| mint_script_pubkey, | |
| collateral_amount, | |
| collateral_asset_id, | |
| None, | |
| )); | |
| if change_amount > 0 { | |
| pst.add_output(Output::new_explicit( | |
| change_recipient_script, | |
| change_amount, | |
| collateral_asset_id, | |
| None, | |
| )); | |
| } | |
| pst.add_output(Output::from_txout(TxOut::new_fee( | |
| fee_amount, | |
| collateral_asset_id, | |
| ))); | |
| pst.extract_tx()? | |
| .verify_tx_amt_proofs(secp256k1::SECP256K1, &[collateral_tx_out])?; | |
| Ok(pst) | |
| // This should be converted to address | |
| Ok(mint_script_pubkey) |
| use std::collections::HashMap; | ||
|
|
||
| use simplicityhl::num::U256; | ||
| use simplicityhl::types::{ResolvedType, TypeConstructible, UIntType}; | ||
| use simplicityhl::value::{UIntValue, ValueConstructible}; | ||
| use simplicityhl::{WitnessValues, str::WitnessName}; | ||
|
|
||
| #[allow(non_camel_case_types)] | ||
| pub type u256 = [u8; 32]; | ||
| pub const DEPTH: usize = 7; | ||
|
|
||
| #[derive(Debug, Clone, bincode::Encode, bincode::Decode, PartialEq, Eq)] | ||
| pub struct SMTWitness { | ||
| key: u256, | ||
| leaf: u256, | ||
| merkle_data: [(u256, bool); DEPTH], | ||
| } | ||
|
|
||
| impl SMTWitness { | ||
| #[must_use] | ||
| pub fn new(key: &u256, leaf: &u256, merkle_data: &[(u256, bool); DEPTH]) -> Self { | ||
| Self { | ||
| key: *key, | ||
| leaf: *leaf, | ||
| merkle_data: *merkle_data, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Default for SMTWitness { | ||
| fn default() -> Self { | ||
| Self { | ||
| key: [0u8; 32], | ||
| leaf: [0u8; 32], | ||
| merkle_data: [([0u8; 32], false); DEPTH], | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Add more context (documentation) about how practically this witness is going to be used (i.e. explain rrrrrr witness argument)
Also if there are nuances, mention them
| 0, | ||
| cmr, | ||
| ControlBlock::from_slice( | ||
| &control_block(cmr, &old_spend_info).serialize(), /*&control_block.serialize()*/ |
There was a problem hiding this comment.
| &control_block(cmr, &old_spend_info).serialize(), /*&control_block.serialize()*/ | |
| &control_block(cmr, &old_spend_info).serialize(), |
| use simplicityhl::simplicity::elements::hashes::HashEngine as _; | ||
| use simplicityhl::simplicity::hashes::{Hash, sha256}; | ||
|
|
||
| use super::build_witness::{DEPTH, u256}; | ||
|
|
||
| #[derive(Debug, Clone, bincode::Encode, bincode::Decode, PartialEq, Eq)] | ||
| pub enum TreeNode { | ||
| Leaf { | ||
| leaf_hash: u256, | ||
| }, | ||
| Branch { | ||
| hash: u256, | ||
| left: Box<TreeNode>, | ||
| right: Box<TreeNode>, | ||
| }, | ||
| } | ||
|
|
||
| impl TreeNode { | ||
| pub fn get_hash(&self) -> u256 { | ||
| match self { | ||
| TreeNode::Leaf { leaf_hash } => *leaf_hash, | ||
| TreeNode::Branch { hash, .. } => *hash, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub struct SparseMerkleTree { | ||
| root: Box<TreeNode>, | ||
| precalculate_hashes: [u256; DEPTH], | ||
| } |
There was a problem hiding this comment.
Add more context and documentation (great if there are any links to external sources)
| impl SparseMerkleTree { | ||
| #[must_use] | ||
| pub fn new() -> Self { | ||
| let mut precalculate_hashes = [[0u8; 32]; DEPTH]; | ||
| let mut eng = sha256::Hash::engine(); | ||
| let zero = [0u8; 32]; | ||
| eng.input(&zero); | ||
| precalculate_hashes[0] = *sha256::Hash::from_engine(eng).as_byte_array(); | ||
|
|
||
| for i in 1..DEPTH { | ||
| let mut eng = sha256::Hash::engine(); | ||
| eng.input(&precalculate_hashes[i - 1]); | ||
| eng.input(&precalculate_hashes[i - 1]); | ||
| precalculate_hashes[i] = *sha256::Hash::from_engine(eng).as_byte_array(); | ||
| } | ||
|
|
||
| Self { | ||
| root: Box::new(TreeNode::Leaf { | ||
| leaf_hash: precalculate_hashes[0], | ||
| }), | ||
| precalculate_hashes, | ||
| } | ||
| } | ||
|
|
||
| fn calculate_hash(left: &mut TreeNode, right: &mut TreeNode) -> u256 { | ||
| let mut eng = sha256::Hash::engine(); | ||
| eng.input(&left.get_hash()); | ||
| eng.input(&right.get_hash()); | ||
| *sha256::Hash::from_engine(eng).as_byte_array() | ||
| } | ||
|
|
||
| // Return array of hashes | ||
| fn traverse( | ||
| defaults: &[u256], | ||
| leaf: &u256, | ||
| path: &[bool], | ||
| root: &mut Box<TreeNode>, | ||
| hashes: &mut [u256], | ||
| ) { | ||
| let Some((is_right, remaining_path)) = path.split_first() else { | ||
| let tag = sha256::Hash::hash(b"TapData"); | ||
| let mut eng = sha256::Hash::engine(); | ||
| eng.input(tag.as_byte_array()); | ||
| eng.input(tag.as_byte_array()); | ||
| eng.input(leaf); | ||
|
|
||
| **root = TreeNode::Leaf { | ||
| leaf_hash: *sha256::Hash::from_engine(eng).as_byte_array(), | ||
| }; | ||
| return; | ||
| }; | ||
|
|
||
| let (child_zero, remaining_defaults) = defaults | ||
| .split_last() | ||
| .expect("Defaults length must match path length"); | ||
|
|
||
| if matches!(**root, TreeNode::Leaf { .. }) { | ||
| let new_branch = Box::new(TreeNode::Branch { | ||
| hash: [0u8; 32], | ||
| left: Box::new(TreeNode::Leaf { | ||
| leaf_hash: *child_zero, | ||
| }), | ||
| right: Box::new(TreeNode::Leaf { | ||
| leaf_hash: *child_zero, | ||
| }), | ||
| }); | ||
|
|
||
| *root = new_branch; | ||
| } | ||
|
|
||
| let (current_hash_slot, remaining_hashes) = hashes | ||
| .split_first_mut() | ||
| .expect("Hashes length must match path length"); | ||
|
|
||
| if let TreeNode::Branch { | ||
| ref mut left, | ||
| ref mut right, | ||
| ref mut hash, | ||
| } = **root | ||
| { | ||
| if *is_right { | ||
| *current_hash_slot = left.get_hash(); | ||
| Self::traverse( | ||
| remaining_defaults, | ||
| leaf, | ||
| remaining_path, | ||
| right, | ||
| remaining_hashes, | ||
| ); | ||
| } else { | ||
| *current_hash_slot = right.get_hash(); | ||
| Self::traverse( | ||
| remaining_defaults, | ||
| leaf, | ||
| remaining_path, | ||
| left, | ||
| remaining_hashes, | ||
| ); | ||
| } | ||
|
|
||
| *hash = Self::calculate_hash(left, right); | ||
| } else { | ||
| unreachable!("Should be a branch at this point"); | ||
| } | ||
| } | ||
|
|
||
| // For insert change 0 to leaf. | ||
| // For delete vice versa. | ||
| // And for udpate change old value to new. | ||
| // Then, recalculate hashes | ||
| pub fn update(&mut self, leaf: &u256, path: [bool; DEPTH]) -> [u256; DEPTH] { | ||
| let mut hashes = self.precalculate_hashes; | ||
| Self::traverse( | ||
| &self.precalculate_hashes, | ||
| leaf, | ||
| &path, | ||
| &mut self.root, | ||
| &mut hashes, | ||
| ); | ||
| hashes | ||
| } | ||
| } |
There was a problem hiding this comment.
Here the same, mention nuances, especially attacks, etc
crates/contracts/Cargo.toml
Outdated
| [dev-dependencies] | ||
| anyhow = "1" No newline at end of file | ||
| anyhow = "1" | ||
| rand = "0.9.2" No newline at end of file |
There was a problem hiding this comment.
Do not add rand as a independent dep, use rand from the simplicityhl::elements dep
crates/contracts/Cargo.toml
Outdated
| bytes32-tr-storage = [] | ||
| array-tr-storage = [] | ||
| smt-storage = [] | ||
| swap-with-change = [] |
There was a problem hiding this comment.
| swap-with-change = [] |
250295e to
2ca279d
Compare
8035e03 to
d19f4d5
Compare
…tion against second preimage attack
d19f4d5 to
93da027
Compare
No description provided.