diff --git a/crates/floresta-chain/src/pruned_utreexo/consensus.rs b/crates/floresta-chain/src/pruned_utreexo/consensus.rs index e53c006d..138e5388 100644 --- a/crates/floresta-chain/src/pruned_utreexo/consensus.rs +++ b/crates/floresta-chain/src/pruned_utreexo/consensus.rs @@ -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; @@ -25,12 +23,11 @@ use floresta_common::prelude::*; 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. @@ -70,37 +67,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 @@ -295,6 +261,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, @@ -308,56 +275,17 @@ impl Consensus { del_hashes: Vec, ) -> Result { let block_hash = block.block_hash(); - let mut leaf_hashes = Vec::new(); let del_hashes = del_hashes .iter() .map(|hash| BitcoinNodeHash::from(hash.as_byte_array())) .collect::>(); - // 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 = leaf_hashes - .iter() - .map(|&hash| BitcoinNodeHash::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 { diff --git a/crates/floresta-chain/src/pruned_utreexo/udata.rs b/crates/floresta-chain/src/pruned_utreexo/udata.rs index 8120ebfb..5d02fc50 100644 --- a/crates/floresta-chain/src/pruned_utreexo/udata.rs +++ b/crates/floresta-chain/src/pruned_utreexo/udata.rs @@ -283,9 +283,12 @@ impl From for UtreexoBlock { pub mod proof_util { use bitcoin::blockdata::script::Instruction; + use bitcoin::consensus::Encodable; use bitcoin::hashes::sha256; use bitcoin::hashes::Hash; use bitcoin::Amount; + use bitcoin::Block; + use bitcoin::BlockHash; use bitcoin::OutPoint; use bitcoin::PubkeyHash; use bitcoin::ScriptBuf; @@ -297,9 +300,12 @@ pub mod proof_util { use bitcoin::WScriptHash; use rustreexo::accumulator::node_hash::BitcoinNodeHash; use rustreexo::accumulator::proof::Proof; + use sha2::Digest; + use sha2::Sha512_256; use super::LeafData; use crate::prelude::*; + use crate::pruned_utreexo::consensus::UTREEXO_TAG_V1; use crate::pruned_utreexo::BlockchainInterface; use crate::CompactLeafData; use crate::ScriptPubkeyType; @@ -328,6 +334,83 @@ pub mod proof_util { }) } + 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 + } + + /// 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") + } + + pub fn get_block_adds( + block: &Block, + height: u32, + block_hash: BlockHash, + ) -> Vec { + let mut leaf_hashes = Vec::new(); + // 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 !is_unspendable(&output.script_pubkey) + && !block_inputs.contains(&(transaction.compute_txid(), i as u32)) + { + leaf_hashes.push(get_leaf_hashes(transaction, i as u32, height, block_hash)) + } + } + } + + // Convert the leaf hashes to NodeHashes used in Rustreexo + leaf_hashes + .iter() + .map(|&hash| BitcoinNodeHash::from(hash.as_byte_array())) + .collect() + } + #[allow(clippy::type_complexity)] pub fn process_proof( udata: &UData, @@ -378,7 +461,10 @@ pub mod proof_util { Ok((proof, hashes, inputs)) } - fn reconstruct_script_pubkey(leaf: &CompactLeafData, input: &TxIn) -> Result { + pub fn reconstruct_script_pubkey( + leaf: &CompactLeafData, + input: &TxIn, + ) -> Result { match &leaf.spk_ty { ScriptPubkeyType::Other(spk) => Ok(ScriptBuf::from(spk.clone().into_vec())), ScriptPubkeyType::PubKeyHash => { diff --git a/crates/floresta-wire/src/p2p_wire/mempool.rs b/crates/floresta-wire/src/p2p_wire/mempool.rs index 3b8730a0..192b7868 100644 --- a/crates/floresta-wire/src/p2p_wire/mempool.rs +++ b/crates/floresta-wire/src/p2p_wire/mempool.rs @@ -6,52 +6,259 @@ use std::collections::HashMap; use std::time::Duration; use std::time::Instant; +use bitcoin::block::Header; +use bitcoin::hashes::Hash; +use bitcoin::Amount; use bitcoin::Block; +use bitcoin::OutPoint; use bitcoin::Transaction; +use bitcoin::TxOut; use bitcoin::Txid; +use floresta_chain::proof_util; +use floresta_chain::pruned_utreexo::BlockchainInterface; +use floresta_chain::CompactLeafData; +use floresta_chain::LeafData; +use rustreexo::accumulator::node_hash::BitcoinNodeHash; +use rustreexo::accumulator::pollard::Pollard; +use rustreexo::accumulator::pollard::PollardAddition; +use rustreexo::accumulator::proof::Proof; + /// Holds the transactions that we broadcasted and are still in the mempool. #[derive(Debug, Default)] -pub struct Mempool(HashMap); +pub struct Mempool { + /// A list of all transactions we currently have in the mempool. + /// + /// Transactions are kept as a map of their transaction id to the transaction itself, we + /// also keep track of when we added the transaction to the mempool to be able to remove + /// stale transactions. + transactions: HashMap, + /// How much memory (in bytes) does the mempool currently use. + mempool_size: usize, + /// The maximum size of the mempool in bytes. + max_mempool_size: usize, + /// The accumulator that we use to verify proofs. + /// + /// This is a Pollard, a light version of a Utreexo accumulator that allows you to hold some + /// leaves, but not all of them. We use this to keep track of mempool proofs so we don't need + /// to re-download them. + acc: Pollard, + /// A map of all the prevouts that are being spent by transactions in the mempool. + /// + /// Since we don't have a full UTXO set, we need to keep track of the outputs that are being + /// spent in order to perform validation and fee calculation. + prevouts: HashMap, + /// A queue of transaction we know about, but don't have a proof for + queue: Vec, +} + +unsafe impl Send for Mempool {} + +#[derive(Debug, Clone)] +/// An error returned when we try to add a transaction to the mempool. +pub enum AcceptToMempoolError { + /// The proof provided is invalid. + InvalidProof, + /// The transaction is trying to spend an output that we don't have. + InvalidPrevout, + /// Memory usage is too high. + MemoryUsageTooHigh, + PrevoutNotFound, + Rustreexo(String), +} + +pub struct MempoolProof { + pub proof: Proof, + pub target_hashes: Vec, + pub leaves: Vec, +} impl Mempool { - pub fn new() -> Mempool { - Mempool(HashMap::new()) - } - /// Find all transactions that are in the mempool and were included in the given block. - /// Remove them from the mempool and return them. - pub fn consume_block(&mut self, block: &Block) -> Vec { - if self.0.is_empty() { - return Vec::new(); + pub fn new(acc: Pollard, max_mempool_size: usize) -> Mempool { + Mempool { + transactions: HashMap::new(), + prevouts: HashMap::new(), + queue: Vec::new(), + mempool_size: 0, + max_mempool_size, + acc, + } + } + + /// List transactions we are pending to process. + /// + /// Usually, we don't have a proof for these transactions, so we can't add them to the mempool, + /// a wire implementation should call this method and try to get a proof for these + /// transactions. + pub fn list_unprocessed(&self) -> Vec { + self.queue.clone() + } + + /// List all transactions we've accepted to the mempool. + /// + /// This won't count transactions that are still in the queue. + pub fn list_mempool(&self) -> Vec { + self.transactions.keys().cloned().collect() + } + + pub fn get_prevouts(&self, tx: &Transaction) -> Vec { + tx.input + .iter() + .filter_map(|input| { + let leaf_data = self.prevouts.get(&input.previous_output)?; + let script_pubkey = proof_util::reconstruct_script_pubkey(leaf_data, input).ok()?; + Some(TxOut { + value: Amount::from_sat(leaf_data.amount), + script_pubkey, + }) + }) + .collect() + } + + pub fn try_prove( + &self, + tx: &Transaction, + chain: &impl BlockchainInterface, + ) -> Result { + let mut target_hashes = Vec::new(); + let mut leaves = Vec::new(); + for input in tx.input.iter() { + let prevout = self + .prevouts + .get(&input.previous_output) + .ok_or(AcceptToMempoolError::PrevoutNotFound)?; + + let block_hash = chain.get_block_hash(prevout.header_code >> 1).unwrap(); + let leaf_data: LeafData = + proof_util::reconstruct_leaf_data(prevout, input, block_hash) + .map_err(|_| AcceptToMempoolError::InvalidPrevout)?; + + let hash = leaf_data._get_leaf_hashes(); + let hash = BitcoinNodeHash::Some(hash.to_byte_array()); + + target_hashes.push(hash); + leaves.push(prevout.clone()); } - let mut delta = Vec::new(); - for tx in block.txdata.iter() { - if self.0.contains_key(&tx.compute_txid()) { - delta.push(self.0.remove(&tx.compute_txid())); + + let proof = self + .acc + .batch_proof(&target_hashes) + .map_err(AcceptToMempoolError::Rustreexo)?; + + Ok(MempoolProof { + proof, + target_hashes, + leaves, + }) + } + + /// Returns a list of transactions that are in the mempool up to the block weight limit. + /// + /// You can use this method to produce a template and mine a block. + pub fn get_block_template(&self, block_header: Header) -> Block { + // add transactions until we reach the block limit + let mut size = 0; + let mut txs = Vec::new(); + for (_, (tx, _)) in self.transactions.iter() { + let tx_size = tx.weight().to_wu(); + if size + tx_size > 4_000_000 { + break; } + + size += tx_size; + txs.push(tx.clone()); } - delta.into_iter().flat_map(|tx| Some(tx?.0)).collect() + + Block { + header: block_header, + txdata: txs, + } + } + + /// Consume a block and remove all transactions that were included in it. + /// + /// This method will remove all transactions that were included in the block from the mempool, + /// update the local accumulator and return the transactions that were included in the block. + pub fn consume_block( + &mut self, + block: &Block, + proof: Proof, + adds: &[PollardAddition], + del_hashes: &[BitcoinNodeHash], + ) -> Result, String> { + if self.transactions.is_empty() { + return Ok(Vec::new()); + } + + self.acc.modify(adds, del_hashes, proof)?; + + Ok(block + .txdata + .iter() + .filter_map(|tx| { + self.transactions + .remove(&tx.compute_txid()) + .map(|(tx, _)| tx) + }) + .collect()) + } + + pub fn get_block_proof( + &self, + _adds: &[PollardAddition], + _del_hashes: &[BitcoinNodeHash], + ) -> Result { + todo!() + } + + pub fn accept_to_mempool_no_acc(&mut self, transaction: Transaction) { + let tx_size = transaction.total_size(); + self.mempool_size += tx_size; + self.transactions + .insert(transaction.compute_txid(), (transaction, Instant::now())); } + /// Add a transaction to the mempool. - pub fn accept_to_mempool(&mut self, transaction: Transaction) { - self.0 + pub fn accept_to_mempool( + &mut self, + transaction: Transaction, + proof: Proof, + prevouts: &[(OutPoint, CompactLeafData)], + del_hashes: &[BitcoinNodeHash], + remembers: &[u64], + ) -> Result<(), AcceptToMempoolError> { + let tx_size = transaction.total_size(); + + if self.mempool_size + tx_size > self.max_mempool_size { + return Err(AcceptToMempoolError::MemoryUsageTooHigh); + } + + self.acc + .verify_and_ingest(proof, del_hashes, remembers) + .map_err(|_| AcceptToMempoolError::InvalidProof)?; + + self.prevouts.extend(prevouts.iter().cloned()); + self.transactions .insert(transaction.compute_txid(), (transaction, Instant::now())); + + Ok(()) } + /// Get a transaction from the mempool. pub fn get_from_mempool(&self, id: &Txid) -> Option<&Transaction> { - if let Some(tx) = self.0.get(id) { - return Some(&tx.0); - } - None + self.transactions.get(id).map(|(tx, _)| tx) } + /// Get all transactions that were in the mempool for more than 1 hour. pub fn get_stale(&mut self) -> Vec { - let mut stale = Vec::new(); - for (txid, transaction) in self.0.iter_mut() { - if transaction.1.elapsed() > Duration::from_secs(60 * 60) { - transaction.1 = Instant::now(); - stale.push(*txid); - } - } - stale + self.transactions + .iter() + .filter_map(|(id, (_, time))| { + if time.elapsed() > Duration::from_secs(3600) { + Some(*id) + } else { + None + } + }) + .collect() } } diff --git a/crates/floresta-wire/src/p2p_wire/node.rs b/crates/floresta-wire/src/p2p_wire/node.rs index 1f161485..4dad1733 100644 --- a/crates/floresta-wire/src/p2p_wire/node.rs +++ b/crates/floresta-wire/src/p2p_wire/node.rs @@ -9,7 +9,6 @@ use std::net::SocketAddr; use std::ops::Deref; use std::ops::DerefMut; use std::sync::Arc; -use std::sync::Mutex; use std::time::Duration; use std::time::Instant; use std::time::SystemTime; @@ -39,7 +38,7 @@ use tokio::spawn; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; -use tokio::sync::RwLock; +use tokio::sync::Mutex; use tokio::time::timeout; use super::address_man::AddressMan; @@ -48,6 +47,7 @@ use super::address_man::LocalAddress; use super::error::AddrParseError; use super::error::WireError; use super::mempool::Mempool; +use super::mempool::MempoolProof; use super::node_context::NodeContext; use super::node_interface::NodeInterface; use super::node_interface::PeerInfo; @@ -134,7 +134,7 @@ impl Default for RunningNode { last_feeler: Instant::now(), last_address_rearrange: Instant::now(), user_requests: Arc::new(NodeInterface { - requests: Mutex::new(Vec::new()), + requests: std::sync::Mutex::new(Vec::new()), }), last_invs: HashMap::default(), inflight_filters: BTreeMap::new(), @@ -146,7 +146,7 @@ pub struct NodeCommon { // 1. Core Blockchain and Transient Data pub(crate) chain: Chain, pub(crate) blocks: HashMap, - pub(crate) mempool: Arc>, + pub(crate) mempool: Arc>, pub(crate) block_filters: Option>>, pub(crate) last_filter: BlockHash, @@ -217,7 +217,7 @@ where pub fn new( config: UtreexoNodeConfig, chain: Chain, - mempool: Arc>, + mempool: Arc>, block_filters: Option>>, ) -> Result { let (node_tx, node_rx) = unbounded_channel(); @@ -735,16 +735,54 @@ where for transaction in transactions { let txid = transaction.compute_txid(); - self.mempool.write().await.accept_to_mempool(transaction); - peer.channel - .send(NodeRequest::BroadcastTransaction(txid)) - .map_err(WireError::ChannelSend)?; - } - let stale = self.mempool.write().await.get_stale(); - for tx in stale { - peer.channel - .send(NodeRequest::BroadcastTransaction(tx)) - .map_err(WireError::ChannelSend)?; + let mut mempool = self.mempool.lock().await; + + if self.network == Network::Regtest { + match mempool.try_prove(&transaction, &self.chain) { + Ok(proof) => { + let MempoolProof { + proof, + target_hashes, + leaves, + } = proof; + + let leaves = transaction + .input + .iter() + .cloned() + .map(|input| input.previous_output) + .zip(leaves.into_iter()) + .collect::>(); + + let targets = proof.targets.clone(); + try_and_log!(mempool.accept_to_mempool( + transaction, + proof, + &leaves, + &target_hashes, + &targets, + )); + } + Err(e) => { + log::error!( + "Could not prove tx {} because: {:?}", + transaction.compute_txid(), + e + ); + } + } + + peer.channel + .send(NodeRequest::BroadcastTransaction(txid)) + .map_err(WireError::ChannelSend)?; + } + + let stale = self.mempool.lock().await.get_stale(); + for tx in stale { + peer.channel + .send(NodeRequest::BroadcastTransaction(tx)) + .map_err(WireError::ChannelSend)?; + } } } Ok(()) @@ -906,7 +944,7 @@ where address: LocalAddress, requests_rx: UnboundedReceiver, peer_id_count: u32, - mempool: Arc>, + mempool: Arc>, network: bitcoin::Network, node_tx: UnboundedSender, user_agent: String, @@ -949,7 +987,7 @@ where pub(crate) async fn open_proxy_connection( proxy: SocketAddr, kind: ConnectionKind, - mempool: Arc>, + mempool: Arc>, network: bitcoin::Network, node_tx: UnboundedSender, peer_id: usize, diff --git a/crates/floresta-wire/src/p2p_wire/peer.rs b/crates/floresta-wire/src/p2p_wire/peer.rs index 2c4b18b2..a94f025b 100644 --- a/crates/floresta-wire/src/p2p_wire/peer.rs +++ b/crates/floresta-wire/src/p2p_wire/peer.rs @@ -36,7 +36,7 @@ use tokio::spawn; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; -use tokio::sync::RwLock; +use tokio::sync::Mutex; use self::peer_utils::make_pong; use super::mempool::Mempool; @@ -148,7 +148,7 @@ pub fn create_tcp_stream_actor( } pub struct Peer { - mempool: Arc>, + mempool: Arc>, network: Network, blocks_only: bool, services: ServiceFlags, @@ -582,13 +582,13 @@ impl Peer { pub async fn handle_get_data(&mut self, inv: Inventory) -> Result<()> { match inv { Inventory::WitnessTransaction(txid) => { - let tx = self.mempool.read().await.get_from_mempool(&txid).cloned(); + let tx = self.mempool.lock().await.get_from_mempool(&txid).cloned(); if let Some(tx) = tx { self.write(NetworkMessage::Tx(tx)).await?; } } Inventory::Transaction(txid) => { - let tx = self.mempool.read().await.get_from_mempool(&txid).cloned(); + let tx = self.mempool.lock().await.get_from_mempool(&txid).cloned(); if let Some(tx) = tx { self.write(NetworkMessage::Tx(tx)).await?; } @@ -601,7 +601,7 @@ impl Peer { #[allow(clippy::too_many_arguments)] pub async fn create_peer( id: u32, - mempool: Arc>, + mempool: Arc>, network: Network, node_tx: UnboundedSender, node_requests: UnboundedReceiver, diff --git a/crates/floresta-wire/src/p2p_wire/running_node.rs b/crates/floresta-wire/src/p2p_wire/running_node.rs index e826879a..dee977ee 100644 --- a/crates/floresta-wire/src/p2p_wire/running_node.rs +++ b/crates/floresta-wire/src/p2p_wire/running_node.rs @@ -9,6 +9,7 @@ use std::time::Duration; use std::time::Instant; use bitcoin::bip158::BlockFilter; +use bitcoin::hashes::Hash; use bitcoin::p2p::address::AddrV2; use bitcoin::p2p::address::AddrV2Message; use bitcoin::p2p::message_blockdata::Inventory; @@ -25,6 +26,8 @@ use log::debug; use log::error; use log::info; use log::warn; +use rustreexo::accumulator::node_hash::BitcoinNodeHash; +use rustreexo::accumulator::pollard::PollardAddition; use rustreexo::accumulator::stump::Stump; use tokio::sync::RwLock; use tokio::time::timeout; @@ -666,9 +669,10 @@ where let (proof, del_hashes, inputs) = floresta_chain::proof_util::process_proof(udata, &block.block.txdata, &self.chain)?; - if let Err(e) = self - .chain - .connect_block(&block.block, proof, inputs, del_hashes) + + if let Err(e) = + self.chain + .connect_block(&block.block, proof.clone(), inputs, del_hashes.clone()) { error!("Invalid block received by peer {} reason: {:?}", peer, e); if let BlockchainError::BlockValidation(e) = e { @@ -712,7 +716,39 @@ where } if !self.chain.is_in_idb() { - let mempool_delta = self.mempool.write().await.consume_block(&block.block); + let del_hashes: Vec = del_hashes + .iter() + .map(|hash| BitcoinNodeHash::from(hash.as_byte_array())) + .collect(); + + let block_height = self + .chain + .get_block_height(&block.block.block_hash())? + .unwrap(); + + let block_hash = block.block.block_hash(); + + let adds = floresta_chain::proof_util::get_block_adds( + &block.block, + block_height, + block_hash, + ); + + let adds: Vec> = adds + .into_iter() + .map(|add| PollardAddition { + remember: true, + hash: add, + }) + .collect(); + + let mempool_delta = self + .mempool + .lock() + .await + .consume_block(&block.block, proof, &adds, &del_hashes) + .unwrap_or(Vec::new()); + debug!( "Block {} accepted, confirmed transactions: {:?}", block.block.block_hash(), @@ -726,6 +762,7 @@ where Ok(_next_block) => next_block = _next_block, Err(_) => break, } + debug!("accepted block {}", block.block.block_hash()); } diff --git a/florestad/src/florestad.rs b/florestad/src/florestad.rs index daa417cd..85fcd2ad 100644 --- a/florestad/src/florestad.rs +++ b/florestad/src/florestad.rs @@ -45,6 +45,7 @@ use log::warn; use log::Record; #[cfg(feature = "metrics")] use metrics; +use rustreexo::accumulator::pollard::Pollard; use tokio::net::TcpListener; use tokio::sync::RwLock; use tokio::task; @@ -398,11 +399,12 @@ impl Florestad { user_agent: self.config.user_agent.clone(), }; + let acc = Pollard::new(); // Chain Provider (p2p) let chain_provider = UtreexoNode::new( config, blockchain_state.clone(), - Arc::new(tokio::sync::RwLock::new(Mempool::new())), + Arc::new(tokio::sync::Mutex::new(Mempool::new(acc, 300_000_000))), cfilters.clone(), ) .expect("Could not create a chain provider");