WIP: starting re-orgs

This commit is contained in:
Boog900 2024-09-01 02:15:16 +01:00
parent bc619b61eb
commit 029f439f0b
No known key found for this signature in database
GPG key ID: 42AB1287CB0041C2
9 changed files with 212 additions and 102 deletions

View file

@ -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 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_helper::map::{combine_low_high_bits_to_u128, split_u128_into_low_high_bits};
use cuprate_types::{ use cuprate_types::{
AltBlockInformation, Chain, ChainId, ExtendedBlockHeader, HardFork, AltBlockInformation, Chain, ChainId, ExtendedBlockHeader, HardFork,
VerifiedTransactionInformation, VerifiedTransactionInformation,
}; };
use monero_serai::block::BlockHeader; use monero_serai::block::{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,
},
};
pub fn add_alt_block( pub fn add_alt_block(
alt_block: &AltBlockInformation, alt_block: &AltBlockInformation,
@ -55,64 +53,48 @@ pub fn add_alt_block(
StorableVec::wrap_ref(&alt_block.block_blob), StorableVec::wrap_ref(&alt_block.block_blob),
)?; )?;
for tx in &alt_block.txs { assert_eq!(alt_block.txs.len(), alt_block.block.transactions.len());
add_alt_transaction(&tx, tables)?; 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(()) Ok(())
} }
pub fn add_alt_transaction( pub fn get_alt_block(
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(
alt_block_height: &AltBlockHeight, alt_block_height: &AltBlockHeight,
prev_hash: &BlockHash, tables: &impl Tables,
tables: &mut impl TablesMut, ) -> Result<AltBlockInformation, RuntimeError> {
) -> Result<(), RuntimeError> { let block_info = tables.alt_blocks_info().get(alt_block_height)?;
match tables.alt_chain_infos().get(&alt_block_height.chain_id) {
Ok(_) => return Ok(()), let block_blob = tables.alt_block_blobs().get(alt_block_height)?.0;
Err(RuntimeError::KeyNotFound) => (),
Err(e) => return Err(e), 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(),
})
} }
let parent_chain = match tables.alt_block_heights().get(prev_hash) { pub fn get_alt_block_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 alt_block_hash(
block_height: &BlockHeight, block_height: &BlockHeight,
alt_chain: ChainId, alt_chain: ChainId,
tables: &mut impl Tables, 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>, range: std::ops::Range<BlockHeight>,
alt_chain: ChainId, alt_chain: ChainId,
tables: &impl Tables, tables: &impl Tables,
) -> Result<Vec<ExtendedBlockHeader>, RuntimeError> { ) -> Result<Vec<ExtendedBlockHeader>, RuntimeError> {
// TODO: this function does not use rayon, however it probably should. // 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 alt_chains = tables.alt_chain_infos();
let ranges = get_alt_chain_history_ranges(range, alt_chain, alt_chains)?;
let mut i = range.end;
let mut current_chain_id = alt_chain.into();
while i > range.start {
let chain_info = alt_chains.get(&current_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 res = ranges let res = ranges
.into_iter() .into_iter()

View 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(&current_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)
}

View file

@ -0,0 +1,7 @@
mod block;
mod chain;
mod tx;
pub use block::*;
pub use chain::*;
pub use tx::*;

View 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),
}
}

View file

