mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-22 19:49:28 +00:00
Fast sync part 2 (#156)
Some checks failed
Audit / audit (push) Has been cancelled
CI / fmt (push) Has been cancelled
CI / typo (push) Has been cancelled
CI / ci (macos-latest, stable, bash) (push) Has been cancelled
CI / ci (ubuntu-latest, stable, bash) (push) Has been cancelled
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Has been cancelled
Deny / audit (push) Has been cancelled
Some checks failed
Audit / audit (push) Has been cancelled
CI / fmt (push) Has been cancelled
CI / typo (push) Has been cancelled
CI / ci (macos-latest, stable, bash) (push) Has been cancelled
CI / ci (ubuntu-latest, stable, bash) (push) Has been cancelled
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Has been cancelled
Deny / audit (push) Has been cancelled
* boilerplate * Cargo.lock * Stub of block validation * Block validation (diff by @dllud) * Cargo.lock * Complete implementation of block validation request * Apply suggestions from code review Co-authored-by: Boog900 <boog900@tutanota.com> * More suggestions * Update consensus/fast-sync/src/fast_sync.rs * Update consensus/fast-sync/src/fast_sync.rs --------- Co-authored-by: Boog900 <boog900@tutanota.com>
This commit is contained in:
parent
f07d08942f
commit
c837f2f48e
3 changed files with 173 additions and 18 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -565,11 +565,15 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"cuprate-blockchain",
|
||||
"cuprate-consensus",
|
||||
"cuprate-consensus-rules",
|
||||
"cuprate-types",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"monero-serai",
|
||||
"rayon",
|
||||
"sha3",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
"tower",
|
||||
|
|
|
@ -11,11 +11,15 @@ path = "src/create.rs"
|
|||
[dependencies]
|
||||
clap = { workspace = true, features = ["derive", "std"] }
|
||||
cuprate-blockchain = { path = "../../storage/cuprate-blockchain" }
|
||||
cuprate-consensus = { path = ".." }
|
||||
cuprate-consensus-rules = { path = "../rules" }
|
||||
cuprate-types = { path = "../../types" }
|
||||
hex.workspace = true
|
||||
hex-literal.workspace = true
|
||||
monero-serai.workspace = true
|
||||
rayon.workspace = true
|
||||
sha3 = "0.10.8"
|
||||
thiserror.workspace = true
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tower.workspace = true
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{
|
||||
cmp,
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
|
@ -7,9 +8,21 @@ use std::{
|
|||
|
||||
#[allow(unused_imports)]
|
||||
use hex_literal::hex;
|
||||
use tower::Service;
|
||||
use monero_serai::{
|
||||
block::Block,
|
||||
transaction::{Input, Transaction},
|
||||
};
|
||||
use tower::{Service, ServiceExt};
|
||||
|
||||
use cuprate_consensus::{
|
||||
context::{BlockChainContextRequest, BlockChainContextResponse},
|
||||
transactions::TransactionVerificationData,
|
||||
};
|
||||
use cuprate_consensus_rules::{miner_tx::MinerTxError, ConsensusError};
|
||||
use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation};
|
||||
|
||||
use crate::{hash_of_hashes, BlockId, HashOfHashes};
|
||||
|
||||
#[cfg(not(test))]
|
||||
static HASHES_OF_HASHES: &[HashOfHashes] = &include!("./data/hashes_of_hashes");
|
||||
|
||||
|
@ -31,13 +44,6 @@ fn max_height() -> u64 {
|
|||
(HASHES_OF_HASHES.len() * BATCH_SIZE) as u64
|
||||
}
|
||||
|
||||
pub enum FastSyncRequest {
|
||||
ValidateHashes {
|
||||
start_height: u64,
|
||||
block_ids: Vec<BlockId>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ValidBlockId(BlockId);
|
||||
|
||||
|
@ -45,31 +51,79 @@ fn valid_block_ids(block_ids: &[BlockId]) -> Vec<ValidBlockId> {
|
|||
block_ids.iter().map(|b| ValidBlockId(*b)).collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum FastSyncRequest {
|
||||
ValidateHashes {
|
||||
start_height: u64,
|
||||
block_ids: Vec<BlockId>,
|
||||
},
|
||||
ValidateBlock {
|
||||
block: Block,
|
||||
txs: HashMap<[u8; 32], Transaction>,
|
||||
token: ValidBlockId,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum FastSyncResponse {
|
||||
ValidateHashes {
|
||||
validated_hashes: Vec<ValidBlockId>,
|
||||
unknown_hashes: Vec<BlockId>,
|
||||
},
|
||||
ValidateBlock(VerifiedBlockInformation),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(thiserror::Error, Debug, PartialEq)]
|
||||
pub enum FastSyncError {
|
||||
InvalidStartHeight, // start_height not a multiple of BATCH_SIZE
|
||||
Mismatch, // hash does not match
|
||||
NothingToDo, // no complete batch to check
|
||||
OutOfRange, // start_height too high
|
||||
#[error("Block does not match its expected hash")]
|
||||
BlockHashMismatch,
|
||||
|
||||
#[error("Start height must be a multiple of the batch size")]
|
||||
InvalidStartHeight,
|
||||
|
||||
#[error("Hash of hashes mismatch")]
|
||||
Mismatch,
|
||||
|
||||
#[error("Given range too small for fast sync (less than one batch)")]
|
||||
NothingToDo,
|
||||
|
||||
#[error("Start height too high for fast sync")]
|
||||
OutOfRange,
|
||||
|
||||
#[error("Block does not have the expected height entry")]
|
||||
BlockHeightMismatch,
|
||||
|
||||
#[error("Block does not contain the expected transaction list")]
|
||||
TxsIncludedWithBlockIncorrect,
|
||||
|
||||
#[error(transparent)]
|
||||
Consensus(#[from] ConsensusError),
|
||||
|
||||
#[error(transparent)]
|
||||
MinerTx(#[from] MinerTxError),
|
||||
|
||||
#[error("Database error: {0}")]
|
||||
DbErr(String),
|
||||
}
|
||||
|
||||
impl From<tower::BoxError> for FastSyncError {
|
||||
fn from(error: tower::BoxError) -> Self {
|
||||
Self::DbErr(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct FastSyncService<C> {
|
||||
context_svc: C,
|
||||
}
|
||||
|
||||
impl<C> FastSyncService<C>
|
||||
where
|
||||
C: Service<FastSyncRequest, Response = FastSyncResponse, Error = FastSyncError>
|
||||
+ Clone
|
||||
C: Service<
|
||||
BlockChainContextRequest,
|
||||
Response = BlockChainContextResponse,
|
||||
Error = tower::BoxError,
|
||||
> + Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
{
|
||||
|
@ -81,8 +135,11 @@ where
|
|||
|
||||
impl<C> Service<FastSyncRequest> for FastSyncService<C>
|
||||
where
|
||||
C: Service<FastSyncRequest, Response = FastSyncResponse, Error = FastSyncError>
|
||||
+ Clone
|
||||
C: Service<
|
||||
BlockChainContextRequest,
|
||||
Response = BlockChainContextResponse,
|
||||
Error = tower::BoxError,
|
||||
> + Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
C::Future: Send + 'static,
|
||||
|
@ -97,12 +154,17 @@ where
|
|||
}
|
||||
|
||||
fn call(&mut self, req: FastSyncRequest) -> Self::Future {
|
||||
let context_svc = self.context_svc.clone();
|
||||
|
||||
Box::pin(async move {
|
||||
match req {
|
||||
FastSyncRequest::ValidateHashes {
|
||||
start_height,
|
||||
block_ids,
|
||||
} => validate_hashes(start_height, &block_ids).await,
|
||||
FastSyncRequest::ValidateBlock { block, txs, token } => {
|
||||
validate_block(context_svc, block, txs, token).await
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -149,6 +211,91 @@ async fn validate_hashes(
|
|||
})
|
||||
}
|
||||
|
||||
async fn validate_block<C>(
|
||||
mut context_svc: C,
|
||||
block: Block,
|
||||
mut txs: HashMap<[u8; 32], Transaction>,
|
||||
token: ValidBlockId,
|
||||
) -> Result<FastSyncResponse, FastSyncError>
|
||||
where
|
||||
C: Service<
|
||||
BlockChainContextRequest,
|
||||
Response = BlockChainContextResponse,
|
||||
Error = tower::BoxError,
|
||||
> + Send
|
||||
+ 'static,
|
||||
C::Future: Send + 'static,
|
||||
{
|
||||
let BlockChainContextResponse::Context(checked_context) = context_svc
|
||||
.ready()
|
||||
.await?
|
||||
.call(BlockChainContextRequest::GetContext)
|
||||
.await?
|
||||
else {
|
||||
panic!("Context service returned wrong response!");
|
||||
};
|
||||
|
||||
let block_chain_ctx = checked_context.unchecked_blockchain_context().clone();
|
||||
|
||||
let block_hash = block.hash();
|
||||
if block_hash != token.0 {
|
||||
return Err(FastSyncError::BlockHashMismatch);
|
||||
}
|
||||
|
||||
let block_blob = block.serialize();
|
||||
|
||||
let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else {
|
||||
return Err(FastSyncError::MinerTx(MinerTxError::InputNotOfTypeGen));
|
||||
};
|
||||
if *height != block_chain_ctx.chain_height {
|
||||
return Err(FastSyncError::BlockHeightMismatch);
|
||||
}
|
||||
|
||||
let mut verified_txs = Vec::with_capacity(txs.len());
|
||||
for tx in &block.txs {
|
||||
let tx = txs
|
||||
.remove(tx)
|
||||
.ok_or(FastSyncError::TxsIncludedWithBlockIncorrect)?;
|
||||
|
||||
let data = TransactionVerificationData::new(tx)?;
|
||||
verified_txs.push(VerifiedTransactionInformation {
|
||||
tx_blob: data.tx_blob,
|
||||
tx_weight: data.tx_weight,
|
||||
fee: data.fee,
|
||||
tx_hash: data.tx_hash,
|
||||
tx: data.tx,
|
||||
});
|
||||
}
|
||||
|
||||
let total_fees = verified_txs.iter().map(|tx| tx.fee).sum::<u64>();
|
||||
let total_outputs = block
|
||||
.miner_tx
|
||||
.prefix
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|output| output.amount.unwrap_or(0))
|
||||
.sum::<u64>();
|
||||
|
||||
let generated_coins = total_outputs - total_fees;
|
||||
|
||||
let weight =
|
||||
block.miner_tx.weight() + verified_txs.iter().map(|tx| tx.tx_weight).sum::<usize>();
|
||||
|
||||
Ok(FastSyncResponse::ValidateBlock(VerifiedBlockInformation {
|
||||
block_blob,
|
||||
txs: verified_txs,
|
||||
block_hash,
|
||||
pow_hash: [0u8; 32],
|
||||
height: *height,
|
||||
generated_coins,
|
||||
weight,
|
||||
long_term_weight: block_chain_ctx.next_block_long_term_weight(weight),
|
||||
cumulative_difficulty: block_chain_ctx.cumulative_difficulty
|
||||
+ block_chain_ctx.next_difficulty,
|
||||
block,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Reference in a new issue