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
35 changes: 34 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ pub use {
crate::error::{ClientError, NodeError},
crate::messages::{Event, Log, Progress, RejectPayload, SyncUpdate, Warning},
crate::network::PeerTimeoutConfig,
crate::node::{Node, NodeState},
crate::node::Node,
};

#[doc(inline)]
Expand Down Expand Up @@ -468,6 +468,39 @@ pub enum FilterSyncPolicy {
Continue,
}

/// The state of the node with respect to connected peers.
#[derive(Debug, Clone, Copy)]
pub enum NodeState {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a personal preference so not holding up this patch, but to me NodeState is so specific to node, I would favor keeping it there so that there is clear ownership. Node defines its states. I think this is inline with my general preference to use lib.rs as a "table of contents" which holds very little definitions itself.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that reasoning definitely. It is used as part of the Log message though. If it does gain function in the future, I think it should go back into node for sure.

/// We are behind on block headers according to our peers.
Behind,
/// We may start downloading compact block filter headers.
HeadersSynced,
/// We may start scanning compact block filters.
FilterHeadersSynced,
/// We may start asking for blocks with matches.
FiltersSynced,
/// We found all known transactions to the wallet.
TransactionsSynced,
}

impl core::fmt::Display for NodeState {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
NodeState::Behind => {
write!(f, "Requesting block headers.")
}
NodeState::HeadersSynced => {
write!(f, "Requesting compact filter headers.")
}
NodeState::FilterHeadersSynced => {
write!(f, "Requesting compact block filters.")
}
NodeState::FiltersSynced => write!(f, "Downloading blocks with relevant transactions."),
NodeState::TransactionsSynced => write!(f, "Fully synced to the highest block."),
}
}
}

macro_rules! log {
($dialog:expr, $expr:expr) => {
match $dialog.log_level {
Expand Down
7 changes: 2 additions & 5 deletions src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@ use bitcoin::{block::Header, p2p::message_network::RejectReason, FeeRate, Script
#[cfg(feature = "filter-control")]
use crate::IndexedFilter;
use crate::{
chain::checkpoints::HeaderCheckpoint, DisconnectedHeader, IndexedBlock, TrustedPeer,
chain::checkpoints::HeaderCheckpoint, DisconnectedHeader, IndexedBlock, NodeState, TrustedPeer,
TxBroadcast,
};

use super::{
error::{FetchBlockError, FetchHeaderError},
node::NodeState,
};
use super::error::{FetchBlockError, FetchHeaderError};

/// Informational messages emitted by a node
#[derive(Debug, Clone)]
Expand Down
29 changes: 26 additions & 3 deletions src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use bitcoin::{
p2p::{message::CommandString, Magic},
};
use std::time::Duration;
use tokio::time::Instant;

pub(crate) mod counter;
pub(crate) mod dns;
Expand All @@ -19,6 +20,11 @@ pub(crate) mod reader;
pub(crate) mod tor;
pub(crate) mod traits;

pub const PROTOCOL_VERSION: u32 = 70016;
pub const KYOTO_VERSION: &str = "0.8.0";
pub const RUST_BITCOIN_VERSION: &str = "0.32.4";
const THIRTY_MINS: u64 = 60 * 30;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct PeerId(pub(crate) u32);

Expand Down Expand Up @@ -59,9 +65,26 @@ impl PeerTimeoutConfig {
}
}

pub const PROTOCOL_VERSION: u32 = 70016;
pub const KYOTO_VERSION: &str = "0.8.0";
pub const RUST_BITCOIN_VERSION: &str = "0.32.4";
pub(crate) struct LastBlockMonitor {
last_block: Option<Instant>,
}

impl LastBlockMonitor {
pub(crate) fn new() -> Self {
Self { last_block: None }
}

pub(crate) fn reset(&mut self) {
self.last_block = Some(Instant::now())
}

pub(crate) fn stale(&self) -> bool {
if let Some(time) = self.last_block {
return Instant::now().duration_since(time) > Duration::from_secs(THIRTY_MINS);
}
false
}
}

pub(crate) struct V1Header {
magic: Magic,
Expand Down
65 changes: 3 additions & 62 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ use bitcoin::{
},
Block, BlockHash, Network, ScriptBuf,
};
use tokio::sync::{mpsc::Receiver, Mutex, RwLock};
use tokio::{
select,
sync::mpsc::{self},
};
use tokio::{
sync::{mpsc::Receiver, Mutex, RwLock},
time::Instant,
};

use crate::{
chain::{
Expand All @@ -28,8 +25,8 @@ use crate::{
db::traits::{HeaderStore, PeerStore},
error::FetchHeaderError,
filters::{cfheader_chain::AppendAttempt, error::CFilterSyncError},
network::{peer_map::PeerMap, PeerId, PeerTimeoutConfig},
FilterSyncPolicy, RejectPayload, TxBroadcastPolicy,
network::{peer_map::PeerMap, LastBlockMonitor, PeerId, PeerTimeoutConfig},
FilterSyncPolicy, NodeState, RejectPayload, TxBroadcastPolicy,
};

use super::{
Expand All @@ -47,47 +44,9 @@ use super::{

pub(crate) const WTXID_VERSION: u32 = 70016;
const LOOP_TIMEOUT: u64 = 1;
const THIRTY_MINS: u64 = 60 * 30;

type PeerRequirement = usize;

// This struct detects for stale tips and requests headers if no blocks were found after 30 minutes of wait time.
pub(crate) struct LastBlockMonitor {
last_block: Option<Instant>,
}

impl LastBlockMonitor {
pub(crate) fn new() -> Self {
Self { last_block: None }
}

pub(crate) fn reset(&mut self) {
self.last_block = Some(Instant::now())
}

pub(crate) fn stale(&self) -> bool {
if let Some(time) = self.last_block {
return Instant::now().duration_since(time) > Duration::from_secs(THIRTY_MINS);
}
false
}
}

/// The state of the node with respect to connected peers.
#[derive(Debug, Clone, Copy)]
pub enum NodeState {
/// We are behind on block headers according to our peers.
Behind,
/// We may start downloading compact block filter headers.
HeadersSynced,
/// We may start scanning compact block filters.
FilterHeadersSynced,
/// We may start asking for blocks with matches.
FiltersSynced,
/// We found all known transactions to the wallet.
TransactionsSynced,
}

/// A compact block filter node. Nodes download Bitcoin block headers, block filters, and blocks to send relevant events to a client.
#[derive(Debug)]
pub struct Node<H: HeaderStore, P: PeerStore> {
Expand Down Expand Up @@ -801,21 +760,3 @@ impl<H: HeaderStore, P: PeerStore> Node<H, P> {
.map_err(NodeError::HeaderDatabase)
}
}

impl core::fmt::Display for NodeState {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
NodeState::Behind => {
write!(f, "Requesting block headers.")
}
NodeState::HeadersSynced => {
write!(f, "Requesting compact filter headers.")
}
NodeState::FilterHeadersSynced => {
write!(f, "Requesting compact block filters.")
}
NodeState::FiltersSynced => write!(f, "Downloading blocks with relevant transactions."),
NodeState::TransactionsSynced => write!(f, "Fully synced to the highest block."),
}
}
}