diff --git a/crates/floresta-chain/benches/chain_state_bench.rs b/crates/floresta-chain/benches/chain_state_bench.rs index 5271be63..d5eb87a4 100644 --- a/crates/floresta-chain/benches/chain_state_bench.rs +++ b/crates/floresta-chain/benches/chain_state_bench.rs @@ -13,6 +13,7 @@ use criterion::criterion_main; use criterion::BatchSize; use criterion::Criterion; use criterion::SamplingMode; +use floresta_chain::pruned_utreexo::utxo_data::UtxoData; use floresta_chain::pruned_utreexo::UpdatableChainstate; use floresta_chain::AssumeValidArg; use floresta_chain::ChainState; @@ -44,7 +45,7 @@ fn setup_test_chain<'a>( fn decode_block_and_inputs( block_file: File, stxos_file: File, -) -> (Block, HashMap) { +) -> (Block, HashMap) { let block_bytes = zstd::decode_all(block_file).unwrap(); let block: Block = deserialize(&block_bytes).unwrap(); @@ -58,7 +59,7 @@ fn decode_block_and_inputs( .iter() .skip(1) // Skip the coinbase transaction .flat_map(|tx| &tx.input) - .map(|txin| (txin.previous_output, stxos.remove(0))) + .map(|txin| (txin.previous_output, UtxoData(stxos.remove(0), 0))) .collect(); assert!(stxos.is_empty(), "Moved all stxos to the inputs map"); diff --git a/crates/floresta-chain/src/pruned_utreexo/chain_state.rs b/crates/floresta-chain/src/pruned_utreexo/chain_state.rs index 54cf5bd5..0453e6ab 100644 --- a/crates/floresta-chain/src/pruned_utreexo/chain_state.rs +++ b/crates/floresta-chain/src/pruned_utreexo/chain_state.rs @@ -10,6 +10,7 @@ use alloc::vec::Vec; use core::cell::UnsafeCell; #[cfg(feature = "bitcoinconsensus")] use core::ffi::c_uint; +use core::ops::Div; use bitcoin::block::Header as BlockHeader; use bitcoin::blockdata::constants::genesis_block; @@ -21,10 +22,8 @@ use bitcoin::hashes::Hash; use bitcoin::script; use bitcoin::Block; use bitcoin::BlockHash; -use bitcoin::OutPoint; use bitcoin::Target; use bitcoin::Transaction; -use bitcoin::TxOut; use bitcoin::Work; use floresta_common::Channel; use log::info; @@ -44,8 +43,10 @@ use super::chainstore::KvChainStore; use super::consensus::Consensus; use super::error::BlockValidationErrors; use super::error::BlockchainError; +use super::nodetime::DisableTime; use super::partial_chain::PartialChainState; use super::partial_chain::PartialChainStateInner; +use super::utxo_data::UtxoMap; use super::BlockchainInterface; use super::ChainStore; use super::UpdatableChainstate; @@ -750,8 +751,22 @@ impl ChainState { &self, block: &Block, height: u32, - inputs: HashMap, + inputs: UtxoMap, ) -> Result<(), BlockchainError> { + let mtp = match self.get_mtp(height) { + Ok(array) => { + let mtp = array[array.len().div(2)]; + + #[cfg(not(feature = "std"))] + let time = DisableTime; + #[cfg(feature = "std")] + let time = StdNodeTime; + Consensus::validate_block_time(block.header.time, mtp, time)?; + Some(mtp) + } + _ => None, + }; + if !block.check_merkle_root() { return Err(BlockchainError::BlockValidation( BlockValidationErrors::BadMerkleRoot, @@ -775,12 +790,14 @@ impl ChainState { // Validate block transactions let subsidy = read_lock!(self).consensus.get_subsidy(height); let verify_script = self.verify_script(height); + #[cfg(feature = "bitcoinconsensus")] let flags = self.get_validation_flags(height, block.header.block_hash()); #[cfg(not(feature = "bitcoinconsensus"))] let flags = 0; Consensus::verify_block_transactions( height, + mtp, inputs, &block.txdata, subsidy, @@ -828,7 +845,7 @@ impl BlockchainInterface for ChainState, + inputs: UtxoMap, del_hashes: Vec, acc: Stump, ) -> Result<(), Self::Error> { @@ -1001,6 +1018,26 @@ impl BlockchainInterface for ChainState UpdatableChainstate for ChainState { + fn get_mtp(&self, height: u32) -> Result<[u32; 11], BlockchainError> { + let mut initial_array = [0u32; 11]; + + for i in 0..11 { + let time = match self.get_block_hash(height.saturating_sub(i)) { + Ok(hash) => self.get_block_header(&hash)?.time, + _ => { + warn!("Block timestamp : {i} Not found"); + 0 + } + }; + initial_array[10 - i as usize] = time; + } + //This is the case where we didnt find even the block in the given height + if initial_array[10] == 0 { + return Err(BlockchainError::BlockNotPresent); + } + Ok(initial_array) + } + fn switch_chain(&self, new_tip: BlockHash) -> Result<(), BlockchainError> { let new_tip = self.get_block_header(&new_tip)?; self.reorg(new_tip) @@ -1071,7 +1108,7 @@ impl UpdatableChainstate for ChainState, + inputs: UtxoMap, del_hashes: Vec, ) -> Result { let header = self.get_disk_block_header(&block.block_hash())?; @@ -1351,6 +1388,7 @@ mod test { use super::UpdatableChainstate; use crate::prelude::HashMap; use crate::pruned_utreexo::consensus::Consensus; + use crate::pruned_utreexo::utxo_data::UtxoData; use crate::AssumeValidArg; use crate::KvChainStore; use crate::Network; @@ -1367,7 +1405,7 @@ mod test { fn decode_block_and_inputs( block_file: File, stxos_file: File, - ) -> (Block, HashMap) { + ) -> (Block, HashMap) { let block_bytes = zstd::decode_all(block_file).unwrap(); let block: Block = deserialize(&block_bytes).unwrap(); @@ -1381,7 +1419,7 @@ mod test { .iter() .skip(1) // Skip the coinbase transaction .flat_map(|tx| &tx.input) - .map(|txin| (txin.previous_output, stxos.remove(0))) + .map(|txin| (txin.previous_output, UtxoData(stxos.remove(0), 0))) .collect(); assert!(stxos.is_empty(), "Moved all stxos to the inputs map"); diff --git a/crates/floresta-chain/src/pruned_utreexo/consensus.rs b/crates/floresta-chain/src/pruned_utreexo/consensus.rs index 0d3e90a6..861c4990 100644 --- a/crates/floresta-chain/src/pruned_utreexo/consensus.rs +++ b/crates/floresta-chain/src/pruned_utreexo/consensus.rs @@ -5,7 +5,10 @@ extern crate alloc; use core::ffi::c_uint; +use core::ops::Sub; +use bitcoin::absolute::Height; +use bitcoin::absolute::Time; use bitcoin::block::Header as BlockHeader; use bitcoin::consensus::Encodable; use bitcoin::hashes::sha256; @@ -13,7 +16,6 @@ use bitcoin::hashes::Hash; use bitcoin::Block; use bitcoin::BlockHash; use bitcoin::CompactTarget; -use bitcoin::OutPoint; use bitcoin::ScriptBuf; use bitcoin::Target; use bitcoin::Transaction; @@ -30,8 +32,13 @@ use sha2::Sha512_256; use super::chainparams::ChainParams; use super::error::BlockValidationErrors; use super::error::BlockchainError; +use super::nodetime::NodeTime; +use super::nodetime::HOUR; +use super::utxo_data::UtxoMap; use crate::TransactionError; +pub const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000_ffff; + /// The value of a single coin in satoshis. pub const COIN_VALUE: u64 = 100_000_000; @@ -110,10 +117,10 @@ impl Consensus { /// - The transaction must not have duplicate inputs /// - The transaction must not spend more coins than it claims in the inputs /// - The transaction must have valid scripts - #[allow(unused)] pub fn verify_block_transactions( height: u32, - mut utxos: HashMap, + mtp: Option, + mut utxos: UtxoMap, transactions: &[Transaction], subsidy: u64, verify_script: bool, @@ -170,6 +177,14 @@ impl Consensus { error, } })?; + if let Some(mtp) = mtp { + Self::validate_locktime(input, transaction, &utxos, height, mtp).map_err( + |error| TransactionError { + txid: transaction.compute_txid(), + error, + }, + )?; + } // TODO check also witness script size } @@ -193,7 +208,14 @@ impl Consensus { #[cfg(feature = "bitcoinconsensus")] if verify_script { transaction - .verify_with_flags(|outpoint| utxos.remove(outpoint), flags) + .verify_with_flags( + // TO-DO: Make this less horrible to understand + |outpoint| match utxos.remove(outpoint) { + Some(utxodata) => Some(utxodata.0), + _ => None, + }, + flags, + ) .map_err(|err| TransactionError { txid: transaction.compute_txid(), error: BlockValidationErrors::ScriptValidationError(err.to_string()), @@ -224,12 +246,9 @@ impl Consensus { /// Returns the TxOut being spent by the given input. /// /// Fails if the UTXO is not present in the given hashmap. - fn get_utxo<'a>( - input: &TxIn, - utxos: &'a HashMap, - ) -> Result<&'a TxOut, BlockValidationErrors> { + fn get_utxo<'a>(input: &TxIn, utxos: &'a UtxoMap) -> Result<&'a TxOut, BlockValidationErrors> { match utxos.get(&input.previous_output) { - Some(txout) => Ok(txout), + Some(txout) => Ok(&txout.0), None => Err( // This is the case when the spender: // - Spends an UTXO that doesn't exist @@ -238,13 +257,58 @@ impl Consensus { ), } } - #[allow(unused)] + pub fn validate_block_time( + block_timestamp: u32, + mtp: u32, + time: impl NodeTime, + ) -> Result<(), BlockValidationErrors> { + if mtp > block_timestamp && block_timestamp > (time.get_time().sub(2 * HOUR)) { + return Err(BlockValidationErrors::InvalidBlockTimestamp); + } + Ok(()) + } + /// Validate the transaction locktime. fn validate_locktime( input: &TxIn, transaction: &Transaction, + out_map: &UtxoMap, height: u32, + mtp: u32, ) -> Result<(), BlockValidationErrors> { - unimplemented!("validate_locktime") + let is_relative_locked = input.sequence.is_relative_lock_time(); + if is_relative_locked { + //validate lock time + let prevout = out_map.get(&input.previous_output).unwrap(); + + let is_block_locked = input.sequence.is_height_locked(); + + if is_block_locked { + let committed_height = prevout.1; + // to retrieve the span contained in the sequence we have to just get the u32 value that the sequence contains. + let height_span = input.sequence.0 & SEQUENCE_LOCKTIME_MASK; + // if the committed height + the span is greater than the current height, the transaction is invalid. + if committed_height + height_span > height { + return Err(BlockValidationErrors::BadRelativeLockTime); + } + } + if !is_block_locked { + let committed_time = prevout.1; + // here we have to shift the sequence 16 bits to the left and 16 to the right get the time lock without the flag messing with us. + let time_lock = (input.sequence.0 & SEQUENCE_LOCKTIME_MASK) * 512_u32; + if committed_time + time_lock > mtp { + return Err(BlockValidationErrors::BadRelativeLockTime); + } + } + } + if !is_relative_locked + && !transaction.is_absolute_timelock_satisfied( + Height::from_consensus(height).unwrap(), + Time::from_consensus(mtp).unwrap(), + ) + { + return Err(BlockValidationErrors::BadAbsoluteLockTime); + } + Ok(()) } /// Validates the script size and the number of sigops in a scriptpubkey or scriptsig. fn validate_script_size(script: &ScriptBuf) -> Result<(), BlockValidationErrors> { diff --git a/crates/floresta-chain/src/pruned_utreexo/error.rs b/crates/floresta-chain/src/pruned_utreexo/error.rs index 59a45e45..4343fdc2 100644 --- a/crates/floresta-chain/src/pruned_utreexo/error.rs +++ b/crates/floresta-chain/src/pruned_utreexo/error.rs @@ -35,6 +35,7 @@ pub struct TransactionError { #[derive(Clone, Debug, PartialEq)] pub enum BlockValidationErrors { + InvalidBlockTimestamp, InvalidCoinbase(String), UtxoNotFound(OutPoint), ScriptValidationError(String), @@ -53,6 +54,8 @@ pub enum BlockValidationErrors { BadBip34, InvalidProof, CoinbaseNotMatured, + BadRelativeLockTime, + BadAbsoluteLockTime, } impl Display for TransactionError { @@ -64,6 +67,15 @@ impl Display for TransactionError { impl Display for BlockValidationErrors { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { + BlockValidationErrors::InvalidBlockTimestamp => { + write!(f, "A block contains a invalid timestamp") + } + BlockValidationErrors::BadAbsoluteLockTime => { + write!(f, "A transaction contains a invalid absolute lock time.",) + } + BlockValidationErrors::BadRelativeLockTime => { + write!(f, "A transaction contains a invalid relative lock time.",) + } BlockValidationErrors::ScriptValidationError(e) => { write!(f, "{}", e) } diff --git a/crates/floresta-chain/src/pruned_utreexo/mod.rs b/crates/floresta-chain/src/pruned_utreexo/mod.rs index 0822074d..08e06125 100644 --- a/crates/floresta-chain/src/pruned_utreexo/mod.rs +++ b/crates/floresta-chain/src/pruned_utreexo/mod.rs @@ -15,7 +15,6 @@ use bitcoin::block::Header as BlockHeader; use bitcoin::hashes::sha256; use bitcoin::Block; use bitcoin::BlockHash; -use bitcoin::OutPoint; use bitcoin::Transaction; use bitcoin::TxOut; use rustreexo::accumulator::node_hash::NodeHash; @@ -23,6 +22,7 @@ use rustreexo::accumulator::proof::Proof; use rustreexo::accumulator::stump::Stump; use self::partial_chain::PartialChainState; +use self::utxo_data::UtxoMap; use crate::prelude::*; use crate::BestChain; use crate::BlockConsumer; @@ -88,7 +88,7 @@ pub trait BlockchainInterface { &self, block: &Block, proof: Proof, - inputs: HashMap, + inputs: UtxoMap, del_hashes: Vec, acc: Stump, ) -> Result<(), Self::Error>; @@ -108,7 +108,7 @@ pub trait UpdatableChainstate { &self, block: &Block, proof: Proof, - inputs: HashMap, + inputs: UtxoMap, del_hashes: Vec, ) -> Result; @@ -155,6 +155,15 @@ pub trait UpdatableChainstate { /// This mimics the behaviour of checking every block before this block, and continues /// from this point fn mark_chain_as_assumed(&self, acc: Stump, tip: BlockHash) -> Result; + + ///Get the mtp of the block with from the given height + /// + ///The mtp is a 11 timestamps array that is used to calculate the median time past. + ///This function returns the last 11 known timestamps of the block with the given height. + ///Unknown timestamps are represented as 0. + /// + ///Returns None if the block isnt found. + fn get_mtp(&self, height: u32) -> Result<[u32; 11], BlockchainError>; } /// [ChainStore] is a trait defining how we interact with our chain database. This definitions @@ -201,6 +210,10 @@ pub enum Notification { } impl UpdatableChainstate for Arc { + fn get_mtp(&self, height: u32) -> Result<[u32; 11], BlockchainError> { + T::get_mtp(self, height) + } + fn flush(&self) -> Result<(), BlockchainError> { T::flush(self) } @@ -213,7 +226,7 @@ impl UpdatableChainstate for Arc { &self, block: &Block, proof: Proof, - inputs: HashMap, + inputs: UtxoMap, del_hashes: Vec, ) -> Result { T::connect_block(self, block, proof, inputs, del_hashes) @@ -347,7 +360,7 @@ impl BlockchainInterface for Arc { &self, block: &Block, proof: Proof, - inputs: HashMap, + inputs: UtxoMap, del_hashes: Vec, acc: Stump, ) -> Result<(), Self::Error> { @@ -358,3 +371,75 @@ impl BlockchainInterface for Arc { T::get_fork_point(self, block) } } +/// Module to delegate local-time context. +/// +/// The consumer of `Floresta-chain` has the option to implement [`NodeTime`] if on a non-std environment.([`get_time()`] implementation that returns 0u32 will disable time checks.) +/// +/// On std you can just use a instance [`StdNodeTime`] as input. +pub mod nodetime { + /// Disable empty struct. + /// + /// Meant to be used in cases to disable time verifications + pub struct DisableTime; + /// One Hour in seconds constant. + pub const HOUR: u32 = 60 * 60; + /// Trait to return time-related context of the chain. + /// + /// [`get_time()`] should return a the latest [unix timestamp](https://en.wikipedia.org/wiki/Unix_time) when the consumer has time-notion. + /// + /// if the consumer does not have any time notion or MTP control, its safe to use `0u32` to disable any validations on time. + pub trait NodeTime { + /// Should return a unix timestamp or 0 to skip any time related validation. + fn get_time(&self) -> u32; + } + impl NodeTime for DisableTime { + fn get_time(&self) -> u32 { + // we simply return zero to disable time checks + 0 + } + } + #[cfg(feature = "std")] + /// A module to provide the standard implementation of [`NodeTime`] trait. It uses [`std::time::SystemTime`] to get the current time. + pub mod standard_node_time { + extern crate std; + use std::time; + /// A empty struct to implement [`NodeTime`] trait using [`std::time::SystemTime`] + pub struct StdNodeTime; + impl super::NodeTime for StdNodeTime { + fn get_time(&self) -> u32 { + time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .unwrap() + .as_secs() as u32 + } + } + } +} +///Module to hold methods and structs related to UTXO data. +pub mod utxo_data { + + use bitcoin::OutPoint; + + use super::*; + + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + /// A struct to hold unverified UTXOs and its metadata. + pub struct UtxoData( + /// The transaction output that created this UTXO. + pub bitcoin::TxOut, + /// The time lock value of the utxo. + pub u32, + ); + + impl From for UtxoData { + fn from(v: TxOut) -> Self { + UtxoData(v, 0) + } + } + impl From<&TxOut> for UtxoData { + fn from(v: &TxOut) -> Self { + UtxoData(v.to_owned(), 0) + } + } + pub(crate) type UtxoMap = HashMap; +} diff --git a/crates/floresta-chain/src/pruned_utreexo/partial_chain.rs b/crates/floresta-chain/src/pruned_utreexo/partial_chain.rs index c7950599..c1c45323 100644 --- a/crates/floresta-chain/src/pruned_utreexo/partial_chain.rs +++ b/crates/floresta-chain/src/pruned_utreexo/partial_chain.rs @@ -11,7 +11,7 @@ //! This choice removes the use of costly atomic operations, but opens space for design flaws //! and memory unsoundness, so here are some tips about this module and how people looking for //! extend or use this code should proceed: -//! +//! //! - Shared ownership is forbidden: if you have two threads or tasks owning this, you'll have //! data race. If you want to hold shared ownership for this module, you need to place a //! [PartialChainState] inside an `Arc` yourself. Don't just Arc this and expect it to @@ -36,6 +36,7 @@ use super::chainparams::ChainParams; use super::consensus::Consensus; use super::error::BlockValidationErrors; use super::error::BlockchainError; +use super::utxo_data::UtxoMap; use super::BlockchainInterface; use super::UpdatableChainstate; use crate::UtreexoBlock; @@ -171,7 +172,7 @@ impl PartialChainStateInner { &mut self, block: &bitcoin::Block, proof: rustreexo::accumulator::proof::Proof, - inputs: HashMap, + inputs: UtxoMap, del_hashes: Vec, ) -> Result { let height = self.current_height + 1; @@ -208,7 +209,7 @@ impl PartialChainStateInner { &self, block: &bitcoin::Block, height: u32, - inputs: HashMap, + inputs: UtxoMap, ) -> Result<(), BlockchainError> { if !block.check_merkle_root() { return Err(BlockchainError::BlockValidation( @@ -244,6 +245,7 @@ impl PartialChainStateInner { let flags = 0; Consensus::verify_block_transactions( height, + None, inputs, &block.txdata, subsidy, @@ -262,7 +264,7 @@ impl PartialChainState { /// [PartialChainState] is through our APIs, and we make sure this [UnsafeCell] is /// always valid. /// The reference returned here **should not** leak through the API, as there's no - /// synchronization mechanims for it. + /// synchronization mechanism for it. #[inline(always)] #[must_use] #[doc(hidden)] @@ -277,7 +279,7 @@ impl PartialChainState { /// [PartialChainState] is through our APIs, and we make sure this [UnsafeCell] is /// always valid. /// The reference returned here **should not** leak through the API, as there's no - /// synchronization mechanims for it. + /// synchronization mechanism for it. #[inline(always)] #[allow(clippy::mut_from_ref)] #[must_use] @@ -307,11 +309,14 @@ impl PartialChainState { } impl UpdatableChainstate for PartialChainState { + fn get_mtp(&self, _height: u32) -> Result<[u32; 11], BlockchainError> { + unimplemented!("a partialChainState will probably give an incomplete MTP") + } fn connect_block( &self, block: &bitcoin::Block, proof: rustreexo::accumulator::proof::Proof, - inputs: HashMap, + inputs: UtxoMap, del_hashes: Vec, ) -> Result { self.inner_mut() @@ -429,7 +434,7 @@ impl BlockchainInterface for PartialChainState { &self, _block: &bitcoin::Block, _proof: rustreexo::accumulator::proof::Proof, - _inputs: HashMap, + _inputs: UtxoMap, _del_hashes: Vec, _acc: Stump, ) -> Result<(), Self::Error> { diff --git a/crates/floresta-chain/src/pruned_utreexo/udata.rs b/crates/floresta-chain/src/pruned_utreexo/udata.rs index ead19e73..50df01d7 100644 --- a/crates/floresta-chain/src/pruned_utreexo/udata.rs +++ b/crates/floresta-chain/src/pruned_utreexo/udata.rs @@ -300,6 +300,7 @@ pub mod proof_util { use super::LeafData; use crate::prelude::*; + use crate::pruned_utreexo::utxo_data::UtxoData; use crate::pruned_utreexo::BlockchainInterface; use crate::CompactLeafData; use crate::ScriptPubkeyType; @@ -333,7 +334,7 @@ pub mod proof_util { udata: &UData, transactions: &[Transaction], chain: &Chain, - ) -> Result<(Proof, Vec, HashMap), Chain::Error> { + ) -> Result<(Proof, Vec, HashMap), Chain::Error> { let targets = udata.proof.targets.iter().map(|target| target.0).collect(); let hashes = udata .proof @@ -357,7 +358,7 @@ pub mod proof_util { txid, vout: vout as u32, }, - out.clone(), + UtxoData(out.clone(), 0), ); } @@ -369,7 +370,7 @@ pub mod proof_util { let leaf = reconstruct_leaf_data(&leaf, input, hash).expect("Invalid proof"); hashes.push(leaf._get_leaf_hashes()); - inputs.insert(leaf.prevout, leaf.utxo); + inputs.insert(leaf.prevout, UtxoData(leaf.utxo, 0)); } } } diff --git a/crates/floresta-wire/src/p2p_wire/node.rs b/crates/floresta-wire/src/p2p_wire/node.rs index 1f161485..914d3b35 100644 --- a/crates/floresta-wire/src/p2p_wire/node.rs +++ b/crates/floresta-wire/src/p2p_wire/node.rs @@ -227,7 +227,7 @@ where .fixed_peer .as_ref() .map(|address| { - Self::resolve_connect_host(&address, Self::get_port(config.network.into())) + Self::resolve_connect_host(address, Self::get_port(config.network.into())) }) .transpose()?; diff --git a/crates/floresta-wire/src/p2p_wire/running_node.rs b/crates/floresta-wire/src/p2p_wire/running_node.rs index 9c5518c3..7f772858 100644 --- a/crates/floresta-wire/src/p2p_wire/running_node.rs +++ b/crates/floresta-wire/src/p2p_wire/running_node.rs @@ -677,7 +677,10 @@ where // to be invalidated. match e { BlockValidationErrors::InvalidCoinbase(_) + | BlockValidationErrors::InvalidBlockTimestamp | BlockValidationErrors::UtxoNotFound(_) + | BlockValidationErrors::BadAbsoluteLockTime + | BlockValidationErrors::BadRelativeLockTime | BlockValidationErrors::ScriptValidationError(_) | BlockValidationErrors::InvalidOutput | BlockValidationErrors::ScriptError diff --git a/crates/floresta-wire/src/p2p_wire/sync_node.rs b/crates/floresta-wire/src/p2p_wire/sync_node.rs index 7cd027af..e4470416 100644 --- a/crates/floresta-wire/src/p2p_wire/sync_node.rs +++ b/crates/floresta-wire/src/p2p_wire/sync_node.rs @@ -220,7 +220,10 @@ where // to be invalidated. match e { BlockValidationErrors::InvalidCoinbase(_) + | BlockValidationErrors::InvalidBlockTimestamp | BlockValidationErrors::UtxoNotFound(_) + | BlockValidationErrors::BadAbsoluteLockTime + | BlockValidationErrors::BadRelativeLockTime | BlockValidationErrors::ScriptValidationError(_) | BlockValidationErrors::InvalidOutput | BlockValidationErrors::ScriptError