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 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(&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 ranges = get_alt_chain_history_ranges(range, alt_chain, alt_chains)?;
let res = ranges
.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::{
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(),

View file

@ -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

View file

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

View file

@ -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>,
},
}

View file

@ -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`].