@ -22,7 +22,7 @@ use cuprate_types::{
use crate::{ use crate::{
ops::{ 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::{
block_exists, get_block_extended_header_from_height, get_block_height, get_block_info, 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 { let block_hash = match chain {
Chain::Main => get_block_info(&block_height, &table_block_infos)?.block_hash, Chain::Main => get_block_info(&block_height, &table_block_infos)?.block_hash,
Chain::Alt(chain) => { 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>>()?, .collect::<Result<Vec<ExtendedBlockHeader>, RuntimeError>>()?,
Chain::Alt(chain_id) => { Chain::Alt(chain_id) => {
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?; let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
alt_extended_headers_in_range( get_alt_extended_headers_in_range(
range, range,
chain_id, chain_id,
get_tables!(env_inner, tx_ro, tables)?.as_ref(), get_tables!(env_inner, tx_ro, tables)?.as_ref(),

View file

@ -7,7 +7,7 @@ use cuprate_database::{ConcreteEnv, Env, EnvInner, RuntimeError, TxRw};
use cuprate_database_service::DatabaseWriteHandle; use cuprate_database_service::DatabaseWriteHandle;
use cuprate_types::{ use cuprate_types::{
blockchain::{BlockchainResponse, BlockchainWriteRequest}, blockchain::{BlockchainResponse, BlockchainWriteRequest},
VerifiedBlockInformation, AltBlockInformation, VerifiedBlockInformation,
}; };
use crate::{ use crate::{
@ -29,10 +29,10 @@ fn handle_blockchain_request(
) -> Result<BlockchainResponse, RuntimeError> { ) -> Result<BlockchainResponse, RuntimeError> {
match req { match req {
BlockchainWriteRequest::WriteBlock(block) => write_block(env, block), BlockchainWriteRequest::WriteBlock(block) => write_block(env, block),
BlockchainWriteRequest::WriteAltBlock(_) => todo!(), BlockchainWriteRequest::WriteAltBlock(alt_block) => write_alt_block(env, alt_block),
BlockchainWriteRequest::StartReorg(_) => todo!(), BlockchainWriteRequest::StartReorg(_) => todo!(),
BlockchainWriteRequest::ReverseReorg(_) => 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 { match result {
Ok(()) => { Ok(()) => {
TxRw::commit(tx_rw)?; 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) => { Err(e) => {
// INVARIANT: ensure database atomicity by aborting // INVARIANT: ensure database atomicity by aborting

View file

@ -145,10 +145,6 @@ cuprate_database::define_tables! {
19 => AltTransactionBlobs, 19 => AltTransactionBlobs,
TxHash => TxBlob, TxHash => TxBlob,
20 => AltTransactionInfos,
TxHash => AltTransactionInfo,
} }
//---------------------------------------------------------------------------------------------------- Tests //---------------------------------------------------------------------------------------------------- Tests

View file

@ -4,12 +4,12 @@
//! responses are also tested in Cuprate's blockchain database crate. //! responses are also tested in Cuprate's blockchain database crate.
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use crate::types::{Chain, ExtendedBlockHeader, OutputOnChain, VerifiedBlockInformation};
use crate::{AltBlockInformation, ChainId};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
ops::Range, ops::Range,
}; };
use crate::{AltBlockInformation, ChainId};
use crate::types::{Chain, ExtendedBlockHeader, OutputOnChain, VerifiedBlockInformation};
//---------------------------------------------------------------------------------------------------- ReadRequest //---------------------------------------------------------------------------------------------------- ReadRequest
/// A read request to the blockchain database. /// A read request to the blockchain database.
@ -223,6 +223,7 @@ pub enum BlockchainResponse {
/// ///
/// currently the response for: /// currently the response for:
/// - [`BlockchainWriteRequest::WriteBlock`] /// - [`BlockchainWriteRequest::WriteBlock`]
/// - [`BlockchainWriteRequest::WriteAltBlock`]
/// - [`BlockchainWriteRequest::ReverseReorg`] /// - [`BlockchainWriteRequest::ReverseReorg`]
/// - [`BlockchainWriteRequest::FlushAltBlocks`] /// - [`BlockchainWriteRequest::FlushAltBlocks`]
Ok, Ok,
@ -231,7 +232,7 @@ pub enum BlockchainResponse {
/// The [`ChainId`] of the old main chain blocks that were popped. /// The [`ChainId`] of the old main chain blocks that were popped.
old_main_chain_id: ChainId, old_main_chain_id: ChainId,
/// The next alt chain blocks. /// The next alt chain blocks.
alt_chain: Vec<AltBlockInformation> alt_chain: Vec<AltBlockInformation>,
}, },
} }

View file

@ -39,8 +39,7 @@ pub struct ExtendedBlockHeader {
//---------------------------------------------------------------------------------------------------- VerifiedTransactionInformation //---------------------------------------------------------------------------------------------------- VerifiedTransactionInformation
/// Verified information of a transaction. /// Verified information of a transaction.
/// ///
/// - If this is in a [`VerifiedBlockInformation`] this represents a valid transaction /// This represents a valid transaction
/// - If this is in an [`AltBlockInformation`] this represents a potentially valid transaction
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct VerifiedTransactionInformation { pub struct VerifiedTransactionInformation {
/// The transaction itself. /// The transaction itself.
@ -121,7 +120,7 @@ pub struct AltBlockInformation {
/// [`Block::serialize`]. /// [`Block::serialize`].
pub block_blob: Vec<u8>, pub block_blob: Vec<u8>,
/// All the transactions in the block, excluding the [`Block::miner_transaction`]. /// All the transactions in the block, excluding the [`Block::miner_transaction`].
pub txs: Vec<VerifiedTransactionInformation>, pub txs: Vec<Vec<u8>>,
/// The block's hash. /// The block's hash.
/// ///
/// [`Block::hash`]. /// [`Block::hash`].