#![warn(missing_docs)]
pub mod chain;
use crate::network::{ConnectionType, PeerTimeoutConfig};
mod network;
mod broadcaster;
pub mod builder;
pub mod client;
pub mod error;
pub mod messages;
pub mod node;
use bitcoin::OutPoint;
use chain::Filter;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::PathBuf;
#[doc(inline)]
pub use chain::checkpoints::HeaderCheckpoint;
#[doc(inline)]
pub use tokio::sync::mpsc::Receiver;
use tokio::sync::mpsc::Sender;
#[doc(inline)]
pub use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::mpsc::UnboundedSender;
#[doc(inline)]
pub use {
crate::builder::Builder,
crate::chain::ChainState,
crate::client::{Client, Requester},
crate::error::{ClientError, NodeError},
crate::messages::{Event, Info, Progress, RejectPayload, SyncUpdate, Warning},
crate::node::Node,
};
#[doc(inline)]
pub use bitcoin::{
bip158::BlockFilter, block::Header, p2p::address::AddrV2, p2p::message_network::RejectReason,
p2p::ServiceFlags, Address, Block, BlockHash, FeeRate, Network, ScriptBuf, Transaction, Wtxid,
};
pub extern crate tokio;
#[derive(Debug, Clone)]
pub struct IndexedBlock {
pub height: u32,
pub block: Block,
}
impl IndexedBlock {
pub(crate) fn new(height: u32, block: Block) -> Self {
Self { height, block }
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum FilterType {
#[default]
Basic,
}
#[derive(Debug, Clone, Copy, Default)]
enum BlockType {
#[default]
Legacy,
Witness,
}
impl From<FilterType> for u8 {
fn from(value: FilterType) -> Self {
match value {
FilterType::Basic => 0x00,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IndexedFilter {
height: u32,
filter: Filter,
}
impl IndexedFilter {
fn new(height: u32, filter: Filter) -> Self {
Self { height, filter }
}
pub fn height(&self) -> u32 {
self.height
}
pub fn block_hash(&self) -> BlockHash {
self.filter.block_hash()
}
pub fn contains_any<'a>(&'a self, scripts: impl Iterator<Item = &'a ScriptBuf>) -> bool {
self.filter.contains_any(scripts)
}
pub fn block_filter(self) -> BlockFilter {
self.filter.into_filter()
}
pub fn into_contents(self) -> Vec<u8> {
self.filter.contents()
}
}
impl std::cmp::PartialOrd for IndexedFilter {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for IndexedFilter {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.height.cmp(&other.height)
}
}
#[derive(Debug, Clone)]
enum TrustedPeerInner {
Addr(AddrV2),
Hostname(String),
}
impl From<AddrV2> for TrustedPeerInner {
fn from(addr: AddrV2) -> Self {
Self::Addr(addr)
}
}
#[derive(Debug, Clone)]
pub struct TrustedPeer {
address: TrustedPeerInner,
port: Option<u16>,
known_services: ServiceFlags,
}
impl TrustedPeer {
pub fn new(address: impl Into<AddrV2>, port: Option<u16>, services: ServiceFlags) -> Self {
Self {
address: TrustedPeerInner::Addr(address.into()),
port,
known_services: services,
}
}
pub fn from_ip(ip_addr: impl Into<IpAddr>) -> Self {
let address = match ip_addr.into() {
IpAddr::V4(ip) => AddrV2::Ipv4(ip),
IpAddr::V6(ip) => AddrV2::Ipv6(ip),
};
Self {
address: TrustedPeerInner::Addr(address),
port: None,
known_services: ServiceFlags::NONE,
}
}
pub fn from_socket_addr(socket_addr: impl Into<SocketAddr>) -> Self {
let socket_addr: SocketAddr = socket_addr.into();
let address = match socket_addr {
SocketAddr::V4(ip) => AddrV2::Ipv4(*ip.ip()),
SocketAddr::V6(ip) => AddrV2::Ipv6(*ip.ip()),
};
Self {
address: TrustedPeerInner::Addr(address),
port: Some(socket_addr.port()),
known_services: ServiceFlags::NONE,
}
}
pub fn from_hostname(hostname: impl Into<String>, port: u16) -> Self {
Self {
address: TrustedPeerInner::Hostname(hostname.into()),
port: Some(port),
known_services: ServiceFlags::NONE,
}
}
pub fn port(&self) -> Option<u16> {
self.port
}
pub fn services(&self) -> ServiceFlags {
self.known_services
}
pub fn set_services(&mut self, services: ServiceFlags) {
self.known_services = services;
}
}
impl From<(IpAddr, Option<u16>)> for TrustedPeer {
fn from(value: (IpAddr, Option<u16>)) -> Self {
let address = match value.0 {
IpAddr::V4(ip) => AddrV2::Ipv4(ip),
IpAddr::V6(ip) => AddrV2::Ipv6(ip),
};
TrustedPeer::new(address, value.1, ServiceFlags::NONE)
}
}
impl From<IpAddr> for TrustedPeer {
fn from(value: IpAddr) -> Self {
TrustedPeer::from_ip(value)
}
}
impl From<SocketAddr> for TrustedPeer {
fn from(value: SocketAddr) -> Self {
TrustedPeer::from_socket_addr(value)
}
}
#[derive(Debug, Clone)]
pub struct Socks5Proxy(SocketAddr);
impl Socks5Proxy {
pub fn new(socket_addr: impl Into<SocketAddr>) -> Self {
Self(socket_addr.into())
}
pub const fn local() -> Self {
Socks5Proxy(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
9050,
))
}
}
impl From<SocketAddr> for Socks5Proxy {
fn from(value: SocketAddr) -> Self {
Self(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NodeState {
Behind,
HeadersSynced,
FilterHeadersSynced,
FiltersSynced,
}
#[derive(Debug)]
struct Config {
required_peers: u8,
white_list: Vec<TrustedPeer>,
whitelist_only: bool,
data_path: Option<PathBuf>,
chain_state: Option<ChainState>,
connection_type: ConnectionType,
peer_timeout_config: PeerTimeoutConfig,
filter_type: FilterType,
block_type: BlockType,
}
impl Default for Config {
fn default() -> Self {
Self {
required_peers: 1,
white_list: Default::default(),
whitelist_only: Default::default(),
data_path: Default::default(),
chain_state: Default::default(),
connection_type: Default::default(),
peer_timeout_config: PeerTimeoutConfig::default(),
filter_type: FilterType::default(),
block_type: BlockType::default(),
}
}
}
pub async fn lookup_host<S: AsRef<str>>(hostname: S) -> Vec<IpAddr> {
crate::network::dns::lookup_hostname(hostname.as_ref()).await
}
fn default_port_from_network(network: &Network) -> u16 {
match network {
Network::Bitcoin => 8333,
Network::Testnet => 18333,
Network::Testnet4 => 48333,
Network::Signet => 38333,
Network::Regtest => 18444,
}
}
#[derive(Debug, Clone)]
struct Dialog {
info_tx: Sender<Info>,
warn_tx: UnboundedSender<Warning>,
event_tx: UnboundedSender<Event>,
}
impl Dialog {
fn new(
info_tx: Sender<Info>,
warn_tx: UnboundedSender<Warning>,
event_tx: UnboundedSender<Event>,
) -> Self {
Self {
info_tx,
warn_tx,
event_tx,
}
}
fn send_warning(&self, warning: Warning) {
let _ = self.warn_tx.send(warning);
}
async fn send_info(&self, info: Info) {
let _ = self.info_tx.send(info).await;
}
fn send_event(&self, message: Event) {
let _ = self.event_tx.send(message);
}
}
#[derive(Debug, Clone)]
pub struct Package {
parent: Transaction,
child: Option<Transaction>,
}
impl Package {
pub fn new_single(transaction: Transaction) -> Self {
Self {
parent: transaction,
child: None,
}
}
pub fn new_one_parent_one_child(
parent: Transaction,
child: Transaction,
) -> Result<Self, error::PackageError> {
Self::child_depends_on_parent(&parent, &child)?;
Ok(Self {
parent,
child: Some(child),
})
}
pub fn from_vec(mut transactions: Vec<Transaction>) -> Result<Self, error::PackageError> {
match transactions.len() {
1 => Ok(Package::new_single(transactions.remove(0))),
2 => {
let parent = transactions.remove(0);
let child = transactions.remove(0);
Ok(Package::new_one_parent_one_child(parent, child)?)
}
invalid => Err(error::PackageError::InvalidPackageLength(invalid)),
}
}
fn child_depends_on_parent(
parent: &Transaction,
child: &Transaction,
) -> Result<(), error::PackageError> {
let outpoints = {
let txid = parent.compute_txid();
let mut outpoints = Vec::with_capacity(parent.output.len());
for vout in 0..parent.output.len() {
outpoints.push(OutPoint {
txid,
vout: vout as u32,
});
}
outpoints
};
if !child
.input
.iter()
.any(|input| outpoints.contains(&input.previous_output))
{
return Err(error::PackageError::UnrelatedTransactions);
}
Ok(())
}
fn advertise_package(&self) -> Wtxid {
match &self.child {
Some(child) => child.compute_wtxid(),
None => self.parent.compute_wtxid(),
}
}
fn parent(&self) -> Transaction {
self.parent.clone()
}
fn child(&self) -> Option<Transaction> {
self.child.clone()
}
}
impl From<Transaction> for Package {
fn from(value: Transaction) -> Self {
Package {
parent: value,
child: None,
}
}
}
macro_rules! impl_sourceless_error {
($e:ident) => {
impl std::error::Error for $e {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
};
}
pub(crate) use impl_sourceless_error;
macro_rules! debug {
($expr:expr) => {
#[cfg(debug_assertions)]
println!("{}", $expr)
};
}
pub(crate) use debug;
#[cfg(test)]
macro_rules! impl_deserialize {
($t:ident, $for:ident) => {
impl<'de> serde::Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let bytes = hex::decode(&s).map_err(serde::de::Error::custom)?;
let bitcoin_type: $for =
bitcoin::consensus::deserialize(&bytes).map_err(serde::de::Error::custom)?;
Ok($t(bitcoin_type))
}
}
};
}
#[cfg(test)]
pub(crate) use impl_deserialize;