Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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