diff --git a/aggregate/Cargo.toml b/aggregate/Cargo.toml index cd17b84..75a6778 100644 --- a/aggregate/Cargo.toml +++ b/aggregate/Cargo.toml @@ -8,4 +8,3 @@ bitcoin = { workspace = true } [dev-dependencies] bitcoin = { workspace = true, default-features = true } -rusqlite = { version = "0.36.0", features = ["bundled"] } diff --git a/aggregate/README.md b/aggregate/README.md index cd8e45f..26d53e7 100644 --- a/aggregate/README.md +++ b/aggregate/README.md @@ -1,3 +1,3 @@ -# SwiftSync Accumulator +# SwiftSync Aggregate -This crate defines a simple accumulator that may add and spend `OutPoint` from a set. If the `OutPoint` are add and spent from the accumulator an equivalent number of times, the accumulator state is zero. Internally, the accumulator is represented as two `u128` integers. Hashing elements is far more computationally expensive than adding and subtracing from `u128` integers, so hashes may be pre-computed with functions in this crate. +This create defines a simple aggregate that may add and spend `OutPoint` from a set. If the `OutPoint` are add and spent from the accumulator an equivalent number of times, the accumulator state is zero. Internally, the accumulator is represented as two `u128` integers. Hashing elements is far more computationally expensive than adding and subtracing from `u128` integers, so hashes may be pre-computed with functions in this create. diff --git a/aggregate/src/lib.rs b/aggregate/src/lib.rs index b8cbf3d..656e253 100644 --- a/aggregate/src/lib.rs +++ b/aggregate/src/lib.rs @@ -107,63 +107,6 @@ impl Default for Aggregate { } } -const VOUT_MAX: usize = 16_000; - -/// An alternative accumulator than that of the original SwiftSync write-up, intended to remove the -/// hashes required to update elements in the set. -#[derive(Debug)] -pub struct MultAggregate { - memo_table: [[[u8; 16]; 2]; VOUT_MAX], - internal: u128, -} - -impl MultAggregate { - pub fn new() -> Self { - let mut memo_table = [[[0u8; 16]; 2]; VOUT_MAX]; - for (vout, arr) in memo_table.iter_mut().enumerate() { - let bytes = sha256t::hash::(&vout.to_le_bytes()).to_byte_array(); - let (left, right) = split_in_half(bytes); - *arr = [left, right]; - } - Self { - memo_table, - internal: 0, - } - } - - fn compute_salted_value(&self, outpoint: OutPoint) -> u128 { - let branches = self.memo_table[outpoint.vout as usize]; - let left = branches[0]; - let right = branches[1]; - let txid_bytes = outpoint.txid.to_byte_array(); - let (tx_left, tx_right) = split_in_half(txid_bytes); - let lhs = u128::from_le_bytes(left).wrapping_mul(u128::from_le_bytes(tx_left)); - let rhs = u128::from_le_bytes(right).wrapping_mul(u128::from_le_bytes(tx_right)); - lhs.wrapping_add(rhs) - } - - /// Add an outpoint to the set - pub fn add(&mut self, outpoint: OutPoint) { - let salted_val = self.compute_salted_value(outpoint); - self.internal = self.internal.wrapping_add(salted_val); - } - - /// Remove an outpoint from the set - pub fn spend(&mut self, outpoint: OutPoint) { - let salted_val = self.compute_salted_value(outpoint); - self.internal = self.internal.wrapping_sub(salted_val); - } - - pub fn is_zero(&self) -> bool { - self.internal.eq(&0) - } -} - -impl Default for MultAggregate { - fn default() -> Self { - Self::new() - } -} #[cfg(test)] mod tests { use bitcoin::{ @@ -301,25 +244,4 @@ mod tests { acc.update(AggregateUpdate::Spent(hash_one)); assert!(acc.is_zero()); } - - #[test] - fn test_mult_agg_is_zero() { - let mut acc = MultAggregate::default(); - let [outpoint_one, outpoint_two, outpoint_three, outpoint_four, outpoint_five] = - make_five_outpoint(); - // Add the members - acc.add(outpoint_one); - acc.add(outpoint_two); - acc.add(outpoint_five); - acc.add(outpoint_four); - acc.add(outpoint_three); - assert!(!acc.is_zero()); - // Take away the members - acc.spend(outpoint_two); - acc.spend(outpoint_five); - acc.spend(outpoint_three); - acc.spend(outpoint_four); - acc.spend(outpoint_one); - assert!(acc.is_zero()); - } } diff --git a/aggregate/tests/test.rs b/aggregate/tests/test.rs index c69aae5..c1860ea 100644 --- a/aggregate/tests/test.rs +++ b/aggregate/tests/test.rs @@ -1,56 +1,50 @@ -use aggregate::{Aggregate, MultAggregate}; +use aggregate::Aggregate; use bitcoin::{OutPoint, Txid}; -use rusqlite::Connection; -const SELECT_STMT: &str = "SELECT txid, vout FROM utxos"; +const TEST_ITERS: usize = 10_000; +const TEST_SEED: u64 = 420; -#[test] -fn test_static_utxo_set() { - let mut acc = Aggregate::new(); - let conn = Connection::open("../contrib/data/signet_outpoints.sqlite").unwrap(); - let mut stmt = conn.prepare(SELECT_STMT).unwrap(); - let mut rows = stmt.query([]).unwrap(); - while let Some(row) = rows.next().unwrap() { - let txid: String = row.get(0).unwrap(); - let vout: u32 = row.get(1).unwrap(); - let txid = txid.parse::().unwrap(); - let outpoint = OutPoint { txid, vout }; - acc.spend(outpoint); +struct Rng { + state: u64, +} + +impl Rng { + fn new(seed: u64) -> Self { + Rng { + state: if seed == 0 { 1 } else { seed }, + } } - assert!(!acc.is_zero()); - let mut stmt = conn.prepare(SELECT_STMT).unwrap(); - let mut rows = stmt.query([]).unwrap(); - while let Some(row) = rows.next().unwrap() { - let txid: String = row.get(0).unwrap(); - let vout: u32 = row.get(1).unwrap(); - let txid = txid.parse::().unwrap(); - let outpoint = OutPoint { txid, vout }; - acc.add(outpoint); + + fn next_u64(&mut self) -> u64 { + self.state ^= self.state << 13; + self.state ^= self.state >> 7; + self.state ^= self.state << 17; + self.state + } + + fn next_32_bytes(&mut self) -> [u8; 32] { + let mut out = [0u8; 32]; + for chunk in out.chunks_exact_mut(8) { + chunk.copy_from_slice(&self.next_u64().to_le_bytes()); + } + out } - assert!(acc.is_zero()); } #[test] -fn test_mult_agg() { - let mut acc = MultAggregate::new(); - let conn = Connection::open("../contrib/data/signet_outpoints.sqlite").unwrap(); - let mut stmt = conn.prepare(SELECT_STMT).unwrap(); - let mut rows = stmt.query([]).unwrap(); - while let Some(row) = rows.next().unwrap() { - let txid: String = row.get(0).unwrap(); - let vout: u32 = row.get(1).unwrap(); - let txid = txid.parse::().unwrap(); +fn test_static_utxo_set() { + let mut acc = Aggregate::new(); + let mut rng = Rng::new(TEST_SEED); + let mut outpoints = Vec::with_capacity(TEST_ITERS); + for _ in 0..TEST_ITERS { + let txid = Txid::from_byte_array(rng.next_32_bytes()); + let vout = (rng.next_u64() % u32::MAX as u64) as u32; let outpoint = OutPoint { txid, vout }; acc.spend(outpoint); + outpoints.push(outpoint); } assert!(!acc.is_zero()); - let mut stmt = conn.prepare(SELECT_STMT).unwrap(); - let mut rows = stmt.query([]).unwrap(); - while let Some(row) = rows.next().unwrap() { - let txid: String = row.get(0).unwrap(); - let vout: u32 = row.get(1).unwrap(); - let txid = txid.parse::().unwrap(); - let outpoint = OutPoint { txid, vout }; + for outpoint in outpoints { acc.add(outpoint); } assert!(acc.is_zero()); diff --git a/contrib/data/signet_outpoints.sqlite b/contrib/data/signet_outpoints.sqlite deleted file mode 100644 index eb779d1..0000000 Binary files a/contrib/data/signet_outpoints.sqlite and /dev/null differ diff --git a/node/README.md b/node/README.md index 6957ef2..cf6dbe2 100644 --- a/node/README.md +++ b/node/README.md @@ -1,6 +1,6 @@ # SwiftSync fast IBD -This binary implements a SwiftSync client that downloads blocks in parallel from multiple peers, references a hint file, and updates an accumulator. Once the client has reached the stop hash specified in the hint file, the accumulator state is reported as verified or false. For more information, read the [SwiftSync specification](https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd). +This binary implements a SwiftSync client that downloads blocks in parallel from multiple peers, references a hintsfile, and updates an accumulator. Once the client has reached the stop hash specified in the hintsfile, the accumulator state is reported as verified or false. For more information, read the [SwiftSync specification](https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd). You will need a `.hints` file locally to run this binary. You may download one from a server: @@ -34,7 +34,7 @@ cargo run --bin ibd --release -- ``` Arguments: - --hintfile The path to your `bitcoin.hints` file that will + --hintsfile The path to your `bitcoin.hints` file that will be used for IBD. Default is `./bitcoin.hints` --blocks-dir Optional directory to store the blocks. Used only to measure performance. diff --git a/node/config_spec.toml b/node/config_spec.toml index dd0cb90..9472d62 100644 --- a/node/config_spec.toml +++ b/node/config_spec.toml @@ -1,5 +1,5 @@ [[param]] -name = "hintfile" +name = "hintsfile" type = "String" default = "\"./bitcoin.hints\".into()" doc = "The path to your `bitcoin.hints` file that will be used for IBD. Default is `./bitcoin.hints`" diff --git a/node/src/bin/ibd.rs b/node/src/bin/ibd.rs index 3f45df1..25a6969 100644 --- a/node/src/bin/ibd.rs +++ b/node/src/bin/ibd.rs @@ -21,7 +21,7 @@ configure_me::include_config!(); fn main() { let (config, _) = Config::including_optional_config_files::<&[&str]>(&[]).unwrap_or_exit(); - let hint_path = config.hintfile; + let hint_path = config.hintsfile; let blocks_dir = config.blocks_dir; let network = config .network