Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Utreexo: adding new item to the accumulator #167 #190

Merged
Merged
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
6 changes: 5 additions & 1 deletion packages/client/src/main.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ fn main(mut arguments: Span<felt252>) -> State {
let Args { mut state, blocks, } = Serde::deserialize(ref arguments)
.expect('Failed to deserialize');

let mut utxo_set = UtxoSet { utreexo_state: state.utreexo_state, cache: Default::default(), };
let mut utxo_set = UtxoSet {
utreexo_state: state.utreexo_state,
leaves_to_add: Default::default(),
cache: Default::default(),
};

for block in blocks {
state
Expand Down
4 changes: 3 additions & 1 deletion packages/client/src/test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ fn test(mut arguments: Span<felt252>) {
// Allows to test one isolated block, or a batch of blocks starting from genesis.
let mut state: State = State { chain_state: chain_state, utreexo_state: Default::default(), };
let mut utxo_set: UtxoSet = UtxoSet {
utreexo_state: state.utreexo_state, cache: Default::default()
utreexo_state: state.utreexo_state,
leaves_to_add: Default::default(),
cache: Default::default()
};

let mut gas_before = get_available_gas();
Expand Down
220 changes: 218 additions & 2 deletions packages/consensus/src/types/utreexo.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
//! Read more about utreexo: https://eprint.iacr.org/2019/611.pdf

use super::transaction::OutPoint;
use core::poseidon::PoseidonTrait;
use core::hash::{HashStateTrait, HashStateExTrait};
use core::fmt::{Display, Formatter, Error};

/// Accumulator representation of the state aka "Compact State Node".
Expand All @@ -50,7 +52,7 @@ pub trait UtreexoAccumulator {
///
/// Note that this call also pushes old UTXOs "to the left", to a larger subtree.
/// This mechanism ensures that short-lived outputs have small inclusion proofs.
fn add(ref self: UtreexoState, output: OutPoint);
fn add(ref self: UtreexoState, outpoint_hash: felt252);

/// Verifies inclusion proof for a single output.
fn verify(
Expand All @@ -72,6 +74,54 @@ pub trait UtreexoAccumulator {
fn delete_batch(ref self: UtreexoState, proof: @UtreexoBatchProof);
}

pub impl UtreexoAccumulatorImpl of UtreexoAccumulator {
// https://eprint.iacr.org/2019/611.pdf Algorithm 1 AddOne
fn add(ref self: UtreexoState, outpoint_hash: felt252) {
let mut new_roots: Array<Option<felt252>> = Default::default();
let mut n: felt252 = outpoint_hash;
let mut first_none_found: bool = false;

for root in self
.roots {
if (!first_none_found) {
if (root.is_none()) {
first_none_found = true;
new_roots.append(Option::Some(n));
} else {
n = PoseidonTrait::new().update_with(((*root).unwrap(), n)).finalize();
new_roots.append(Option::None);
}
} else {
new_roots.append(*root);
}
};

//check if end with Option::None
if (new_roots[new_roots.len() - 1].is_some()) {
new_roots.append(Option::None);
}

self.roots = new_roots.span();
self.num_leaves += 1_u64;
}

fn verify(
self: @UtreexoState, output: @OutPoint, proof: @UtreexoProof
) -> Result<(), UtreexoError> {
Result::Ok(())
}

fn delete(ref self: UtreexoState, proof: @UtreexoProof) {}

fn verify_batch(
self: @UtreexoState, outputs: Span<OutPoint>, proof: @UtreexoBatchProof
) -> Result<(), UtreexoError> {
Result::Ok(())
}

fn delete_batch(ref self: UtreexoState, proof: @UtreexoBatchProof) {}
}

#[derive(Drop, Copy, PartialEq)]
pub enum UtreexoError {}

Expand All @@ -97,7 +147,7 @@ pub struct UtreexoBatchProof {

pub impl UtreexoStateDefault of Default<UtreexoState> {
fn default() -> UtreexoState {
UtreexoState { roots: array![].span(), num_leaves: 0, }
UtreexoState { roots: array![Option::None].span(), num_leaves: 0, }
}
}

Expand Down Expand Up @@ -142,3 +192,169 @@ impl UtreexoBatchProofDisplay of Display<UtreexoBatchProof> {
Result::Ok(())
}
}


#[cfg(test)]
mod tests {
use consensus::types::utxo_set::{UtxoSet, UtxoSetTrait};

#[test]
/// To check the validity of expected fields, there is a python program from ZeroSync
/// https://github.com/ZeroSync/ZeroSync/blob/main/src/utxo_set/bridge_node.py
/// $ python scripts/data/utreexo.py
fn test_utreexo_add1() {
let mut utxo_set: UtxoSet = UtxoSetTrait::new(Default::default());
let outpoint: felt252 = 0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C;

// add first leave to empty utreexo
utxo_set.leaves_to_add = array![outpoint];
utxo_set.utreexo_add();

let expected: Span<Option<felt252>> = array![
Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C),
Option::None
]
.span();
assert_eq!(utxo_set.utreexo_state.roots, expected, "cannot add first leave");
assert_eq!(utxo_set.utreexo_state.num_leaves, 1);

// add second leave
utxo_set.leaves_to_add = array![outpoint];
utxo_set.utreexo_add();

let expected: Span<Option<felt252>> = array![
Option::None,
Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6),
Option::None
]
.span();
assert_eq!(utxo_set.utreexo_state.roots, expected, "cannot add second leave");
assert_eq!(utxo_set.utreexo_state.num_leaves, 2);

// add thirdth leave
utxo_set.leaves_to_add = array![outpoint];
utxo_set.utreexo_add();

let expected: Span<Option<felt252>> = array![
Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C),
Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6),
Option::None
]
.span();
assert_eq!(utxo_set.utreexo_state.roots, expected, "cannot add thirdth leave");
assert_eq!(utxo_set.utreexo_state.num_leaves, 3);

// add fourth leave
utxo_set.leaves_to_add = array![outpoint];
utxo_set.utreexo_add();

let expected: Span<Option<felt252>> = array![
Option::None,
Option::None,
Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1),
Option::None
]
.span();
assert_eq!(utxo_set.utreexo_state.roots, expected, "cannot add fourth leave");
assert_eq!(utxo_set.utreexo_state.num_leaves, 4);

// add fifth leave
utxo_set.leaves_to_add = array![outpoint];
utxo_set.utreexo_add();

let expected: Span<Option<felt252>> = array![
Option::Some(0x291F8F5FC449D42C715B529E542F24A80136D18F4A85DE28829CD3DCAAC1B9C),
Option::None,
Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1),
Option::None
]
.span();
assert_eq!(utxo_set.utreexo_state.roots, expected, "cannot add fifth leave");
assert_eq!(utxo_set.utreexo_state.num_leaves, 5);

// add 3 leaves
utxo_set.leaves_to_add = array![outpoint, outpoint, outpoint];
utxo_set.utreexo_add();

let expected: Span<Option<felt252>> = array![
Option::None,
Option::None,
Option::None,
Option::Some(0x708EB39E30B035376EC871F8F17CD3BADAE6A68406B13C3BB671009D56F5AD),
Option::None
]
.span();
assert_eq!(utxo_set.utreexo_state.roots, expected, "cannot add 3 leaves");
assert_eq!(utxo_set.utreexo_state.num_leaves, 8);

// add 22 leaves
utxo_set
.leaves_to_add =
array![
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint,
outpoint
];
utxo_set.utreexo_add();

let expected: Span<Option<felt252>> = [
Option::None(()),
Option::Some(0x738A7C495E564574993BBCB6A62D65C3C570BB81C63801066AF8934649F66F6),
Option::Some(0x25D0DE35DD446E3D35504866FD7A04D4245E01B5908E19EAA70ABA84DD5A1F1),
Option::Some(0x708EB39E30B035376EC871F8F17CD3BADAE6A68406B13C3BB671009D56F5AD),
Option::Some(0x58D6BEF6CFC28638FB4C8271355961F50922BCC1577DD2B6D04E11B7A911702),
Option::None(())
].span();
assert_eq!(utxo_set.utreexo_state.roots, expected, "cannot add 22 leaves");
assert_eq!(utxo_set.utreexo_state.num_leaves, 30);
}
///
/// python scripts/data/utreexo.py
///
/// Roots:
/// ['0x0291f8f5fc449d42c715b529e542f24a80136d18f4a85de28829cd3dcaac1b9c', '', '', '', '', '',
/// '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
///
/// Roots:
/// ['', '0x0738a7c495e564574993bbcb6a62d65c3c570bb81c63801066af8934649f66f6', '', '', '', '',
/// '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
///
/// Roots: ['0x0291f8f5fc449d42c715b529e542f24a80136d18f4a85de28829cd3dcaac1b9c',
/// '0x0738a7c495e564574993bbcb6a62d65c3c570bb81c63801066af8934649f66f6', '', '', '', '', '',
/// '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
///
/// Roots: ['', '', '0x025d0de35dd446e3d35504866fd7a04d4245e01b5908e19eaa70aba84dd5a1f1', '',
/// '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
///
/// Roots: ['0x0291f8f5fc449d42c715b529e542f24a80136d18f4a85de28829cd3dcaac1b9c', '',
/// '0x025d0de35dd446e3d35504866fd7a04d4245e01b5908e19eaa70aba84dd5a1f1', '', '', '', '', '',
/// '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
///
/// Roots: ['', '', '', '0x00708eb39e30b035376ec871f8f17cd3badae6a68406b13c3bb671009d56f5ad',
/// '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
///
/// Roots: ['', '0x0738a7c495e564574993bbcb6a62d65c3c570bb81c63801066af8934649f66f6',
/// '0x025d0de35dd446e3d35504866fd7a04d4245e01b5908e19eaa70aba84dd5a1f1',
/// '0x00708eb39e30b035376ec871f8f17cd3badae6a68406b13c3bb671009d56f5ad',
/// '0x058d6bef6cfc28638fb4c8271355961f50922bcc1577dd2b6d04e11b7a911702', '', '', '', '', '',
/// '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
}
15 changes: 12 additions & 3 deletions packages/consensus/src/types/utxo_set.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
use core::dict::Felt252Dict;
use core::hash::{HashStateTrait, HashStateExTrait};
use core::poseidon::PoseidonTrait;
use super::utreexo::UtreexoState;
use super::transaction::OutPoint;
use super::utreexo::{UtreexoState, UtreexoAccumulator};

