Skip to content

Commit

Permalink
Feat: Create some very basic mempool stuff (#346)
Browse files Browse the repository at this point in the history
* bump rustreexo

* mempool: cache proofs and inputs for mempool

This commit adds a partial forest to mempool, and the ability to verify
proofs.

* Mempool: make proving work, add a bunch of tests

Now, if you call `get_block_proof` for a block whose outputs we have in
our mempool, we'll get a proof for it.

This commit also adds some tests for mempool stuff
  • Loading branch information
Davidson-Souza authored Feb 10, 2025
1 parent 879e263 commit 7083de4
Show file tree
Hide file tree
Showing 21 changed files with 1,276 additions and 164 deletions.
6 changes: 4 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/floresta-chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ categories = ["cryptography::cryptocurrencies", "database"]
crate-type = ["cdylib", "rlib"]

[dependencies]
rustreexo = "0.3.0"
rustreexo = "0.4"
sha2 = "^0.10.6"
log = "0.4"
kv = "0.24.0"
Expand Down
8 changes: 4 additions & 4 deletions crates/floresta-chain/src/pruned_utreexo/chain_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use log::trace;
use log::warn;
#[cfg(feature = "metrics")]
use metrics;
use rustreexo::accumulator::node_hash::NodeHash;
use rustreexo::accumulator::node_hash::BitcoinNodeHash;
use rustreexo::accumulator::proof::Proof;
use rustreexo::accumulator::stump::Stump;
use spin::RwLock;
Expand Down Expand Up @@ -634,7 +634,7 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
assert_eq!(acc.len() % 32, 0);
while acc.len() >= 32 {
let root = acc.drain(0..32).collect::<Vec<u8>>();
let root = NodeHash::from(&*root);
let root = BitcoinNodeHash::from(&*root);
roots.push(root);
}
Stump { leaves, roots }
Expand Down Expand Up @@ -831,7 +831,7 @@ impl<PersistedState: ChainStore> BlockchainInterface for ChainState<PersistedSta
// verify the proof
let del_hashes = del_hashes
.iter()
.map(|hash| NodeHash::from(hash.as_byte_array()))
.map(|hash| BitcoinNodeHash::from(hash.as_byte_array()))
.collect::<Vec<_>>();

if !acc.verify(&proof, &del_hashes)? {
Expand Down Expand Up @@ -1170,7 +1170,7 @@ impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedSta
Ok(())
}

fn get_root_hashes(&self) -> Vec<NodeHash> {
fn get_root_hashes(&self) -> Vec<BitcoinNodeHash> {
let inner = read_lock!(self);
inner.acc.roots.clone()
}
Expand Down
6 changes: 3 additions & 3 deletions crates/floresta-chain/src/pruned_utreexo/chainparams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bitcoin::p2p::ServiceFlags;
use bitcoin::params::Params;
use bitcoin::Block;
use bitcoin::BlockHash;
use rustreexo::accumulator::node_hash::NodeHash;
use rustreexo::accumulator::node_hash::BitcoinNodeHash;

use crate::prelude::*;
use crate::AssumeValidArg;
Expand Down Expand Up @@ -74,7 +74,7 @@ pub struct AssumeUtreexoValue {
/// Same as block_hash, but in height
pub height: u32,
/// The roots of the Utreexo accumulator at this block
pub roots: Vec<NodeHash>,
pub roots: Vec<BitcoinNodeHash>,
/// The number of leaves in the Utreexo accumulator at this block
pub leaves: u64,
}
Expand Down Expand Up @@ -109,7 +109,7 @@ impl ChainParams {
"972ea2c7472c22e4eab49e9c2db5757a048b271b6251883ce89ccfeaa38b47ab",
]
.into_iter()
.map(|x| NodeHash::from_str(x).unwrap())
.map(|x| BitcoinNodeHash::from_str(x).unwrap())
.collect(),
leaves: 2587882501,
},
Expand Down
86 changes: 7 additions & 79 deletions crates/floresta-chain/src/pruned_utreexo/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ extern crate alloc;
use core::ffi::c_uint;

use bitcoin::block::Header as BlockHeader;
use bitcoin::consensus::Encodable;
use bitcoin::hashes::sha256;
use bitcoin::hashes::Hash;
use bitcoin::Block;
use bitcoin::BlockHash;
use bitcoin::CompactTarget;
use bitcoin::OutPoint;
use bitcoin::ScriptBuf;
Expand All @@ -21,15 +19,14 @@ use bitcoin::TxIn;
use bitcoin::TxOut;
use bitcoin::Txid;
use floresta_common::prelude::*;
use rustreexo::accumulator::node_hash::NodeHash;
use rustreexo::accumulator::node_hash::BitcoinNodeHash;
use rustreexo::accumulator::proof::Proof;
use rustreexo::accumulator::stump::Stump;
use sha2::Digest;
use sha2::Sha512_256;

use super::chainparams::ChainParams;
use super::error::BlockValidationErrors;
use super::error::BlockchainError;
use super::udata;
use crate::TransactionError;

/// The value of a single coin in satoshis.
Expand Down Expand Up @@ -69,37 +66,6 @@ impl Consensus {
subsidy
}

/// Returns the hash of a leaf node in the utreexo accumulator.
#[inline]
fn get_leaf_hashes(
transaction: &Transaction,
vout: u32,
height: u32,
block_hash: BlockHash,
) -> sha256::Hash {
let header_code = height << 1;

let mut ser_utxo = Vec::new();
let utxo = transaction.output.get(vout as usize).unwrap();
utxo.consensus_encode(&mut ser_utxo).unwrap();
let header_code = if transaction.is_coinbase() {
header_code | 1
} else {
header_code
};

let leaf_hash = Sha512_256::new()
.chain_update(UTREEXO_TAG_V1)
.chain_update(UTREEXO_TAG_V1)
.chain_update(block_hash)
.chain_update(transaction.compute_txid())
.chain_update(vout.to_le_bytes())
.chain_update(header_code.to_le_bytes())
.chain_update(ser_utxo)
.finalize();
sha256::Hash::from_slice(leaf_hash.as_slice())
.expect("parent_hash: Engines shouldn't be Err")
}
/// Verify if all transactions in a block are valid. Here we check the following:
/// - The block must contain at least one transaction, and this transaction must be coinbase
/// - The first transaction in the block must be coinbase
Expand Down Expand Up @@ -279,6 +245,7 @@ impl Consensus {
CompactTarget::from_next_work_required(first_block.bits, actual_timespan as u64, params)
.into()
}

/// Updates our accumulator with the new block. This is done by calculating the new
/// root hash of the accumulator, and then verifying the proof of inclusion of the
/// deleted nodes. If the proof is valid, we return the new accumulator. Otherwise,
Expand All @@ -292,56 +259,17 @@ impl Consensus {
del_hashes: Vec<sha256::Hash>,
) -> Result<Stump, BlockchainError> {
let block_hash = block.block_hash();
let mut leaf_hashes = Vec::new();
let del_hashes = del_hashes
.iter()
.map(|hash| NodeHash::from(hash.as_byte_array()))
.map(|hash| BitcoinNodeHash::from(hash.as_byte_array()))
.collect::<Vec<_>>();
// Get inputs from the block, we'll need this HashSet to check if an output is spent
// in the same block. If it is, we don't need to add it to the accumulator.
let mut block_inputs = HashSet::new();
for transaction in block.txdata.iter() {
for input in transaction.input.iter() {
block_inputs.insert((input.previous_output.txid, input.previous_output.vout));
}
}

// Get all leaf hashes that will be added to the accumulator
for transaction in block.txdata.iter() {
for (i, output) in transaction.output.iter().enumerate() {
if !Self::is_unspendable(&output.script_pubkey)
&& !block_inputs.contains(&(transaction.compute_txid(), i as u32))
{
leaf_hashes.push(Self::get_leaf_hashes(
transaction,
i as u32,
height,
block_hash,
))
}
}
}
// Convert the leaf hashes to NodeHashes used in Rustreexo
let hashes: Vec<NodeHash> = leaf_hashes
.iter()
.map(|&hash| NodeHash::from(hash.as_byte_array()))
.collect();
let adds = udata::proof_util::get_block_adds(block, height, block_hash);

// Update the accumulator
let acc = acc.modify(&hashes, &del_hashes, &proof)?.0;
let acc = acc.modify(&adds, &del_hashes, &proof)?.0;
Ok(acc)
}

fn is_unspendable(script: &ScriptBuf) -> bool {
if script.len() > 10_000 {
return true;
}

if !script.is_empty() && script.as_bytes()[0] == 0x6a {
return true;
}

false
}
}
#[cfg(test)]
mod tests {
Expand Down
6 changes: 3 additions & 3 deletions crates/floresta-chain/src/pruned_utreexo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use bitcoin::BlockHash;
use bitcoin::OutPoint;
use bitcoin::Transaction;
use bitcoin::TxOut;
use rustreexo::accumulator::node_hash::NodeHash;
use rustreexo::accumulator::node_hash::BitcoinNodeHash;
use rustreexo::accumulator::proof::Proof;
use rustreexo::accumulator::stump::Stump;

Expand Down Expand Up @@ -132,7 +132,7 @@ pub trait UpdatableChainstate {
/// marked as invalid.
fn mark_block_as_valid(&self, block: BlockHash) -> Result<(), BlockchainError>;
/// Returns the root hashes of our utreexo forest
fn get_root_hashes(&self) -> Vec<NodeHash>;
fn get_root_hashes(&self) -> Vec<BitcoinNodeHash>;
/// Returns a partial chainstate from a range of blocks.
///
/// [PartialChainState] is a simplified version of `ChainState` that is used during IBD.
Expand Down Expand Up @@ -223,7 +223,7 @@ impl<T: UpdatableChainstate> UpdatableChainstate for Arc<T> {
T::accept_header(self, header)
}

fn get_root_hashes(&self) -> Vec<NodeHash> {
fn get_root_hashes(&self) -> Vec<BitcoinNodeHash> {
T::get_root_hashes(self)
}

Expand Down
9 changes: 5 additions & 4 deletions crates/floresta-chain/src/pruned_utreexo/partial_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
//! threads, as long as the origin thread gives away the ownership.
use bitcoin::BlockHash;
use floresta_common::prelude::*;
use rustreexo::accumulator::node_hash::BitcoinNodeHash;
extern crate alloc;

use core::cell::UnsafeCell;
Expand Down Expand Up @@ -318,7 +319,7 @@ impl UpdatableChainstate for PartialChainState {
.process_block(block, proof, inputs, del_hashes)
}

fn get_root_hashes(&self) -> Vec<rustreexo::accumulator::node_hash::NodeHash> {
fn get_root_hashes(&self) -> Vec<BitcoinNodeHash> {
self.inner().current_acc.roots.clone()
}

Expand Down Expand Up @@ -506,7 +507,7 @@ mod tests {
use bitcoin::block::Header;
use bitcoin::consensus::deserialize;
use bitcoin::Block;
use rustreexo::accumulator::node_hash::NodeHash;
use rustreexo::accumulator::node_hash::BitcoinNodeHash;
use rustreexo::accumulator::proof::Proof;
use rustreexo::accumulator::stump::Stump;

Expand Down Expand Up @@ -641,7 +642,7 @@ mod tests {
"bedb648c9a3c5741660f926c1552d83ebb4cb1842cca6855b6d1089bb4951ce1",
]
.iter()
.map(|hash| NodeHash::from_str(hash).unwrap())
.map(|hash| BitcoinNodeHash::from_str(hash).unwrap())
.collect();

let acc2 = Stump { roots, leaves: 100 };
Expand Down Expand Up @@ -682,7 +683,7 @@ mod tests {
"1864a4982532447dcb3d9a5d2fea9f8ed4e3b1e759d55b8a427fb599fed0c302",
]
.iter()
.map(|x| NodeHash::from(hex::decode(x).unwrap().as_slice()))
.map(|x| BitcoinNodeHash::from(hex::decode(x).unwrap().as_slice()))
.collect::<Vec<_>>();

let expected_acc: Stump = Stump { leaves: 150, roots };
Expand Down
Loading

0 comments on commit 7083de4

Please sign in to comment.