Skip to content

Commit

Permalink
(dc) add: new struct for utxo: UtxoData, new trait for time functiona…
Browse files Browse the repository at this point in the history
…lities: NodeTime.
  • Loading branch information
jaoleal committed Dec 5, 2024
1 parent 30b707c commit 4772a7d
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 6 deletions.
40 changes: 39 additions & 1 deletion crates/floresta-chain/src/pruned_utreexo/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ extern crate alloc;

use core::ffi::c_uint;

use bitcoin::absolute::Height;
use bitcoin::absolute::Time;
use bitcoin::block::Header as BlockHeader;
use bitcoin::consensus::Encodable;
use bitcoin::hashes::sha256;
Expand All @@ -31,6 +33,7 @@ use sha2::Sha512_256;
use super::chainparams::ChainParams;
use super::error::BlockValidationErrors;
use super::error::BlockchainError;
use super::utxo_data::UtxoMap;
use crate::TransactionError;

/// The value of a single coin in satoshis.
Expand Down Expand Up @@ -233,12 +236,47 @@ impl Consensus {
Ok(())
}
#[allow(unused)]
/// Validate the transaction locktime. if mtp is 0, skip the validation.
fn validate_locktime(
input: &TxIn,
transaction: &Transaction,
out_map: &UtxoMap,
height: u32,
mtp: u32,
) -> Result<(), BlockValidationErrors> {
unimplemented!("validate_locktime")
if mtp == 0 {
return Ok(());
}
if input.sequence.is_relative_lock_time() {
//validate lock time
let prevout = out_map.get(&input.previous_output).unwrap();
if input.sequence.is_height_locked() {
let committed_height = prevout.get_height();
// 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;
// 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 input.sequence.is_time_locked() {
let committed_time = prevout.get_time();
// 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 << 16) >> 16) * 512_u32;
if committed_time + time_lock > mtp {
return Err(BlockValidationErrors::BadRelativeLockTime);
}
}
}
if input.sequence.enables_absolute_lock_time()
&& !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 script.
fn validate_script_size(script: &ScriptBuf) -> Result<(), BlockValidationErrors> {
Expand Down
8 changes: 8 additions & 0 deletions crates/floresta-chain/src/pruned_utreexo/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub enum BlockValidationErrors {
BadBip34,
InvalidProof,
CoinbaseNotMatured,
BadRelativeLockTime,
BadAbsoluteLockTime,
}

impl Display for TransactionError {
Expand All @@ -63,6 +65,12 @@ impl Display for TransactionError {
impl Display for BlockValidationErrors {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
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)
}
Expand Down
92 changes: 89 additions & 3 deletions crates/floresta-chain/src/pruned_utreexo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub trait BlockchainInterface {
/// Register for receiving notifications for some event. Right now it only works for
/// new blocks, but may work with transactions in the future too.
/// if a module performs some heavy-lifting on the block's data, it should pass in a
/// vector or a channel where data can be transfered to the atual worker, otherwise
/// vector or a channel where data can be transferred to the atual worker, otherwise
/// chainstate will be stuck for as long as you have work to do.
fn subscribe(&self, tx: Arc<dyn BlockConsumer>);
/// Tells whether or not we are on ibd
Expand Down Expand Up @@ -136,7 +136,7 @@ pub trait UpdatableChainstate {
/// Returns a partial chainstate from a range of blocks.
///
/// [PartialChainState] is a simplified version of `ChainState` that is used during IBD.
/// It doesn't suport reorgs, only hold headers for a subset of blocks and isn't [Sync].
/// It doesn't support reorgs, only hold headers for a subset of blocks and isn't [Sync].
/// The idea here is that you take a OS thread or some async task that will drive one
/// [PartialChainState] to completion by downloading blocks inside that chainstate's range.
/// If all goes right, it'll end without error, and you should mark blocks in this range as
Expand All @@ -152,7 +152,7 @@ pub trait UpdatableChainstate {
) -> Result<PartialChainState, BlockchainError>;
/// Marks a chain as fully-valid
///
/// This mimics the behavour of checking every block before this block, and continues
/// 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<bool, BlockchainError>;
}
Expand Down Expand Up @@ -358,3 +358,89 @@ impl<T: BlockchainInterface> BlockchainInterface for Arc<T> {
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 super::*;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// A struct to hold UTXO and its metadata.
pub struct UtxoData {
/// The transaction output that created this UTXO.
pub txout: bitcoin::TxOut,

//we need this two mostly to validate relative-locktime.
/// The height of the block that the utxo was committed.
pub height: u32,
/// The timestamp of the block that the utxo was committed.
pub time: u32,
}
impl UtxoData {
/// Creates a new UtxoData.
pub const fn new(txout: bitcoin::TxOut, height: u32, time: u32) -> Self {
Self {
txout,
height,
time,
}
}
/// Gets the output.
pub const fn get_txout(&self) -> &bitcoin::TxOut {
&self.txout
}
/// Gets the height that the output was committed.
pub const fn get_height(&self) -> u32 {
self.height
}
pub const fn get_time(&self) -> u32 {
self.time
}
}
/// A [`HashMap<bitcoin::OutPoint, UtxoData>`] alias.
///
/// Use HashMap to store and fast-find all needed UTXOs related data.
pub type UtxoMap = HashMap<bitcoin::OutPoint, UtxoData>;
}
4 changes: 3 additions & 1 deletion crates/floresta-wire/src/p2p_wire/running_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ where
continue;
}

// Aks our peers for new addresses
// Ask our peers for new addresses
periodic_job!(
self.ask_for_addresses().await,
self.last_get_address_request,
Expand Down Expand Up @@ -662,6 +662,8 @@ where
// to be invalidated.
match e {
BlockValidationErrors::InvalidCoinbase(_)
| BlockValidationErrors::BadAbsoluteLockTime
| BlockValidationErrors::BadRelativeLockTime
| BlockValidationErrors::UtxoAlreadySpent(_)
| BlockValidationErrors::ScriptValidationError(_)
| BlockValidationErrors::InvalidOutput
Expand Down
4 changes: 3 additions & 1 deletion crates/floresta-wire/src/p2p_wire/sync_node.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! A node that downlaods and validates the blockchain.
//! A node that downloads and validates the blockchain.
use std::sync::Arc;
use std::time::Duration;
Expand Down Expand Up @@ -196,6 +196,8 @@ where
// to be invalidated.
match e {
BlockValidationErrors::InvalidCoinbase(_)
| BlockValidationErrors::BadAbsoluteLockTime
| BlockValidationErrors::BadRelativeLockTime
| BlockValidationErrors::UtxoAlreadySpent(_)
| BlockValidationErrors::ScriptValidationError(_)
| BlockValidationErrors::InvalidOutput
Expand Down

0 comments on commit 4772a7d

Please sign in to comment.