mirror of
https://github.com/Cuprate/cuprate.git
synced 2025-01-08 20:09:44 +00:00
WIP: starting re-orgs
This commit is contained in:
parent
bc619b61eb
commit
029f439f0b
9 changed files with 212 additions and 102 deletions
|
@ -1,22 +1,20 @@
|
|||
use std::cmp::max;
|
||||
|
||||
use crate::ops::alt_block::{
|
||||
add_alt_transaction_blob, check_add_alt_chain_info, get_alt_chain_history_ranges,
|
||||
get_alt_transaction_blob,
|
||||
};
|
||||
use crate::ops::block::{get_block_extended_header_from_height, get_block_info};
|
||||
use crate::tables::{Tables, TablesMut};
|
||||
use crate::types::{
|
||||
AltBlockHeight, AltTransactionInfo, BlockHash, BlockHeight, CompactAltBlockInfo,
|
||||
};
|
||||
use bytemuck::TransparentWrapper;
|
||||
use cuprate_database::{DatabaseRo, DatabaseRw, RuntimeError, StorableVec};
|
||||
use cuprate_database::{RuntimeError, StorableVec};
|
||||
use cuprate_helper::map::{combine_low_high_bits_to_u128, split_u128_into_low_high_bits};
|
||||
use cuprate_types::{
|
||||
AltBlockInformation, Chain, ChainId, ExtendedBlockHeader, HardFork,
|
||||
VerifiedTransactionInformation,
|
||||
};
|
||||
use monero_serai::block::BlockHeader;
|
||||
|
||||
use crate::{
|
||||
ops::block::{get_block_extended_header_from_height, get_block_info},
|
||||
tables::{Tables, TablesMut},
|
||||
types::{
|
||||
AltBlockHeight, AltChainInfo, AltTransactionInfo, BlockHash, BlockHeight,
|
||||
CompactAltBlockInfo,
|
||||
},
|
||||
};
|
||||
use monero_serai::block::{Block, BlockHeader};
|
||||
|
||||
pub fn add_alt_block(
|
||||
alt_block: &AltBlockInformation,
|
||||
|
@ -55,64 +53,48 @@ pub fn add_alt_block(
|
|||
StorableVec::wrap_ref(&alt_block.block_blob),
|
||||
)?;
|
||||
|
||||
for tx in &alt_block.txs {
|
||||
add_alt_transaction(&tx, tables)?;
|
||||
assert_eq!(alt_block.txs.len(), alt_block.block.transactions.len());
|
||||
for (tx, tx_hash) in alt_block.txs.iter().zip(&alt_block.block.transactions) {
|
||||
add_alt_transaction_blob(tx_hash, StorableVec::wrap_ref(tx), tables)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_alt_transaction(
|
||||
tx: &VerifiedTransactionInformation,
|
||||
tables: &mut impl TablesMut,
|
||||
) -> Result<(), RuntimeError> {
|
||||
if tables.tx_ids().get(&tx.tx_hash).is_ok()
|
||||
|| tables.alt_transaction_infos().get(&tx.tx_hash).is_ok()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
tables.alt_transaction_infos_mut().put(
|
||||
&tx.tx_hash,
|
||||
&AltTransactionInfo {
|
||||
tx_weight: tx.tx_weight,
|
||||
fee: tx.fee,
|
||||
tx_hash: tx.tx_hash,
|
||||
},
|
||||
)?;
|
||||
|
||||
tables
|
||||
.alt_transaction_blobs_mut()
|
||||
.put(&tx.tx_hash, StorableVec::wrap_ref(&tx.tx_blob))
|
||||
}
|
||||
|
||||
pub fn check_add_alt_chain_info(
|
||||
pub fn get_alt_block(
|
||||
alt_block_height: &AltBlockHeight,
|
||||
prev_hash: &BlockHash,
|
||||
tables: &mut impl TablesMut,
|
||||
) -> Result<(), RuntimeError> {
|
||||
match tables.alt_chain_infos().get(&alt_block_height.chain_id) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(RuntimeError::KeyNotFound) => (),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
tables: &impl Tables,
|
||||
) -> Result<AltBlockInformation, RuntimeError> {
|
||||
let block_info = tables.alt_blocks_info().get(alt_block_height)?;
|
||||
|
||||
let parent_chain = match tables.alt_block_heights().get(prev_hash) {
|
||||
Ok(alt_parent_height) => Chain::Alt(alt_parent_height.chain_id.into()),
|
||||
Err(RuntimeError::KeyNotFound) => Chain::Main,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
let block_blob = tables.alt_block_blobs().get(alt_block_height)?.0;
|
||||
|
||||
tables.alt_chain_infos_mut().put(
|
||||
&alt_block_height.chain_id,
|
||||
&AltChainInfo {
|
||||
parent_chain: parent_chain.into(),
|
||||
common_ancestor_height: alt_block_height.height - 1,
|
||||
},
|
||||
)
|
||||
let block = Block::read(&mut block_blob.as_slice())?;
|
||||
|
||||
let txs = block
|
||||
.transactions
|
||||
.iter()
|
||||
.map(|tx_hash| get_alt_transaction_blob(tx_hash, tables))
|
||||
.collect()?;
|
||||
|
||||
Ok(AltBlockInformation {
|
||||
block,
|
||||
block_blob,
|
||||
txs,
|
||||
block_hash: block_info.block_hash,
|
||||
pow_hash: block_info.pow_hash,
|
||||
height: block_info.height,
|
||||
weight: block_info.weight,
|
||||
long_term_weight: block_info.long_term_weight,
|
||||
cumulative_difficulty: combine_low_high_bits_to_u128(
|
||||
block_info.cumulative_difficulty_low,
|
||||
block_info.cumulative_difficulty_high,
|
||||
),
|
||||
chain_id: alt_block_height.chain_id.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn alt_block_hash(
|
||||
pub fn get_alt_block_hash(
|
||||
block_height: &BlockHeight,
|
||||
alt_chain: ChainId,
|
||||
tables: &mut impl Tables,
|
||||
|
@ -152,37 +134,15 @@ pub fn alt_block_hash(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn alt_extended_headers_in_range(
|
||||
pub fn get_alt_extended_headers_in_range(
|
||||
range: std::ops::Range<BlockHeight>,
|
||||
alt_chain: ChainId,
|
||||
tables: &impl Tables,
|
||||
) -> Result<Vec<ExtendedBlockHeader>, RuntimeError> {
|
||||
// TODO: this function does not use rayon, however it probably should.
|
||||
|
||||
let mut ranges = Vec::with_capacity(5);
|
||||
let alt_chains = tables.alt_chain_infos();
|
||||
|
||||
let mut i = range.end;
|
||||
let mut current_chain_id = alt_chain.into();
|
||||
while i > range.start {
|
||||
let chain_info = alt_chains.get(¤t_chain_id)?;
|
||||
|
||||
let start_height = max(range.start, chain_info.common_ancestor_height + 1);
|
||||
|
||||
ranges.push((chain_info.parent_chain.into(), start_height..i));
|
||||
i = chain_info.common_ancestor_height;
|
||||
|
||||
match chain_info.parent_chain.into() {
|
||||
Chain::Main => {
|
||||
ranges.push((Chain::Main, range.start..i));
|
||||
break;
|
||||
}
|
||||
Chain::Alt(alt_chain_id) => {
|
||||
current_chain_id = alt_chain_id.into();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
let ranges = get_alt_chain_history_ranges(range, alt_chain, alt_chains)?;
|
||||
|
||||
let res = ranges
|
||||
.into_iter()
|
63
storage/blockchain/src/ops/alt_block/chain.rs
Normal file
63
storage/blockchain/src/ops/alt_block/chain.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use crate::tables::{AltChainInfos, TablesMut};
|
||||
use crate::types::{AltBlockHeight, AltChainInfo, BlockHash, BlockHeight};
|
||||
use cuprate_database::{DatabaseRo, RuntimeError};
|
||||
use cuprate_types::{Chain, ChainId};
|
||||
use std::cmp::max;
|
||||
|
||||
pub fn check_add_alt_chain_info(
|
||||
alt_block_height: &AltBlockHeight,
|
||||
prev_hash: &BlockHash,
|
||||
tables: &mut impl TablesMut,
|
||||
) -> Result<(), RuntimeError> {
|
||||
match tables.alt_chain_infos().get(&alt_block_height.chain_id) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(RuntimeError::KeyNotFound) => (),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
|
||||
let parent_chain = match tables.alt_block_heights().get(prev_hash) {
|
||||
Ok(alt_parent_height) => Chain::Alt(alt_parent_height.chain_id.into()),
|
||||
Err(RuntimeError::KeyNotFound) => Chain::Main,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
tables.alt_chain_infos_mut().put(
|
||||
&alt_block_height.chain_id,
|
||||
&AltChainInfo {
|
||||
parent_chain: parent_chain.into(),
|
||||
common_ancestor_height: alt_block_height.height - 1,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_alt_chain_history_ranges(
|
||||
range: std::ops::Range<BlockHeight>,
|
||||
alt_chain: ChainId,
|
||||
alt_chain_infos: &impl DatabaseRo<AltChainInfos>,
|
||||
) -> Result<Vec<(Chain, std::ops::Range<BlockHeight>)>, RuntimeError> {
|
||||
let mut ranges = Vec::with_capacity(5);
|
||||
|
||||
let mut i = range.end;
|
||||
let mut current_chain_id = alt_chain.into();
|
||||
while i > range.start {
|
||||
let chain_info = alt_chain_infos.get(¤t_chain_id)?;
|
||||
|
||||
let start_height = max(range.start, chain_info.common_ancestor_height + 1);
|
||||
|
||||
ranges.push((chain_info.parent_chain.into(), start_height..i));
|
||||
i = chain_info.common_ancestor_height;
|
||||
|
||||
match chain_info.parent_chain.into() {
|
||||
Chain::Main => {
|
||||
ranges.push((Chain::Main, range.start..i));
|
||||
break;
|
||||
}
|
||||
Chain::Alt(alt_chain_id) => {
|
||||
current_chain_id = alt_chain_id.into();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ranges)
|
||||
}
|
7
storage/blockchain/src/ops/alt_block/mod.rs
Normal file
7
storage/blockchain/src/ops/alt_block/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod block;
|
||||
mod chain;
|
||||
mod tx;
|
||||
|
||||
pub use block::*;
|
||||
pub use chain::*;
|
||||
pub use tx::*;
|
35
storage/blockchain/src/ops/alt_block/tx.rs
Normal file
35
storage/blockchain/src/ops/alt_block/tx.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use crate::tables::{Tables, TablesMut};
|
||||
use crate::types::{AltTransactionInfo, TxHash};
|
||||
use bytemuck::TransparentWrapper;
|
||||
use cuprate_database::{RuntimeError, StorableVec};
|
||||
use cuprate_types::VerifiedTransactionInformation;
|
||||
|
||||
pub fn add_alt_transaction_blob(
|
||||
tx_hash: &TxHash,
|
||||
tx_block: &StorableVec<u8>,
|
||||
tables: &mut impl TablesMut,
|
||||
) -> Result<(), RuntimeError> {
|
||||
if tables.tx_ids().get(&tx_hash).is_ok() || tables.alt_transaction_blobs().get(&tx_hash).is_ok()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
tables.alt_transaction_blobs_mut().put(&tx_hash, tx_block)
|
||||
}
|
||||
|
||||
pub fn get_alt_transaction_blob(
|
||||
tx_hash: &TxHash,
|
||||
tables: &impl Tables,
|
||||
) -> Result<Vec<u8>, RuntimeError> {
|
||||
match tables.alt_transaction_blobs().get(tx_hash) {
|
||||
Ok(blob) => Ok(blob.0),
|
||||
Err(RuntimeError::KeyNotFound) => {
|
||||
let tx_id = tables.tx_ids().get(tx_hash)?;
|
||||
|
||||
let blob = tables.tx_blobs().get(&tx_id)?;
|
||||
|
||||
Ok(blob.0)
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ use cuprate_types::{
|
|||
|
||||
use crate::{
|
||||
ops::{
|
||||
alt_block::{alt_block_hash, alt_extended_headers_in_range},
|
||||
alt_block::{get_alt_block_hash, get_alt_extended_headers_in_range},
|
||||
block::{
|
||||
block_exists, get_block_extended_header_from_height, get_block_height, get_block_info,
|
||||
},
|
||||
|
@ -200,7 +200,7 @@ fn block_hash(env: &ConcreteEnv, block_height: BlockHeight, chain: Chain) -> Res
|
|||
let block_hash = match chain {
|
||||
Chain::Main => get_block_info(&block_height, &table_block_infos)?.block_hash,
|
||||
Chain::Alt(chain) => {
|
||||
alt_block_hash(&block_height, chain, &mut env_inner.open_tables(&tx_ro)?)?
|
||||
get_alt_block_hash(&block_height, chain, &mut env_inner.open_tables(&tx_ro)?)?
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -284,7 +284,7 @@ fn block_extended_header_in_range(
|
|||
.collect::<Result<Vec<ExtendedBlockHeader>, RuntimeError>>()?,
|
||||
Chain::Alt(chain_id) => {
|
||||
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
|
||||
alt_extended_headers_in_range(
|
||||
get_alt_extended_headers_in_range(
|
||||
range,
|
||||
chain_id,
|
||||
get_tables!(env_inner, tx_ro, tables)?.as_ref(),
|
||||
|
|
|
@ -7,7 +7,7 @@ use cuprate_database::{ConcreteEnv, Env, EnvInner, RuntimeError, TxRw};
|
|||
use cuprate_database_service::DatabaseWriteHandle;
|
||||
use cuprate_types::{
|
||||
blockchain::{BlockchainResponse, BlockchainWriteRequest},
|
||||
VerifiedBlockInformation,
|
||||
AltBlockInformation, VerifiedBlockInformation,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -29,10 +29,10 @@ fn handle_blockchain_request(
|
|||
) -> Result<BlockchainResponse, RuntimeError> {
|
||||
match req {
|
||||
BlockchainWriteRequest::WriteBlock(block) => write_block(env, block),
|
||||
BlockchainWriteRequest::WriteAltBlock(_) => todo!(),
|
||||
BlockchainWriteRequest::WriteAltBlock(alt_block) => write_alt_block(env, alt_block),
|
||||
BlockchainWriteRequest::StartReorg(_) => todo!(),
|
||||
BlockchainWriteRequest::ReverseReorg(_) => todo!(),
|
||||
BlockchainWriteRequest::FlushAltBlocks => todo!(),
|
||||
BlockchainWriteRequest::FlushAltBlocks => flush_alt_blocks(env),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,56 @@ fn write_block(env: &ConcreteEnv, block: &VerifiedBlockInformation) -> ResponseR
|
|||
match result {
|
||||
Ok(()) => {
|
||||
TxRw::commit(tx_rw)?;
|
||||
Ok(BlockchainResponse::WriteBlockOk)
|
||||
Ok(BlockchainResponse::Ok)
|
||||
}
|
||||
Err(e) => {
|
||||
// INVARIANT: ensure database atomicity by aborting
|
||||
// the transaction on `add_block()` failures.
|
||||
TxRw::abort(tx_rw)
|
||||
.expect("could not maintain database atomicity by aborting write transaction");
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`BlockchainWriteRequest::WriteAltBlock`].
|
||||
#[inline]
|
||||
fn write_alt_block(env: &ConcreteEnv, block: &AltBlockInformation) -> ResponseResult {
|
||||
let env_inner = env.env_inner();
|
||||
let tx_rw = env_inner.tx_rw()?;
|
||||
|
||||
let result = {
|
||||
let mut tables_mut = env_inner.open_tables_mut(&tx_rw)?;
|
||||
crate::ops::alt_block::add_alt_block(block, &mut tables_mut)
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
TxRw::commit(tx_rw)?;
|
||||
Ok(BlockchainResponse::Ok)
|
||||
}
|
||||
Err(e) => {
|
||||
// INVARIANT: ensure database atomicity by aborting
|
||||
// the transaction on `add_block()` failures.
|
||||
TxRw::abort(tx_rw)
|
||||
.expect("could not maintain database atomicity by aborting write transaction");
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`BlockchainWriteRequest::FlushAltBlocks`].
|
||||
#[inline]
|
||||
fn flush_alt_blocks(env: &ConcreteEnv) -> ResponseResult {
|
||||
let env_inner = env.env_inner();
|
||||
let mut tx_rw = env_inner.tx_rw()?;
|
||||
|
||||
let result = { crate::ops::alt_block::flush_alt_blocks(&env_inner, &mut tx_rw) };
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
TxRw::commit(tx_rw)?;
|
||||
Ok(BlockchainResponse::Ok)
|
||||
}
|
||||
Err(e) => {
|
||||
// INVARIANT: ensure database atomicity by aborting
|
||||
|
|
|
@ -145,10 +145,6 @@ cuprate_database::define_tables! {
|
|||
|
||||
19 => AltTransactionBlobs,
|
||||
TxHash => TxBlob,
|
||||
|
||||
20 => AltTransactionInfos,
|
||||
TxHash => AltTransactionInfo,
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
//! responses are also tested in Cuprate's blockchain database crate.
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use crate::types::{Chain, ExtendedBlockHeader, OutputOnChain, VerifiedBlockInformation};
|
||||
use crate::{AltBlockInformation, ChainId};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ops::Range,
|
||||
};
|
||||
use crate::{AltBlockInformation, ChainId};
|
||||
use crate::types::{Chain, ExtendedBlockHeader, OutputOnChain, VerifiedBlockInformation};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- ReadRequest
|
||||
/// A read request to the blockchain database.
|
||||
|
@ -223,6 +223,7 @@ pub enum BlockchainResponse {
|
|||
///
|
||||
/// currently the response for:
|
||||
/// - [`BlockchainWriteRequest::WriteBlock`]
|
||||
/// - [`BlockchainWriteRequest::WriteAltBlock`]
|
||||
/// - [`BlockchainWriteRequest::ReverseReorg`]
|
||||
/// - [`BlockchainWriteRequest::FlushAltBlocks`]
|
||||
Ok,
|
||||
|
@ -231,7 +232,7 @@ pub enum BlockchainResponse {
|
|||
/// The [`ChainId`] of the old main chain blocks that were popped.
|
||||
old_main_chain_id: ChainId,
|
||||
/// The next alt chain blocks.
|
||||
alt_chain: Vec<AltBlockInformation>
|
||||
alt_chain: Vec<AltBlockInformation>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -39,8 +39,7 @@ pub struct ExtendedBlockHeader {
|
|||
//---------------------------------------------------------------------------------------------------- VerifiedTransactionInformation
|
||||
/// Verified information of a transaction.
|
||||
///
|
||||
/// - If this is in a [`VerifiedBlockInformation`] this represents a valid transaction
|
||||
/// - If this is in an [`AltBlockInformation`] this represents a potentially valid transaction
|
||||
/// This represents a valid transaction
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct VerifiedTransactionInformation {
|
||||
/// The transaction itself.
|
||||
|
@ -121,7 +120,7 @@ pub struct AltBlockInformation {
|
|||
/// [`Block::serialize`].
|
||||
pub block_blob: Vec<u8>,
|
||||
/// All the transactions in the block, excluding the [`Block::miner_transaction`].
|
||||
pub txs: Vec<VerifiedTransactionInformation>,
|
||||
pub txs: Vec<Vec<u8>>,
|
||||
/// The block's hash.
|
||||
///
|
||||
/// [`Block::hash`].
|
||||
|
|
Loading…
Reference in a new issue