Skip to content

Commit

Permalink
add chain_selector mod (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
Davidson-Souza authored Mar 12, 2024
1 parent da138eb commit ba636a0
Show file tree
Hide file tree
Showing 15 changed files with 1,290 additions and 1,030 deletions.
130 changes: 122 additions & 8 deletions crates/floresta-chain/src/pruned_utreexo/chain_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,10 +755,52 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
})?;
Ok(())
}

fn get_assumeutreexo_index(&self) -> (BlockHash, u32) {
let guard = read_lock!(self);
guard.consensus.parameters.assumeutreexo_index
}
}

impl<PersistedState: ChainStore> BlockchainInterface for ChainState<PersistedState> {
type Error = BlockchainError;

fn get_block_locator_for_tip(&self, tip: BlockHash) -> Result<Vec<BlockHash>, BlockchainError> {
let mut hashes = Vec::new();
let height = self
.get_disk_block_header(&tip)?
.height()
.ok_or(BlockchainError::BlockNotPresent)?;

let mut index = height;
let mut current_height = height;
let mut current_header = self.get_disk_block_header(&tip)?;
let mut step = 1;

while index > 0 {
while current_height > index {
current_header = self.get_ancestor(&current_header)?;
current_height -= 1;
}

if hashes.len() >= 10 {
step *= 2;
}

hashes.push(current_header.block_hash());

if index > step {
index -= step;
} else {
break;
}
}

// genesis
hashes.push(self.get_block_hash(0).unwrap());
Ok(hashes)
}

fn is_in_idb(&self) -> bool {
self.inner.read().ibd
}
Expand Down Expand Up @@ -878,6 +920,68 @@ impl<PersistedState: ChainStore> BlockchainInterface for ChainState<PersistedSta
}
}
impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedState> {
fn mark_chain_as_valid(&self) -> Result<bool, BlockchainError> {
let (assume_utreexo_hash, assume_utreexo_height) = self.get_assumeutreexo_index();
let curr_validation_index = self.get_validation_index()?;

// We already ran once
if curr_validation_index >= assume_utreexo_height {
return Ok(true);
}

let mut assumed_hash = self.get_best_block()?.1;
// Walks the chain until finding our assumeutxo block.
// Since this block was passed in before starting florestad, this value should be
// lesser than or equal our current tip. If we don't find that block, it means the
// assumeutxo block was reorged out (or never was in the main chain). That's weird, but we
// should take precoution against it
while let Ok(header) = self.get_block_header(&assumed_hash) {
if header.block_hash() == assume_utreexo_hash {
break;
}
// We've reached genesis and didn't our block
if self.is_genesis(&header) {
break;
}
assumed_hash = self.get_ancestor(&header)?.block_hash();
}

// The assumeutreexo value passed is **not** in the main chain, start validaton from geneis
if assumed_hash != assume_utreexo_hash {
warn!("We are in a diffenrent chain than our default or provided assumeutreexo value. Restarting from genesis");

let mut guard = write_lock!(self);

guard.best_block.validation_index = assumed_hash; // Should be equal to genesis
guard.acc = Stump::new();

return Ok(false);
}

let mut curr_header = self.get_block_header(&assumed_hash)?;

// The assumeutreexo value passed is inside our main chain, start from that point
while let Ok(header) = self.get_disk_block_header(&curr_header.block_hash()) {
// We've reached genesis and didn't our block
if self.is_genesis(&header) {
break;
}
self.update_header(&DiskBlockHeader::FullyValid(
*header,
header.height().unwrap(),
))?;
curr_header = *self.get_ancestor(&header)?;
}

let mut guard = write_lock!(self);
let acc = guard.consensus.parameters.network_roots.clone();
guard.best_block.validation_index = assumed_hash;
info!("assuming chain with hash={assumed_hash}");
guard.acc = acc;

Ok(true)
}

fn invalidate_block(&self, block: BlockHash) -> Result<(), BlockchainError> {
let height = self.get_disk_block_header(&block)?.height();
if height.is_none() {
Expand Down Expand Up @@ -961,13 +1065,22 @@ impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedSta
);
self.flush()?;
}
if !ibd {
info!(
"New tip! hash={} height={height} tx_count={}",
block.block_hash(),
block.txdata.len()
);
self.flush()?;

match ibd {
false => {
info!(
"New tip! hash={} height={height} tx_count={}",
block.block_hash(),
block.txdata.len()
);
self.flush()?;
}
true => {
if block.block_hash() == self.get_best_block()?.1 {
info!("Tip reached, toggle IBD off");
self.toggle_ibd(false);
}
}
}

self.update_view(height, &block.header, acc)?;
Expand All @@ -984,8 +1097,8 @@ impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedSta
fn flush(&self) -> Result<(), BlockchainError> {
self.save_acc()?;
let inner = read_lock!(self);
inner.chainstore.flush()?;
inner.chainstore.save_height(&inner.best_block)?;
inner.chainstore.flush()?;
Ok(())
}

Expand Down Expand Up @@ -1034,6 +1147,7 @@ impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedSta
trace!("Header not in the best chain");
self.maybe_reorg(header)?;
}

Ok(())
}
fn get_root_hashes(&self) -> Vec<NodeHash> {
Expand Down
45 changes: 45 additions & 0 deletions crates/floresta-chain/src/pruned_utreexo/chainparams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use bitcoin::blockdata::constants::genesis_block;
use bitcoin::Block;
use bitcoin::BlockHash;
use bitcoin::Target;
use rustreexo::accumulator::node_hash::NodeHash;
use rustreexo::accumulator::stump::Stump;

use crate::prelude::*;
use crate::Network;
Expand Down Expand Up @@ -49,6 +51,8 @@ pub struct ChainParams {
/// A list of exceptions to the rules, where the key is the block hash and the value is the
/// verification flags
pub exceptions: HashMap<BlockHash, c_uint>,
pub network_roots: Stump,
pub assumeutreexo_index: (BlockHash, u32),
}

impl ChainParams {
Expand All @@ -62,6 +66,32 @@ impl ChainParams {
}
}

fn get_signet_roots() -> Stump {
let roots: Vec<NodeHash> = [
"8e6fcdcf05020fa1f7131a59a7050b33ca74852f5e82a5fbe236402bc4c8a928",
"f4c92949c71be7613699977eebf6d3bd5c8fd3e538a01380583e5aba14273425",
"d73ceb2748d342b14a269d7c0feb34aca1341a6367cc75cff6db8422eb01916d",
"a012e516784ccb7af26d7b356bf645e6a167cce5b48b9368c58c523acd25f6bf",
"e6e74ebc1d01ac47541c90afaac208c9b0f16226d2d046742032374e925a79ae",
"235b255558e994e6c5b6011469e891436cbf18107a939847e6e5df4cb939a96b",
"a9f45482564f0cb103067636c39fe30df1fa04b6b04d438c655530d991432761",
"d46716b7ccaf8d9eff11557527056f6100e016126df369eef95b9c9874467d40",
"7039b9053ef819d35c079eb4dcdd37029653a325bf416768e7de16bacf2c90af",
"f7a626339303030fc1b71d228e74aebdc2126cb7a2c5e01eb036225ea9dd41c2",
"b21123705cb4cef5a104705037ccd80ae7281789aa07cd468d5949c7e62df37b",
"ca931559f3ad9c91b9510f5dbfa42467e40ad8a0069d8f273de6079e9b115232",
"954ca698b58b6e6cdcc89948c841059d892578b7d67a249965fff83de5aaa7e3",
]
.iter()
.map(|hash| NodeHash::from_str(hash).unwrap())
.collect();

Stump {
roots,
leaves: 1477499,
}
}

#[cfg(feature = "bitcoinconsensus")]
fn get_exceptions() -> HashMap<BlockHash, c_uint> {
// For some reason, some blocks in the mainnet and testnet have different rules than it should
Expand Down Expand Up @@ -89,13 +119,15 @@ fn get_exceptions() -> HashMap<BlockHash, c_uint> {
fn get_exceptions() -> HashMap<BlockHash, c_uint> {
HashMap::new()
}

impl From<Network> for ChainParams {
fn from(net: Network) -> Self {
let genesis = genesis_block(net.into());
let max_target = ChainParams::max_target(net);
let exceptions = get_exceptions();
match net {
Network::Bitcoin => ChainParams {
assumeutreexo_index: (genesis.block_hash(), 0),
genesis,
max_target,
pow_allow_min_diff: false,
Expand All @@ -110,8 +142,10 @@ impl From<Network> for ChainParams {
segwit_activation_height: 481824,
csv_activation_height: 419328,
exceptions,
network_roots: Stump::default(),
},
Network::Testnet => ChainParams {
assumeutreexo_index: (genesis.block_hash(), 0),
genesis,
max_target,
pow_allow_min_diff: true,
Expand All @@ -126,6 +160,7 @@ impl From<Network> for ChainParams {
segwit_activation_height: 834_624,
csv_activation_height: 770_112,
exceptions,
network_roots: Stump::default(),
},
Network::Signet => ChainParams {
genesis,
Expand All @@ -142,8 +177,17 @@ impl From<Network> for ChainParams {
bip66_activation_height: 1,
segwit_activation_height: 1,
exceptions,
network_roots: get_signet_roots(),
assumeutreexo_index: (
BlockHash::from_str(
"0000001321625245a27e0be82a640106d019e35e48a024a17df1ceeb9b1f2131",
)
.unwrap(),
74551,
),
},
Network::Regtest => ChainParams {
assumeutreexo_index: (genesis.block_hash(), 0),
genesis,
max_target,
pow_allow_min_diff: false,
Expand All @@ -158,6 +202,7 @@ impl From<Network> for ChainParams {
bip66_activation_height: 0,
segwit_activation_height: 0,
exceptions,
network_roots: Stump::default(),
},
}
}
Expand Down
12 changes: 12 additions & 0 deletions crates/floresta-chain/src/pruned_utreexo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ pub trait BlockchainInterface {
fn is_coinbase_mature(&self, height: u32, block: BlockHash) -> Result<bool, Self::Error>;
/// Returns a block locator
fn get_block_locator(&self) -> Result<Vec<BlockHash>, Self::Error>;
/// Returns a block locator from a given tip
///
/// This method may be used to get the locator from a tip that's not the best one
fn get_block_locator_for_tip(&self, tip: BlockHash) -> Result<Vec<BlockHash>, BlockchainError>;
/// Returns the last block we validated
fn get_validation_index(&self) -> Result<u32, Self::Error>;
/// Triggers a rescan, downloading (but not validating) all blocks in [start_height:tip]
Expand All @@ -90,6 +94,8 @@ pub trait UpdatableChainstate {
/// Accepts a new header to our chain. This method is called before connect_block, and
/// makes some basic checks on a header and saves it on disk. We only accept a block as
/// valid after calling connect_block.
///
/// This function returns whether this block is on our best-known chain, or in a fork
fn accept_header(&self, header: BlockHeader) -> Result<(), BlockchainError>;
/// Not used for now, but in a future blockchain with mempool, we can process transactions
/// that are not in a block yet.
Expand Down Expand Up @@ -121,6 +127,12 @@ pub trait UpdatableChainstate {
final_height: u32,
acc: Stump,
) -> Result<PartialChainState, BlockchainError>;

/// Marks a chain as fully-valid
///
/// This mimics the behavour of checking every block before this block, and continues
/// from this point
fn mark_chain_as_valid(&self) -> Result<bool, BlockchainError>;
}

/// [ChainStore] is a trait defining how we interact with our chain database. This definitions
Expand Down
34 changes: 11 additions & 23 deletions crates/floresta-chain/src/pruned_utreexo/partial_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,6 @@ pub struct PartialChainState(pub(crate) UnsafeCell<PartialChainStateInner>);
unsafe impl Send for PartialChainState {}

impl PartialChainStateInner {
/// Returns the height we started syncing from
pub fn initial_height(&self) -> u32 {
self.initial_height
}

/// Is this interval valid?
pub fn is_valid(&self) -> bool {
self.is_sync() && self.error.is_none()
}

/// Returns the validation error, if any
pub fn error(&self) -> Option<BlockValidationErrors> {
self.error.clone()
}

/// Returns the height we have synced up to so far
pub fn current_height(&self) -> u32 {
self.current_height
Expand Down Expand Up @@ -223,11 +208,6 @@ impl PartialChainStateInner {
Ok(height)
}

/// Is the current accumulator what we expect?
pub fn is_expected(&self, acc: Stump) -> bool {
self.current_acc == acc
}

/// Check whether a block is valid
fn validate_block(
&self,
Expand Down Expand Up @@ -356,6 +336,10 @@ impl UpdatableChainstate for PartialChainState {
fn process_rescan_block(&self, _block: &bitcoin::Block) -> Result<(), BlockchainError> {
unimplemented!("we don't do rescan")
}

fn mark_chain_as_valid(&self) -> Result<bool, BlockchainError> {
unimplemented!("no need to mark as valid")
}
}

impl BlockchainInterface for PartialChainState {
Expand Down Expand Up @@ -402,6 +386,13 @@ impl BlockchainInterface for PartialChainState {
unimplemented!("PartialChainState::get_block_header")
}

fn get_block_locator_for_tip(
&self,
_tip: BlockHash,
) -> Result<Vec<BlockHash>, BlockchainError> {
unimplemented!("PartialChainState::get_block_locator_for_tip")
}

fn get_block(&self, _hash: &bitcoin::BlockHash) -> Result<bitcoin::Block, Self::Error> {
unimplemented!("PartialChainState::get_block")
}
Expand Down Expand Up @@ -547,7 +538,6 @@ mod tests {
.unwrap();
}
assert_eq!(chainstate.inner().current_height, 100);
assert!(chainstate.inner().is_valid());
}

#[test]
Expand Down Expand Up @@ -647,7 +637,5 @@ mod tests {

assert_eq!(chainstate2.inner().current_height, 150);
assert_eq!(chainstate2.inner().current_acc, expected_acc);

assert!(chainstate2.inner().is_valid());
}
}
Loading

0 comments on commit ba636a0

Please sign in to comment.