#[derive(Default, Destruct)]
pub struct UtxoSet {
/// Utreexo state.
pub utreexo_state: UtreexoState,
/// The leaves represent the poseidon hashes of a block's outpoints, i.e. utxos
pub leaves_to_add: Array<felt252>,
/// Hashes of UTXOs created within the current block(s).
/// Note that to preserve the ordering, cache has to be updated right after a
/// particular output is created or spent.
Expand All @@ -32,10 +34,17 @@ pub impl UtxoSetImpl of UtxoSetTrait {
}

fn add(ref self: UtxoSet, output: OutPoint) {
let outpoint_hash = PoseidonTrait::new().update_with(output).finalize();
if output.data.cached {
let outpoint_hash = PoseidonTrait::new().update_with(output).finalize();
self.cache.insert(outpoint_hash, true);
} else { // TODO: update utreexo roots
} else {
self.leaves_to_add.append(outpoint_hash);
}
}

fn utreexo_add(ref self: UtxoSet) {
for leave in self.leaves_to_add.clone() {
self.utreexo_state.add(leave);
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/consensus/src/validation/transaction.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,9 @@ mod tests {
.update_with((*tx.inputs[0]).previous_output)
.finalize();
cache.insert(outpoint_hash, true);
let mut utxo_set: UtxoSet = UtxoSet { utreexo_state: Default::default(), cache: cache, };
let mut utxo_set: UtxoSet = UtxoSet {
utreexo_state: Default::default(), leaves_to_add: Default::default(), cache: cache,
};

validate_transaction(@tx, block_height, 0, txid, ref utxo_set).unwrap();
}
Expand Down
3 changes: 2 additions & 1 deletion scripts/data/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
requests==2.32.3
black==24.8.0
flake8==7.1.1
flake8-black==0.3.6
flake8-black==0.3.6
poseidon_py==0.1.5
Loading
Loading