Skip to content

Commit 1549a88

Browse files
authored
cuprated: Add fast sync (#389)
* add specific method for context * add new statemachine for tx verification * fix consensus crates build * working builds * fix CI * add docs * fix CI * fix docs * fix clippy * cleanup * add docs to `blockchain_context` * fix doc tests * add output cache * new monero-serai * todo * todo * Revert "new monero-serai" This reverts commit fe3f6ac. * use indexmap to request outputs * clean up * fix typos * fix CI * fix cargo hack * fix reorgs * check if a block is already present before adding it to the alt block cache * fmt * update to new monero oxide API * fmt & fix cache * update config values * fix tests * add fast sync * fix start_height check * disable kill switch for now * add fast sync config options * add docs * wait for all blocks to download before starting the syncer again. * fix permit * typo * fix import order * fmt * add docs + order imports * fix clippy * review fixes * rename top -> stop
1 parent 5c2b56c commit 1549a88

File tree

27 files changed

+643
-404
lines changed

27 files changed

+643
-404
lines changed

Cargo.lock

Lines changed: 3 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

binaries/cuprated/src/blockchain.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ use cuprate_types::{
2020
use crate::constants::PANIC_CRITICAL_SERVICE_ERROR;
2121

2222
mod chain_service;
23+
mod fast_sync;
2324
pub mod interface;
2425
mod manager;
2526
mod syncer;
2627
mod types;
2728

29+
pub use fast_sync::set_fast_sync_hashes;
2830
pub use manager::init_blockchain_manager;
2931
pub use types::ConsensusBlockchainReadHandle;
3032

binaries/cuprated/src/blockchain/chain_service.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use futures::{future::BoxFuture, FutureExt, TryFutureExt};
44
use tower::Service;
55

66
use cuprate_blockchain::service::BlockchainReadHandle;
7+
use cuprate_fast_sync::validate_entries;
78
use cuprate_p2p::block_downloader::{ChainSvcRequest, ChainSvcResponse};
9+
use cuprate_p2p_core::NetworkZone;
810
use cuprate_types::blockchain::{BlockchainReadRequest, BlockchainResponse};
911

1012
/// That service that allows retrieving the chain state to give to the P2P crates, so we can figure out
@@ -14,16 +16,16 @@ use cuprate_types::blockchain::{BlockchainReadRequest, BlockchainResponse};
1416
#[derive(Clone)]
1517
pub struct ChainService(pub BlockchainReadHandle);
1618

17-
impl Service<ChainSvcRequest> for ChainService {
18-
type Response = ChainSvcResponse;
19+
impl<N: NetworkZone> Service<ChainSvcRequest<N>> for ChainService {
20+
type Response = ChainSvcResponse<N>;
1921
type Error = tower::BoxError;
2022
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
2123

2224
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2325
self.0.poll_ready(cx).map_err(Into::into)
2426
}
2527

26-
fn call(&mut self, req: ChainSvcRequest) -> Self::Future {
28+
fn call(&mut self, req: ChainSvcRequest<N>) -> Self::Future {
2729
let map_res = |res: BlockchainResponse| match res {
2830
BlockchainResponse::CompactChainHistory {
2931
block_ids,
@@ -67,6 +69,18 @@ impl Service<ChainSvcRequest> for ChainService {
6769
})
6870
.map_err(Into::into)
6971
.boxed(),
72+
ChainSvcRequest::ValidateEntries(entries, start_height) => {
73+
let mut blockchain_read_handle = self.0.clone();
74+
75+
async move {
76+
let (valid, unknown) =
77+
validate_entries(entries, start_height, &mut blockchain_read_handle)
78+
.await?;
79+
80+
Ok(ChainSvcResponse::ValidateEntries { valid, unknown })
81+
}
82+
.boxed()
83+
}
7084
}
7185
}
7286
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use std::slice;
2+
3+
use cuprate_helper::network::Network;
4+
5+
/// The hashes of the compiled in fast sync file.
6+
static FAST_SYNC_HASHES: &[[u8; 32]] = {
7+
let bytes = include_bytes!("./fast_sync/fast_sync_hashes.bin");
8+
9+
if bytes.len() % 32 == 0 {
10+
// SAFETY: The file byte length must be perfectly divisible by 32, checked above.
11+
unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<[u8; 32]>(), bytes.len() / 32) }
12+
} else {
13+
panic!();
14+
}
15+
};
16+
17+
/// Set the fast-sync hashes according to the provided values.
18+
pub fn set_fast_sync_hashes(fast_sync: bool, network: Network) {
19+
cuprate_fast_sync::set_fast_sync_hashes(if fast_sync && network == Network::Mainnet {
20+
FAST_SYNC_HASHES
21+
} else {
22+
&[]
23+
});
24+
}
204 KB
Binary file not shown.

binaries/cuprated/src/blockchain/manager.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc};
22

33
use futures::StreamExt;
44
use monero_serai::block::Block;
5-
use tokio::sync::{mpsc, oneshot, Notify};
5+
use tokio::sync::{mpsc, oneshot, Notify, OwnedSemaphorePermit};
66
use tower::{BoxError, Service, ServiceExt};
77
use tracing::error;
88

@@ -106,15 +106,17 @@ impl BlockchainManager {
106106
/// The [`BlockchainManager`] task.
107107
pub async fn run(
108108
mut self,
109-
mut block_batch_rx: mpsc::Receiver<BlockBatch>,
109+
mut block_batch_rx: mpsc::Receiver<(BlockBatch, Arc<OwnedSemaphorePermit>)>,
110110
mut command_rx: mpsc::Receiver<BlockchainManagerCommand>,
111111
) {
112112
loop {
113113
tokio::select! {
114-
Some(batch) = block_batch_rx.recv() => {
114+
Some((batch, permit)) = block_batch_rx.recv() => {
115115
self.handle_incoming_block_batch(
116116
batch,
117117
).await;
118+
119+
drop(permit);
118120
}
119121
Some(incoming_command) = command_rx.recv() => {
120122
self.handle_command(incoming_command).await;

binaries/cuprated/src/blockchain/manager/handler.rs

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
//! The blockchain manager handler functions.
2+
use std::{collections::HashMap, sync::Arc};
3+
24
use bytes::Bytes;
35
use futures::{TryFutureExt, TryStreamExt};
46
use monero_serai::{
57
block::Block,
68
transaction::{Input, Transaction},
79
};
810
use rayon::prelude::*;
9-
use std::ops::ControlFlow;
10-
use std::{collections::HashMap, sync::Arc};
1111
use tower::{Service, ServiceExt};
12-
use tracing::{info, instrument};
12+
use tracing::{info, instrument, Span};
1313

1414
use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle};
1515
use cuprate_consensus::{
@@ -21,12 +21,13 @@ use cuprate_consensus::{
2121
BlockChainContextRequest, BlockChainContextResponse, ExtendedConsensusError,
2222
};
2323
use cuprate_consensus_context::NewBlockData;
24+
use cuprate_fast_sync::{block_to_verified_block_information, fast_sync_stop_height};
2425
use cuprate_helper::cast::usize_to_u64;
2526
use cuprate_p2p::{block_downloader::BlockBatch, constants::LONG_BAN, BroadcastRequest};
2627
use cuprate_txpool::service::interface::TxpoolWriteRequest;
2728
use cuprate_types::{
2829
blockchain::{BlockchainReadRequest, BlockchainResponse, BlockchainWriteRequest},
29-
AltBlockInformation, HardFork, TransactionVerificationData, VerifiedBlockInformation,
30+
AltBlockInformation, Chain, HardFork, TransactionVerificationData, VerifiedBlockInformation,
3031
};
3132

3233
use crate::{
@@ -166,6 +167,11 @@ impl super::BlockchainManager {
166167
/// This function will panic if any internal service returns an unexpected error that we cannot
167168
/// recover from or if the incoming batch contains no blocks.
168169
async fn handle_incoming_block_batch_main_chain(&mut self, batch: BlockBatch) {
170+
if batch.blocks.last().unwrap().0.number().unwrap() < fast_sync_stop_height() {
171+
self.handle_incoming_block_batch_fast_sync(batch).await;
172+
return;
173+
}
174+
169175
let Ok((prepped_blocks, mut output_cache)) = batch_prepare_main_chain_blocks(
170176
batch.blocks,
171177
&mut self.blockchain_context_service,
@@ -195,7 +201,32 @@ impl super::BlockchainManager {
195201

196202
self.add_valid_block_to_main_chain(verified_block).await;
197203
}
198-
info!("Successfully added block batch");
204+
info!(fast_sync = false, "Successfully added block batch");
205+
}
206+
207+
/// Handles an incoming block batch while we are under the fast sync height.
208+
///
209+
/// # Panics
210+
///
211+
/// This function will panic if any internal service returns an unexpected error that we cannot
212+
/// recover from.
213+
async fn handle_incoming_block_batch_fast_sync(&mut self, batch: BlockBatch) {
214+
let mut valid_blocks = Vec::with_capacity(batch.blocks.len());
215+
for (block, txs) in batch.blocks {
216+
let block = block_to_verified_block_information(
217+
block,
218+
txs,
219+
self.blockchain_context_service.blockchain_context(),
220+
);
221+
self.add_valid_block_to_blockchain_cache(&block).await;
222+
223+
valid_blocks.push(block);
224+
}
225+
226+
self.batch_add_valid_block_to_blockchain_database(valid_blocks)
227+
.await;
228+
229+
info!(fast_sync = true, "Successfully added block batch");
199230
}
200231

201232
/// Handles an incoming [`BlockBatch`] that does not follow the main-chain.
@@ -212,7 +243,6 @@ impl super::BlockchainManager {
212243
/// recover from.
213244
async fn handle_incoming_block_batch_alt_chain(&mut self, mut batch: BlockBatch) {
214245
// TODO: this needs testing (this whole section does but alt-blocks specifically).
215-
216246
let mut blocks = batch.blocks.into_iter();
217247

218248
while let Some((block, txs)) = blocks.next() {
@@ -248,6 +278,8 @@ impl super::BlockchainManager {
248278
Ok(AddAltBlock::Cached) => (),
249279
}
250280
}
281+
282+
info!(alt_chain = true, "Successfully added block batch");
251283
}
252284

253285
/// Handles an incoming alt [`Block`].
@@ -284,9 +316,10 @@ impl super::BlockchainManager {
284316
unreachable!();
285317
};
286318

287-
if chain.is_some() {
288-
// The block could also be in the main-chain here under some circumstances.
289-
return Ok(AddAltBlock::Cached);
319+
match chain {
320+
Some((Chain::Alt(_), _)) => return Ok(AddAltBlock::Cached),
321+
Some((Chain::Main, _)) => anyhow::bail!("Alt block already in main chain"),
322+
None => (),
290323
}
291324

292325
let alt_block_info =
@@ -458,6 +491,36 @@ impl super::BlockchainManager {
458491
})
459492
.collect::<Vec<[u8; 32]>>();
460493

494+
self.add_valid_block_to_blockchain_cache(&verified_block)
495+
.await;
496+
497+
self.blockchain_write_handle
498+
.ready()
499+
.await
500+
.expect(PANIC_CRITICAL_SERVICE_ERROR)
501+
.call(BlockchainWriteRequest::WriteBlock(verified_block))
502+
.await
503+
.expect(PANIC_CRITICAL_SERVICE_ERROR);
504+
505+
self.txpool_write_handle
506+
.ready()
507+
.await
508+
.expect(PANIC_CRITICAL_SERVICE_ERROR)
509+
.call(TxpoolWriteRequest::NewBlock { spent_key_images })
510+
.await
511+
.expect(PANIC_CRITICAL_SERVICE_ERROR);
512+
}
513+
514+
/// Adds a [`VerifiedBlockInformation`] to the blockchain context cache.
515+
///
516+
/// # Panics
517+
///
518+
/// This function will panic if any internal service returns an unexpected error that we cannot
519+
/// recover from.
520+
async fn add_valid_block_to_blockchain_cache(
521+
&mut self,
522+
verified_block: &VerifiedBlockInformation,
523+
) {
461524
self.blockchain_context_service
462525
.ready()
463526
.await
@@ -474,28 +537,33 @@ impl super::BlockchainManager {
474537
}))
475538
.await
476539
.expect(PANIC_CRITICAL_SERVICE_ERROR);
540+
}
477541

542+
/// Batch writes the [`VerifiedBlockInformation`]s to the database.
543+
///
544+
/// The blocks must be sequential.
545+
///
546+
/// # Panics
547+
///
548+
/// This function will panic if any internal service returns an unexpected error that we cannot
549+
/// recover from.
550+
async fn batch_add_valid_block_to_blockchain_database(
551+
&mut self,
552+
blocks: Vec<VerifiedBlockInformation>,
553+
) {
478554
self.blockchain_write_handle
479555
.ready()
480556
.await
481557
.expect(PANIC_CRITICAL_SERVICE_ERROR)
482-
.call(BlockchainWriteRequest::WriteBlock(verified_block))
483-
.await
484-
.expect(PANIC_CRITICAL_SERVICE_ERROR);
485-
486-
self.txpool_write_handle
487-
.ready()
488-
.await
489-
.expect(PANIC_CRITICAL_SERVICE_ERROR)
490-
.call(TxpoolWriteRequest::NewBlock { spent_key_images })
558+
.call(BlockchainWriteRequest::BatchWriteBlocks(blocks))
491559
.await
492560
.expect(PANIC_CRITICAL_SERVICE_ERROR);
493561
}
494562
}
495563

496564
/// The result from successfully adding an alt-block.
497565
enum AddAltBlock {
498-
/// The alt-block was cached or was already present in the DB.
566+
/// The alt-block was cached.
499567
Cached,
500568
/// The chain was reorged.
501569
Reorged,

0 commit comments

Comments
 (0)