Skip to content

Commit d33dbc3

Browse files
committed
feat: foundational types & crypto
1 parent d5bac97 commit d33dbc3

File tree

10 files changed

+1070
-70
lines changed

10 files changed

+1070
-70
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ serde_json = "1.0"
3838
# Cryptography
3939
ed25519-consensus = "2.1"
4040
sha2 = "0.10"
41+
rand = "0.8"
4142

4243
# Storage
4344
rocksdb = { version = "0.21", default-features = false, features = ["lz4"] }

crates/crypto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ sha2 = { workspace = true }
1111
thiserror = { workspace = true }
1212
hex = { workspace = true }
1313
serde = { workspace = true }
14+
rand = { workspace = true }
1415

1516
[dev-dependencies]
1617
proptest = { workspace = true }

crates/crypto/src/hash.rs

Lines changed: 158 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
1-
//! Hashing utilities.
1+
//! Hashing utilities using SHA-256.
2+
//!
3+
//! Provides standard cryptographic hashing functions and Merkle tree
4+
//! computation for transaction batches.
25
36
use sha2::{Digest, Sha256};
47

5-
/// Hash a block header.
6-
pub fn hash_block(data: &[u8]) -> [u8; 32] {
8+
/// Hash arbitrary data using SHA-256.
9+
pub fn hash(data: &[u8]) -> [u8; 32] {
710
let mut hasher = Sha256::new();
811
hasher.update(data);
912
hasher.finalize().into()
1013
}
1114

15+
/// Hash a block header.
16+
pub fn hash_block(data: &[u8]) -> [u8; 32] {
17+
hash(data)
18+
}
19+
1220
/// Hash a transaction.
1321
pub fn hash_tx(data: &[u8]) -> [u8; 32] {
14-
let mut hasher = Sha256::new();
15-
hasher.update(data);
16-
hasher.finalize().into()
22+
hash(data)
1723
}
1824

19-
/// Compute Merkle root of transaction hashes.
25+
/// Compute Merkle root of transaction hashes using binary tree.
26+
///
27+
/// Implementation follows Bitcoin's Merkle tree algorithm:
28+
/// - If empty, returns zero hash
29+
/// - If single hash, returns that hash
30+
/// - Otherwise, recursively combines pairs of hashes
31+
/// - If odd number of hashes, duplicates the last one
2032
pub fn merkle_root(hashes: &[[u8; 32]]) -> [u8; 32] {
2133
if hashes.is_empty() {
2234
return [0u8; 32];
@@ -25,14 +37,50 @@ pub fn merkle_root(hashes: &[[u8; 32]]) -> [u8; 32] {
2537
return hashes[0];
2638
}
2739

28-
// Simplified Merkle tree (not production-ready)
29-
let mut hasher = Sha256::new();
30-
for hash in hashes {
31-
hasher.update(hash);
40+
// Recursively build merkle tree
41+
merkle_root_recursive(hashes)
42+
}
43+
44+
/// Internal recursive merkle tree builder.
45+
fn merkle_root_recursive(hashes: &[[u8; 32]]) -> [u8; 32] {
46+
if hashes.len() == 1 {
47+
return hashes[0];
3248
}
49+
50+
let mut next_level = Vec::new();
51+
52+
// Process pairs of hashes
53+
for chunk in hashes.chunks(2) {
54+
let combined = if chunk.len() == 2 {
55+
// Hash the concatenation of two hashes
56+
hash_pair(&chunk[0], &chunk[1])
57+
} else {
58+
// Odd number: duplicate the last hash
59+
hash_pair(&chunk[0], &chunk[0])
60+
};
61+
next_level.push(combined);
62+
}
63+
64+
merkle_root_recursive(&next_level)
65+
}
66+
67+
/// Hash a pair of hashes together.
68+
fn hash_pair(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
69+
let mut hasher = Sha256::new();
70+
hasher.update(left);
71+
hasher.update(right);
3372
hasher.finalize().into()
3473
}
3574

75+
/// Compute merkle root from raw transaction data.
76+
///
77+
/// This is a convenience function that hashes transactions first,
78+
/// then computes the merkle root.
79+
pub fn merkle_root_from_txs(transactions: &[&[u8]]) -> [u8; 32] {
80+
let tx_hashes: Vec<[u8; 32]> = transactions.iter().map(|tx| hash_tx(tx)).collect();
81+
merkle_root(&tx_hashes)
82+
}
83+
3684
#[cfg(test)]
3785
mod tests {
3886
use super::*;
@@ -44,4 +92,103 @@ mod tests {
4492
let hash2 = hash_tx(data);
4593
assert_eq!(hash1, hash2);
4694
}
95+
96+
#[test]
97+
fn test_merkle_root_empty() {
98+
let hashes = [];
99+
let root = merkle_root(&hashes);
100+
assert_eq!(root, [0u8; 32]);
101+
}
102+
103+
#[test]
104+
fn test_merkle_root_single() {
105+
let hash = [1u8; 32];
106+
let root = merkle_root(&[hash]);
107+
assert_eq!(root, hash);
108+
}
109+
110+
#[test]
111+
fn test_merkle_root_two() {
112+
let hash1 = [1u8; 32];
113+
let hash2 = [2u8; 32];
114+
let root = merkle_root(&[hash1, hash2]);
115+
116+
// Should be hash of concatenation
117+
let expected = hash_pair(&hash1, &hash2);
118+
assert_eq!(root, expected);
119+
}
120+
121+
#[test]
122+
fn test_merkle_root_four() {
123+
let hashes = [
124+
[1u8; 32],
125+
[2u8; 32],
126+
[3u8; 32],
127+
[4u8; 32],
128+
];
129+
let root = merkle_root(&hashes);
130+
131+
// Manually compute expected root
132+
let h12 = hash_pair(&hashes[0], &hashes[1]);
133+
let h34 = hash_pair(&hashes[2], &hashes[3]);
134+
let expected = hash_pair(&h12, &h34);
135+
136+
assert_eq!(root, expected);
137+
}
138+
139+
#[test]
140+
fn test_merkle_root_odd() {
141+
let hashes = [
142+
[1u8; 32],
143+
[2u8; 32],
144+
[3u8; 32],
145+
];
146+
let root = merkle_root(&hashes);
147+
148+
// Third hash should be duplicated
149+
let h12 = hash_pair(&hashes[0], &hashes[1]);
150+
let h33 = hash_pair(&hashes[2], &hashes[2]);
151+
let expected = hash_pair(&h12, &h33);
152+
153+
assert_eq!(root, expected);
154+
}
155+
156+
#[test]
157+
fn test_merkle_root_deterministic() {
158+
let hashes = [
159+
[1u8; 32],
160+
[2u8; 32],
161+
[3u8; 32],
162+
[4u8; 32],
163+
];
164+
165+
let root1 = merkle_root(&hashes);
166+
let root2 = merkle_root(&hashes);
167+
168+
assert_eq!(root1, root2);
169+
}
170+
171+
#[test]
172+
fn test_merkle_root_from_txs() {
173+
let txs = vec![b"tx1".as_slice(), b"tx2".as_slice(), b"tx3".as_slice()];
174+
let root = merkle_root_from_txs(&txs);
175+
176+
// Should match manual calculation
177+
let tx_hashes: Vec<[u8; 32]> = txs.iter().map(|tx| hash_tx(tx)).collect();
178+
let expected = merkle_root(&tx_hashes);
179+
180+
assert_eq!(root, expected);
181+
}
182+
183+
#[test]
184+
fn test_different_order_different_root() {
185+
let hashes1 = [[1u8; 32], [2u8; 32]];
186+
let hashes2 = [[2u8; 32], [1u8; 32]];
187+
188+
let root1 = merkle_root(&hashes1);
189+
let root2 = merkle_root(&hashes2);
190+
191+
// Order matters
192+
assert_ne!(root1, root2);
193+
}
47194
}

crates/crypto/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
pub mod hash;
1010
pub mod signature;
1111

12-
pub use hash::{hash_block, hash_tx, merkle_root};
13-
pub use signature::{PrivateKey, PublicKey, Signature};
12+
pub use hash::{hash, hash_block, hash_tx, merkle_root, merkle_root_from_txs};
13+
pub use signature::{Address, KeyPair, PrivateKey, PublicKey, Signature, SignatureError};

0 commit comments

Comments
 (0)