Skip to content

Commit

Permalink
Merge pull request #2539 from dusk-network/bloom-filter
Browse files Browse the repository at this point in the history
Replace `event_hash` for `event_bloom`
  • Loading branch information
Eduardo Leegwater Simões authored Sep 30, 2024
2 parents f52e13f + dd1e1ef commit ee5e359
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 63 deletions.
19 changes: 14 additions & 5 deletions consensus/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use node_data::StepName;
use crate::errors::*;

pub type StateRoot = [u8; 32];
pub type EventHash = [u8; 32];
pub type EventBloom = [u8; 256];
pub type Voter = (PublicKey, usize);

#[derive(Default, Clone, Debug)]
Expand All @@ -36,19 +36,28 @@ pub struct Output {
pub discarded_txs: Vec<Transaction>,
}

#[derive(Debug, Default, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct VerificationOutput {
pub state_root: StateRoot,
pub event_hash: EventHash,
pub event_bloom: EventBloom,
}

impl Default for VerificationOutput {
fn default() -> Self {
Self {
state_root: [0u8; 32],
event_bloom: [0u8; 256],
}
}
}

impl fmt::Display for VerificationOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"VerificationOutput {{ state_root: {}, event_hash: {} }}",
"VerificationOutput {{ state_root: {}, event_bloom: {} }}",
hex::encode(self.state_root),
hex::encode(self.event_hash)
hex::encode(self.event_bloom)
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion consensus/src/proposal/block_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl<T: Operations> Generator<T> {
self.executor.execute_state_transition(call_params).await?;

blk_header.state_hash = result.verification_output.state_root;
blk_header.event_hash = result.verification_output.event_hash;
blk_header.event_bloom = result.verification_output.event_bloom;

let tx_hashes: Vec<_> =
result.txs.iter().map(|t| t.inner.hash()).collect();
Expand Down
10 changes: 5 additions & 5 deletions consensus/src/validation/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ impl<T: Operations + 'static> ValidationStep<T> {
) -> anyhow::Result<()> {
match executor.verify_state_transition(candidate, voters).await {
Ok(output) => {
// Ensure the `event_hash` and `state_root` returned
// Ensure the `event_bloom` and `state_root` returned
// from the VST call are the
// ones we expect to have with the
// current candidate block.
if output.event_hash != candidate.header().event_hash {
if output.event_bloom != candidate.header().event_bloom {
return Err(anyhow!(
"mismatch, event_hash: {}, candidate_event_hash: {}",
hex::encode(output.event_hash),
hex::encode(candidate.header().event_hash)
"mismatch, event_bloom: {}, candidate_event_bloom: {}",
hex::encode(output.event_bloom),
hex::encode(candidate.header().event_bloom)
));
}

Expand Down
1 change: 1 addition & 0 deletions node-data/src/ledger/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use super::*;

pub type Hash = [u8; 32];
pub type Bloom = [u8; 256];

#[derive(Default, Debug, Clone)]
pub struct Block {
Expand Down
34 changes: 29 additions & 5 deletions node-data/src/ledger/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::message::{ConsensusHeader, MESSAGE_MAX_ITER};
use super::*;

pub type Seed = Signature;
#[derive(Default, Eq, PartialEq, Clone, Serialize)]
#[derive(Eq, PartialEq, Clone, Serialize)]
#[cfg_attr(any(feature = "faker", test), derive(Dummy))]
pub struct Header {
// Hashable fields
Expand All @@ -24,7 +24,7 @@ pub struct Header {
#[serde(serialize_with = "crate::serialize_hex")]
pub state_hash: Hash,
#[serde(serialize_with = "crate::serialize_hex")]
pub event_hash: Hash,
pub event_bloom: Bloom,
pub generator_bls_pubkey: PublicKeyBytes,
#[serde(serialize_with = "crate::serialize_hex")]
pub txroot: Hash,
Expand All @@ -47,6 +47,30 @@ pub struct Header {
pub att: Attestation,
}

impl Default for Header {
fn default() -> Self {
Self {
version: Default::default(),
height: Default::default(),
timestamp: Default::default(),
prev_block_hash: Default::default(),
seed: Default::default(),
state_hash: Default::default(),
event_bloom: [0u8; 256],
generator_bls_pubkey: Default::default(),
txroot: Default::default(),
faultroot: Default::default(),
gas_limit: Default::default(),
iteration: Default::default(),
prev_block_cert: Default::default(),
failed_iterations: Default::default(),
hash: Default::default(),
signature: Default::default(),
att: Default::default(),
}
}
}

impl Header {
pub fn size(&self) -> io::Result<usize> {
let mut buf = vec![];
Expand All @@ -68,7 +92,7 @@ impl std::fmt::Debug for Header {
.field("prev_block_hash", &to_str(&self.prev_block_hash))
.field("seed", &to_str(self.seed.inner()))
.field("state_hash", &to_str(&self.state_hash))
.field("event_hash", &to_str(&self.event_hash))
.field("event_hash", &to_str(&self.event_bloom))
.field("gen_bls_pubkey", &to_str(self.generator_bls_pubkey.inner()))
.field("gas_limit", &self.gas_limit)
.field("hash", &to_str(&self.hash))
Expand Down Expand Up @@ -103,7 +127,7 @@ impl Header {
w.write_all(self.seed.inner())?;

w.write_all(&self.state_hash)?;
w.write_all(&self.event_hash)?;
w.write_all(&self.event_bloom)?;
w.write_all(self.generator_bls_pubkey.inner())?;
w.write_all(&self.txroot)?;
w.write_all(&self.faultroot)?;
Expand Down Expand Up @@ -151,7 +175,7 @@ impl Header {
generator_bls_pubkey: PublicKeyBytes(generator_bls_pubkey),
iteration,
state_hash,
event_hash,
event_bloom: event_hash,
txroot,
faultroot,
hash: [0; 32],
Expand Down
2 changes: 1 addition & 1 deletion node-data/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1560,7 +1560,7 @@ mod tests {
seed: ledger::Seed::from([2; 48]),
generator_bls_pubkey: bls::PublicKeyBytes([5; 96]),
state_hash: [4; 32],
event_hash: [5; 32],
event_bloom: [5; 256],
hash: [6; 32],
txroot: [7; 32],
faultroot: [8; 32],
Expand Down
2 changes: 1 addition & 1 deletion node/src/chain/acceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ impl<DB: database::DB, VM: vm::VMExecution, N: Network> Acceptor<N, DB, VM> {
est_elapsed_time = start.elapsed();

assert_eq!(header.state_hash, verification_output.state_root);
assert_eq!(header.event_hash, verification_output.event_hash);
assert_eq!(header.event_bloom, verification_output.event_bloom);

let rolling_results =
self.rolling_finality::<DB>(pni, blk, db, &mut events)?;
Expand Down
194 changes: 194 additions & 0 deletions rusk/src/lib/bloom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use blake3::{Hasher, OUT_LEN};
use dusk_bytes::Serializable;
use execution_core::Event;

const BLOOM_BYTE_LEN: usize = 256;

/// A 2048 bit bloom filter.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Bloom([u8; BLOOM_BYTE_LEN]);

impl Bloom {
/// Create a new, empty, bloom.
pub const fn new() -> Self {
Self([0; BLOOM_BYTE_LEN])
}

/// Instantiate a new handle for IUF-style adding members, and membership
/// checks.
pub fn iuf(&mut self) -> BloomIuf {
BloomIuf {
hasher: Hasher::new(),
bloom: self,
}
}

/// Add a member to the bloom.
#[allow(unused)]
pub fn add(&mut self, bytes: impl AsRef<[u8]>) {
let mut iuf = self.iuf();
iuf.update(bytes);
iuf.add();
}

/// Add an event to the bloom.
#[allow(unused)]
pub fn add_event(&mut self, event: &Event) {
let mut iuf = self.iuf();
iuf.update(event.source);
iuf.update(&event.topic);
// We explicitly omit the `data` from the filter, since it can be
// obtained by other means. Like this the bloom is just used for
// querying if a specific event topic has been emitted by a given
// contract in a block. iuf.update(&event.data);
iuf.add();
}

/// Add some events to the bloom.
#[allow(unused)]
pub fn add_events<'a, I: IntoIterator<Item = &'a Event>>(
&mut self,
events: I,
) {
for event in events {
self.add_event(event);
}
}

/// Returns true if the bytes are contained in the bloom.
#[allow(unused)]
pub fn contains(&mut self, bytes: impl AsRef<[u8]>) -> bool {
let mut iuf = self.iuf();
iuf.update(bytes);
iuf.contains()
}

fn _add(&mut self, hash: &[u8; OUT_LEN]) {
let (i0, v0, i1, v1, i2, v2) = bloom_values(hash);
self.0[i0] |= v0;
self.0[i1] |= v1;
self.0[i2] |= v2;
}

fn _contains(&self, hash: &[u8; OUT_LEN]) -> bool {
let (i0, v0, i1, v1, i2, v2) = bloom_values(hash);
v0 == v0 & self.0[i0] && v1 == v1 & self.0[i1] && v2 == v2 & self.0[i2]
}
}

impl Default for Bloom {
fn default() -> Self {
Self::new()
}
}

impl From<Bloom> for [u8; BLOOM_BYTE_LEN] {
fn from(bloom: Bloom) -> Self {
bloom.0
}
}

impl Serializable<BLOOM_BYTE_LEN> for Bloom {
type Error = dusk_bytes::Error;

fn from_bytes(buf: &[u8; BLOOM_BYTE_LEN]) -> Result<Self, Self::Error>
where
Self: Sized,
{
Ok(Self(*buf))
}

fn to_bytes(&self) -> [u8; BLOOM_BYTE_LEN] {
self.0
}
}

fn bloom_values(hash: &[u8; OUT_LEN]) -> (usize, u8, usize, u8, usize, u8) {
let v0 = 1 << (hash[1] & 0x7);
let v1 = 1 << (hash[3] & 0x7);
let v2 = 1 << (hash[5] & 0x7);

let i0 =
BLOOM_BYTE_LEN - ((be16(hash[0], hash[1]) & 0x7ff) >> 3) as usize - 1;
let i1 =
BLOOM_BYTE_LEN - ((be16(hash[2], hash[3]) & 0x7ff) >> 3) as usize - 1;
let i2 =
BLOOM_BYTE_LEN - ((be16(hash[4], hash[5]) & 0x7ff) >> 3) as usize - 1;

(i0, v0, i1, v1, i2, v2)
}

fn be16(b0: u8, b1: u8) -> u16 {
(b1 as u16) | ((b0 as u16) << 8)
}

/// Allows for an Init/Update/Finalize API for adding to and checking the
/// contents of a [`Bloom`] filter.
pub struct BloomIuf<'a> {
hasher: Hasher,
bloom: &'a mut Bloom,
}

impl<'a> BloomIuf<'a> {
/// Updates the underlying hasher with the given `bytes`.
pub fn update(&mut self, bytes: impl AsRef<[u8]>) {
self.hasher.update(bytes.as_ref());
}

/// Finalize hashing and return true if the result is contained in the
/// [`Bloom`],
#[allow(unused)]
pub fn contains(self) -> bool {
let hash = self.hasher.finalize().into();
self.bloom._contains(&hash)
}

/// Finalize hashing and add the result to the underlying [`Bloom`],
/// returning the added bytes.
pub fn add(self) -> [u8; OUT_LEN] {
let hash = self.hasher.finalize().into();
self.bloom._add(&hash);
hash
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn membership() {
const INSERT_BYTES: &[u8; 43] =
b"The quick brown fox jumps over the lazy dog";
const NON_INSERT_BYTES: &[u8; 37] =
b"Two driven jocks help fax my big quiz";

let mut bloom = Bloom::new();

assert!(
!bloom.contains(INSERT_BYTES),
"bloom should not contain the bytes before adding"
);
assert!(
!bloom.contains(NON_INSERT_BYTES),
"bloom should not contain the bytes that are never added"
);

bloom.add(INSERT_BYTES);

assert!(
bloom.contains(INSERT_BYTES),
"bloom should contain the bytes after adding"
);
assert!(
!bloom.contains(NON_INSERT_BYTES),
"bloom should not contain the bytes that are never added"
);
}
}
2 changes: 1 addition & 1 deletion rusk/src/lib/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub enum Error {
CoinbaseDuskSpent(Dusk, Dusk),
/// Failed to produce proper state
#[cfg(feature = "chain")]
InconsistentState(dusk_consensus::operations::VerificationOutput),
InconsistentState(Box<dusk_consensus::operations::VerificationOutput>),
/// Other
Other(Box<dyn std::error::Error>),
/// Commit not found amongst existing commits
Expand Down
1 change: 1 addition & 0 deletions rusk/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#![feature(lazy_cell)]

mod bloom;
mod error;
pub mod gen_id;
pub mod http;
Expand Down
Loading

0 comments on commit ee5e359

Please sign in to comment.