Skip to content
Merged

- #65

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion aggregate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ bitcoin = { workspace = true }

[dev-dependencies]
bitcoin = { workspace = true, default-features = true }
rusqlite = { version = "0.36.0", features = ["bundled"] }
4 changes: 2 additions & 2 deletions aggregate/README.md
Original file line number Diff line number Diff line change
@@ -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.
78 changes: 0 additions & 78 deletions aggregate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<SwiftSyncTag>(&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::{
Expand Down Expand Up @@ -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());
}
}
76 changes: 35 additions & 41 deletions aggregate/tests/test.rs
Original file line number Diff line number Diff line change
@@ -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::<Txid>().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::<Txid>().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::<Txid>().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::<Txid>().unwrap();
let outpoint = OutPoint { txid, vout };
for outpoint in outpoints {
acc.add(outpoint);
}
assert!(acc.is_zero());
Expand Down
Binary file removed contrib/data/signet_outpoints.sqlite
Binary file not shown.
4 changes: 2 additions & 2 deletions node/README.md
Original file line number Diff line number Diff line change
@@ -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:

Expand Down Expand Up @@ -34,7 +34,7 @@ cargo run --bin ibd --release -- <args>

```
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.
Expand Down
2 changes: 1 addition & 1 deletion node/config_spec.toml
Original file line number Diff line number Diff line change
@@ -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`"
Expand Down
2 changes: 1 addition & 1 deletion node/src/bin/ibd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down