From 4cb1728fc6d80118a32f3c69adc50494b1b7aa24 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Fri, 1 Nov 2024 08:27:07 -1000 Subject: [PATCH 1/2] feat: add support for testnet4 --- Cargo.toml | 4 ++ example/testnet4.rs | 77 +++++++++++++++++++++++++++++++++++++++ src/chain/checkpoints.rs | 36 ++++++++++++++++++ src/chain/header_batch.rs | 3 ++ src/network/dns.rs | 1 + src/prelude.rs | 2 + 6 files changed, 123 insertions(+) create mode 100644 example/testnet4.rs diff --git a/Cargo.toml b/Cargo.toml index e6612da..7d9f10a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,10 @@ path = "src/lib.rs" name = "signet" path = "example/signet.rs" +[[example]] +name = "testnet" +path = "example/testnet4.rs" + [[example]] name = "rescan" path = "example/rescan.rs" diff --git a/example/testnet4.rs b/example/testnet4.rs new file mode 100644 index 0000000..4c6fa2e --- /dev/null +++ b/example/testnet4.rs @@ -0,0 +1,77 @@ +use kyoto::core::messages::NodeMessage; +use kyoto::{chain::checkpoints::HeaderCheckpoint, core::builder::NodeBuilder}; +use kyoto::{Address, Network, TrustedPeer}; +use std::collections::HashSet; +use std::{net::Ipv4Addr, str::FromStr}; + +const NETWORK: Network = Network::Testnet4; +const RECOVERY_HEIGHT: u32 = 0; + +#[tokio::main] +async fn main() { + // Add third-party logging + let subscriber = tracing_subscriber::FmtSubscriber::new(); + tracing::subscriber::set_global_default(subscriber).unwrap(); + // Use a predefined checkpoint + let checkpoint = HeaderCheckpoint::closest_checkpoint_below_height(RECOVERY_HEIGHT, NETWORK); + // Add Bitcoin scripts to scan the blockchain for + let address = Address::from_str("tb1qkqkt3ra8d44lt90t9thgy3lgucsjrtywwgq8yp") + .unwrap() + .require_network(NETWORK) + .unwrap() + .into(); + let mut addresses = HashSet::new(); + addresses.insert(address); + // Add preferred peers to connect to + let peer = TrustedPeer::from_ip(Ipv4Addr::new(95, 217, 73, 162)); + // Create a new node builder + let builder = NodeBuilder::new(NETWORK); + // Add node preferences and build the node/client + let (node, client) = builder + // Add the peers + .add_peer(peer) + // The Bitcoin scripts to monitor + .add_scripts(addresses) + // Only scan blocks strictly after an anchor checkpoint + .anchor_checkpoint(checkpoint) + // The number of connections we would like to maintain + .num_required_peers(1) + // Create the node and client + .build_node() + .unwrap(); + + // Run the node on a new task + tokio::task::spawn(async move { node.run().await }); + + // Split the client into components that send messages and listen to messages. + // With this construction, different parts of the program can take ownership of + // specific tasks. + let (sender, mut receiver) = client.split(); + // Continually listen for events until the node is synced to its peers. + loop { + if let Ok(message) = receiver.recv().await { + match message { + NodeMessage::Dialog(d) => tracing::info!("{d}"), + NodeMessage::Warning(e) => tracing::warn!("{e}"), + NodeMessage::Block(b) => drop(b), + NodeMessage::BlocksDisconnected(r) => { + for dc in r { + let warning = format!("Block disconnected {}", dc.height); + tracing::warn!(warning); + } + } + NodeMessage::Synced(update) => { + tracing::info!("Synced chain up to block {}", update.tip.height); + tracing::info!("Chain tip: {}", update.tip.hash); + break; + } + NodeMessage::ConnectionsMet => { + tracing::info!("Connected to all required peers"); + } + _ => (), + } + } + } + let _ = sender.shutdown().await; + tracing::info!("Shutting down"); +} diff --git a/src/chain/checkpoints.rs b/src/chain/checkpoints.rs index b405f31..55fbf10 100644 --- a/src/chain/checkpoints.rs +++ b/src/chain/checkpoints.rs @@ -101,6 +101,34 @@ pub const SIGNET_HEADER_CP: &[(Height, &str)] = &[ ), ]; +/// Known block hashes for Testnet4. +pub const TESTNET4_HEADER_CP: &[(Height, &str)] = &[ + ( + 0, + "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043", + ), + ( + 10_000, + "000000000037079ff4c37eed57d00eb9ddfde8737b559ffa4101b11e76c97466", + ), + ( + 20_000, + "0000000000003a28386161143be8e7cdc3d857021986c4d0ee140d852a155b59", + ), + ( + 30_000, + "000000000000000095a56b41da7618b40949a3aef84059732ff1b045cb44fbbf", + ), + ( + 40_000, + "000000000000000c1a1fad82b0e133f4772802b6dff7a95990580ae2e15c634f", + ), + ( + 50_000, + "00000000e2c8c94ba126169a88997233f07a9769e2b009fb10cad0e893eff2cb", + ), +]; + /// Known block hashes on the Bitcoin blockchain. pub const MAINNET_HEADER_CP: &[(Height, &str)] = &[ ( @@ -481,6 +509,13 @@ impl HeaderCheckpoint { }) .collect(), Network::Testnet => panic!("unimplemented network"), + Network::Testnet4 => TESTNET4_HEADER_CP + .iter() + .copied() + .map(|(height, hash)| { + HeaderCheckpoint::new(height, BlockHash::from_str(hash).unwrap()) + }) + .collect(), Network::Signet => SIGNET_HEADER_CP .iter() .copied() @@ -521,6 +556,7 @@ impl HeaderCheckpoints { let cp_list = match network { Network::Bitcoin => MAINNET_HEADER_CP.to_vec(), Network::Testnet => panic!("unimplemented network"), + Network::Testnet4 => TESTNET4_HEADER_CP.to_vec(), Network::Signet => SIGNET_HEADER_CP.to_vec(), Network::Regtest => REGTEST_HEADER_CP.to_vec(), _ => unreachable!(), diff --git a/src/chain/header_batch.rs b/src/chain/header_batch.rs index 1a97288..543b5d7 100644 --- a/src/chain/header_batch.rs +++ b/src/chain/header_batch.rs @@ -26,6 +26,9 @@ impl HeadersBatch { .zip(self.batch.iter().skip(1)) .all(|(first, second)| { let transition = Target::from_compact(first.bits).max_transition_threshold(params); + if Target::from_compact(second.bits).gt(&transition) { + println!("There is a problem"); + } first.block_hash().eq(&second.prev_blockhash) && Target::from_compact(second.bits).le(&transition) }) diff --git a/src/network/dns.rs b/src/network/dns.rs index c251690..afc6188 100644 --- a/src/network/dns.rs +++ b/src/network/dns.rs @@ -72,6 +72,7 @@ impl<'a> Dns<'a> { Network::Testnet => TESTNET_SEEDS.to_vec(), Network::Signet => SIGNET_SEEDS.to_vec(), Network::Regtest => Vec::with_capacity(0), + Network::Testnet4 => Vec::with_capacity(0), _ => unreachable!(), }; Self { seeds } diff --git a/src/prelude.rs b/src/prelude.rs index 7e6f05a..fbfd49a 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -108,6 +108,7 @@ pub(crate) fn params_from_network(network: &Network) -> Params { match network { Network::Bitcoin => Params::new(*network), Network::Testnet => panic!("unimplemented network"), + Network::Testnet4 => Params::new(*network), Network::Signet => Params::new(*network), Network::Regtest => Params::new(*network), _ => unreachable!(), @@ -118,6 +119,7 @@ pub(crate) 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, _ => unreachable!(), From 4412c7e0e15a20615de75bad08f85a591cdc2a97 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Fri, 1 Nov 2024 08:36:37 -1000 Subject: [PATCH 2/2] fix: remove bits audit --- example/testnet4.rs | 4 +++- src/chain/chain.rs | 3 ++- src/chain/header_batch.rs | 13 +++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/example/testnet4.rs b/example/testnet4.rs index 4c6fa2e..f499d23 100644 --- a/example/testnet4.rs +++ b/example/testnet4.rs @@ -7,6 +7,8 @@ use std::{net::Ipv4Addr, str::FromStr}; const NETWORK: Network = Network::Testnet4; const RECOVERY_HEIGHT: u32 = 0; +// THE TESTNET4 DOES NOT CURRENTLY HAVE PEERS THAT SERVE COMPACT BLOCK FILTERS + #[tokio::main] async fn main() { // Add third-party logging @@ -23,7 +25,7 @@ async fn main() { let mut addresses = HashSet::new(); addresses.insert(address); // Add preferred peers to connect to - let peer = TrustedPeer::from_ip(Ipv4Addr::new(95, 217, 73, 162)); + let peer = TrustedPeer::from_ip(Ipv4Addr::new(18, 189, 156, 102)); // Create a new node builder let builder = NodeBuilder::new(NETWORK); // Add node preferences and build the node/client diff --git a/src/chain/chain.rs b/src/chain/chain.rs index f292799..77b3e0c 100644 --- a/src/chain/chain.rs +++ b/src/chain/chain.rs @@ -44,6 +44,7 @@ const REORG_LOOKBACK: u32 = 7; const MAX_HEADER_SIZE: usize = 20_000; const FILTER_BASIC: u8 = 0x00; +#[allow(unused)] #[derive(Debug)] pub(crate) struct Chain { header_chain: HeaderChain, @@ -358,7 +359,7 @@ impl Chain { } // All the headers connect with each other and is the difficulty adjustment not absurd - if !header_batch.connected_with_valid_bits(&self.params).await { + if !header_batch.connected().await { return Err(HeaderSyncError::HeadersNotConnected); } diff --git a/src/chain/header_batch.rs b/src/chain/header_batch.rs index 543b5d7..8e7ecc1 100644 --- a/src/chain/header_batch.rs +++ b/src/chain/header_batch.rs @@ -1,4 +1,4 @@ -use bitcoin::{block::Header, params::Params, Target}; +use bitcoin::block::Header; use crate::{ impl_sourceless_error, @@ -20,18 +20,11 @@ impl HeadersBatch { } // Are they all logically connected? - pub(crate) async fn connected_with_valid_bits(&self, params: &Params) -> bool { + pub(crate) async fn connected(&self) -> bool { self.batch .iter() .zip(self.batch.iter().skip(1)) - .all(|(first, second)| { - let transition = Target::from_compact(first.bits).max_transition_threshold(params); - if Target::from_compact(second.bits).gt(&transition) { - println!("There is a problem"); - } - first.block_hash().eq(&second.prev_blockhash) - && Target::from_compact(second.bits).le(&transition) - }) + .all(|(first, second)| first.block_hash().eq(&second.prev_blockhash)) } // Are all the blocks of sufficient work and meet their own target?