mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-23 03:59:31 +00:00
check txpool on incoming block
This commit is contained in:
parent
93fb3c657c
commit
5ab5b062fd
4 changed files with 77 additions and 27 deletions
|
@ -15,6 +15,8 @@ use tower::{Service, ServiceExt};
|
||||||
use cuprate_blockchain::service::BlockchainReadHandle;
|
use cuprate_blockchain::service::BlockchainReadHandle;
|
||||||
use cuprate_consensus::transactions::new_tx_verification_data;
|
use cuprate_consensus::transactions::new_tx_verification_data;
|
||||||
use cuprate_helper::cast::usize_to_u64;
|
use cuprate_helper::cast::usize_to_u64;
|
||||||
|
use cuprate_txpool::service::interface::{TxpoolReadRequest, TxpoolReadResponse};
|
||||||
|
use cuprate_txpool::service::TxpoolReadHandle;
|
||||||
use cuprate_types::{
|
use cuprate_types::{
|
||||||
blockchain::{BlockchainReadRequest, BlockchainResponse},
|
blockchain::{BlockchainReadRequest, BlockchainResponse},
|
||||||
Chain,
|
Chain,
|
||||||
|
@ -38,7 +40,7 @@ pub enum IncomingBlockError {
|
||||||
///
|
///
|
||||||
/// The inner values are the block hash and the indexes of the missing txs in the block.
|
/// The inner values are the block hash and the indexes of the missing txs in the block.
|
||||||
#[error("Unknown transactions in block.")]
|
#[error("Unknown transactions in block.")]
|
||||||
UnknownTransactions([u8; 32], Vec<u64>),
|
UnknownTransactions([u8; 32], Vec<usize>),
|
||||||
/// We are missing the block's parent.
|
/// We are missing the block's parent.
|
||||||
#[error("The block has an unknown parent.")]
|
#[error("The block has an unknown parent.")]
|
||||||
Orphan,
|
Orphan,
|
||||||
|
@ -59,8 +61,9 @@ pub enum IncomingBlockError {
|
||||||
/// - the block's parent is unknown
|
/// - the block's parent is unknown
|
||||||
pub async fn handle_incoming_block(
|
pub async fn handle_incoming_block(
|
||||||
block: Block,
|
block: Block,
|
||||||
given_txs: Vec<Transaction>,
|
mut given_txs: HashMap<[u8; 32], Transaction>,
|
||||||
blockchain_read_handle: &mut BlockchainReadHandle,
|
blockchain_read_handle: &mut BlockchainReadHandle,
|
||||||
|
txpool_read_handle: &mut TxpoolReadHandle,
|
||||||
) -> Result<IncomingBlockOk, IncomingBlockError> {
|
) -> Result<IncomingBlockOk, IncomingBlockError> {
|
||||||
/// A [`HashSet`] of block hashes that the blockchain manager is currently handling.
|
/// A [`HashSet`] of block hashes that the blockchain manager is currently handling.
|
||||||
///
|
///
|
||||||
|
@ -72,7 +75,12 @@ pub async fn handle_incoming_block(
|
||||||
/// which are also more expensive than `Mutex`s.
|
/// which are also more expensive than `Mutex`s.
|
||||||
static BLOCKS_BEING_HANDLED: LazyLock<Mutex<HashSet<[u8; 32]>>> =
|
static BLOCKS_BEING_HANDLED: LazyLock<Mutex<HashSet<[u8; 32]>>> =
|
||||||
LazyLock::new(|| Mutex::new(HashSet::new()));
|
LazyLock::new(|| Mutex::new(HashSet::new()));
|
||||||
// FIXME: we should look in the tx-pool for txs when that is ready.
|
|
||||||
|
if given_txs.len() > block.transactions.len() {
|
||||||
|
return Err(IncomingBlockError::InvalidBlock(anyhow::anyhow!(
|
||||||
|
"Too many transactions given for block"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
if !block_exists(block.header.previous, blockchain_read_handle)
|
if !block_exists(block.header.previous, blockchain_read_handle)
|
||||||
.await
|
.await
|
||||||
|
@ -90,23 +98,32 @@ pub async fn handle_incoming_block(
|
||||||
return Ok(IncomingBlockOk::AlreadyHave);
|
return Ok(IncomingBlockOk::AlreadyHave);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this when we have a working tx-pool.
|
let TxpoolReadResponse::TxsForBlock { mut txs, missing } = txpool_read_handle
|
||||||
if given_txs.len() != block.transactions.len() {
|
.ready()
|
||||||
return Err(IncomingBlockError::UnknownTransactions(
|
.await
|
||||||
block_hash,
|
.expect(PANIC_CRITICAL_SERVICE_ERROR)
|
||||||
(0..usize_to_u64(block.transactions.len())).collect(),
|
.call(TxpoolReadRequest::TxsForBlock(block.transactions.clone()))
|
||||||
));
|
.await
|
||||||
}
|
.expect(PANIC_CRITICAL_SERVICE_ERROR)
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: check we actually got given the right txs.
|
if !missing.is_empty() {
|
||||||
let prepped_txs = given_txs
|
let needed_hashes = missing.iter().map(|index| block.transactions[*index]);
|
||||||
.into_par_iter()
|
|
||||||
.map(|tx| {
|
for needed_hash in needed_hashes {
|
||||||
let tx = new_tx_verification_data(tx)?;
|
let Some(tx) = given_txs.remove(&needed_hash) else {
|
||||||
Ok((tx.tx_hash, tx))
|
return Err(IncomingBlockError::UnknownTransactions(block_hash, missing));
|
||||||
})
|
};
|
||||||
.collect::<Result<_, anyhow::Error>>()
|
|
||||||
.map_err(IncomingBlockError::InvalidBlock)?;
|
txs.insert(
|
||||||
|
needed_hash,
|
||||||
|
new_tx_verification_data(tx)
|
||||||
|
.map_err(|e| IncomingBlockError::InvalidBlock(e.into()))?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let Some(incoming_block_tx) = COMMAND_TX.get() else {
|
let Some(incoming_block_tx) = COMMAND_TX.get() else {
|
||||||
// We could still be starting up the blockchain manager.
|
// We could still be starting up the blockchain manager.
|
||||||
|
@ -126,7 +143,7 @@ pub async fn handle_incoming_block(
|
||||||
incoming_block_tx
|
incoming_block_tx
|
||||||
.send(BlockchainManagerCommand::AddBlock {
|
.send(BlockchainManagerCommand::AddBlock {
|
||||||
block,
|
block,
|
||||||
prepped_txs,
|
prepped_txs: txs,
|
||||||
response_tx,
|
response_tx,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! Tx-pool [`service`](super) interface.
|
//! Tx-pool [`service`](super) interface.
|
||||||
//!
|
//!
|
||||||
//! This module contains `cuprate_txpool`'s [`tower::Service`] request and response enums.
|
//! This module contains `cuprate_txpool`'s [`tower::Service`] request and response enums.
|
||||||
use std::{collections::HashSet, sync::Arc};
|
|
||||||
|
|
||||||
use cuprate_types::TransactionVerificationData;
|
use cuprate_types::TransactionVerificationData;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use crate::types::{TransactionBlobHash, TransactionHash};
|
use crate::types::{TransactionBlobHash, TransactionHash};
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ pub enum TxpoolReadRequest {
|
||||||
///
|
///
|
||||||
/// The hash is **not** the transaction hash, it is the hash of the serialized tx-blob.
|
/// The hash is **not** the transaction hash, it is the hash of the serialized tx-blob.
|
||||||
FilterKnownTxBlobHashes(HashSet<TransactionBlobHash>),
|
FilterKnownTxBlobHashes(HashSet<TransactionBlobHash>),
|
||||||
|
/// A request to pull some transactions for an incoming block.
|
||||||
|
TxsForBlock(Vec<TransactionHash>),
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- TxpoolReadResponse
|
//---------------------------------------------------------------------------------------------------- TxpoolReadResponse
|
||||||
|
@ -36,6 +38,10 @@ pub enum TxpoolReadResponse {
|
||||||
/// The tx hashes of the blob hashes that were known but were in the stem pool.
|
/// The tx hashes of the blob hashes that were known but were in the stem pool.
|
||||||
stem_pool_hashes: Vec<TransactionHash>,
|
stem_pool_hashes: Vec<TransactionHash>,
|
||||||
},
|
},
|
||||||
|
TxsForBlock {
|
||||||
|
txs: HashMap<[u8; 32], TransactionVerificationData>,
|
||||||
|
missing: Vec<usize>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- TxpoolWriteRequest
|
//---------------------------------------------------------------------------------------------------- TxpoolWriteRequest
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use rayon::ThreadPool;
|
use rayon::ThreadPool;
|
||||||
|
|
||||||
use cuprate_database::{ConcreteEnv, DatabaseRo, Env, EnvInner, RuntimeError};
|
use cuprate_database::{ConcreteEnv, DatabaseRo, Env, EnvInner, RuntimeError};
|
||||||
use cuprate_database_service::{init_thread_pool, DatabaseReadService, ReaderThreads};
|
use cuprate_database_service::{init_thread_pool, DatabaseReadService, ReaderThreads};
|
||||||
|
|
||||||
use crate::ops::in_stem_pool;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ops::get_transaction_verification_data,
|
ops::{get_transaction_verification_data, in_stem_pool},
|
||||||
service::{
|
service::{
|
||||||
interface::{TxpoolReadRequest, TxpoolReadResponse},
|
interface::{TxpoolReadRequest, TxpoolReadResponse},
|
||||||
types::{ReadResponseResult, TxpoolReadHandle},
|
types::{ReadResponseResult, TxpoolReadHandle},
|
||||||
|
@ -51,7 +53,6 @@ fn init_read_service_with_pool(env: Arc<ConcreteEnv>, pool: Arc<ThreadPool>) ->
|
||||||
/// 1. `Request` is mapped to a handler function
|
/// 1. `Request` is mapped to a handler function
|
||||||
/// 2. Handler function is called
|
/// 2. Handler function is called
|
||||||
/// 3. [`TxpoolReadResponse`] is returned
|
/// 3. [`TxpoolReadResponse`] is returned
|
||||||
#[expect(clippy::needless_pass_by_value)]
|
|
||||||
fn map_request(
|
fn map_request(
|
||||||
env: &ConcreteEnv, // Access to the database
|
env: &ConcreteEnv, // Access to the database
|
||||||
request: TxpoolReadRequest, // The request we must fulfill
|
request: TxpoolReadRequest, // The request we must fulfill
|
||||||
|
@ -62,6 +63,7 @@ fn map_request(
|
||||||
TxpoolReadRequest::FilterKnownTxBlobHashes(blob_hashes) => {
|
TxpoolReadRequest::FilterKnownTxBlobHashes(blob_hashes) => {
|
||||||
filter_known_tx_blob_hashes(env, blob_hashes)
|
filter_known_tx_blob_hashes(env, blob_hashes)
|
||||||
}
|
}
|
||||||
|
TxpoolReadRequest::TxsForBlock(txs_needed) => txs_for_block(env, txs_needed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,3 +159,29 @@ fn filter_known_tx_blob_hashes(
|
||||||
stem_pool_hashes,
|
stem_pool_hashes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`TxpoolReadRequest::TxsForBlock`].
|
||||||
|
fn txs_for_block(env: &ConcreteEnv, txs: Vec<TransactionHash>) -> ReadResponseResult {
|
||||||
|
let inner_env = env.env_inner();
|
||||||
|
let tx_ro = inner_env.tx_ro()?;
|
||||||
|
|
||||||
|
let tables = inner_env.open_tables(&tx_ro)?;
|
||||||
|
|
||||||
|
let mut missing_tx_indexes = Vec::with_capacity(txs.len());
|
||||||
|
let mut txs_verification_data = HashMap::with_capacity(txs.len());
|
||||||
|
|
||||||
|
for (i, tx_hash) in txs.into_iter().enumerate() {
|
||||||
|
match get_transaction_verification_data(&tx_hash, &tables) {
|
||||||
|
Ok(tx) => {
|
||||||
|
txs_verification_data.insert(tx_hash, tx);
|
||||||
|
}
|
||||||
|
Err(RuntimeError::KeyNotFound) => missing_tx_indexes.push(i),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(TxpoolReadResponse::TxsForBlock {
|
||||||
|
txs: txs_verification_data,
|
||||||
|
missing: missing_tx_indexes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
//!
|
//!
|
||||||
//! <!-- FIXME: Add schema here or a link to it when complete -->
|
//! <!-- FIXME: Add schema here or a link to it when complete -->
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
use monero_serai::transaction::Timelock;
|
use monero_serai::transaction::Timelock;
|
||||||
|
|
||||||
use cuprate_types::{CachedVerificationState, HardFork};
|
use cuprate_types::{CachedVerificationState, HardFork};
|
||||||
|
|
Loading…
Reference in a new issue