Skip to content
Open
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
641 changes: 563 additions & 78 deletions Cargo.lock

Large diffs are not rendered by default.

67 changes: 39 additions & 28 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ use nakamoto_chain::filter::cache::FilterCache;
use nakamoto_chain::filter::cache::StoredHeader;
use nakamoto_chain::{block::cache::BlockCache, filter::BlockFilter};

use nakamoto_common::bitcoin::network::constants::ServiceFlags;
use nakamoto_common::bitcoin::network::message::NetworkMessage;
use nakamoto_common::bitcoin::network::Address;
use nakamoto_common::bitcoin::util::uint::Uint256;
use nakamoto_common::bitcoin::p2p::message::NetworkMessage;
use nakamoto_common::bitcoin::p2p::Address;
use nakamoto_common::bitcoin::p2p::ServiceFlags;
use nakamoto_common::bitcoin::Txid;
use nakamoto_common::bitcoin_num::uint::Uint256;
use nakamoto_common::block::store::{Genesis as _, Store as _};
use nakamoto_common::block::time::{AdjustedTime, RefClock};
use nakamoto_common::block::tree::{self, BlockReader, ImportResult};
use nakamoto_common::block::{BlockHash, BlockHeader, Height, Transaction};
use nakamoto_common::block::{BlockHash, Header, Height, Transaction};
use nakamoto_common::nonempty::NonEmpty;
use nakamoto_common::p2p::peer::{Source, Store as _};
use nakamoto_p2p::fsm;
Expand Down Expand Up @@ -70,6 +70,8 @@ pub struct Config {
pub services: ServiceFlags,
/// Configured limits.
pub limits: Limits,
/// P2P_v2
pub p2p_v2: bool,
}

/// Configuration for loading event handling.
Expand Down Expand Up @@ -126,6 +128,7 @@ impl Default for Config {
hooks: Hooks::default(),
limits: Limits::default(),
services: ServiceFlags::NONE,
p2p_v2: false,
}
}
}
Expand Down Expand Up @@ -165,7 +168,7 @@ where
/// Runs a pre-loaded client.
pub struct ClientRunner<R> {
service: Service<
BlockCache<store::File<BlockHeader>>,
BlockCache<store::File<Header>>,
FilterCache<store::File<StoredHeader>>,
peer::Cache,
RefClock<AdjustedTime<net::SocketAddr>>,
Expand Down Expand Up @@ -249,7 +252,7 @@ impl<R: Reactor> Client<R> {
/// Load the client configuration. Takes a loading handler that can optionally receive
/// loading events.
pub fn load(
self,
mut self,
config: Config,
loading: impl Into<LoadingHandler>,
) -> Result<ClientRunner<R>, Error> {
Expand Down Expand Up @@ -379,6 +382,9 @@ impl<R: Reactor> Client<R> {
log::info!(target: "client", "{} seeds added to address book", peers.len());
}

self.reactor
.configure_network(config.network.to_str(), config.p2p_v2);

Ok(ClientRunner {
listen,
commands: self.commands,
Expand Down Expand Up @@ -413,6 +419,11 @@ impl<R: Reactor> Client<R> {
pub fn handle(&self) -> Handle<R::Waker> {
self.handle.clone()
}

/// Configures network and p2p version used by the client
pub fn configure_network(&mut self, network: String, is_v2: bool) {
self.reactor.configure_network(network, is_v2);
}
}

/// An instance of [`handle::Handle`] for [`Client`].
Expand Down Expand Up @@ -473,21 +484,21 @@ impl<W: Waker> Handle<W> {
}

impl<W: Waker> handle::Handle for Handle<W> {
fn get_tip(&self) -> Result<(Height, BlockHeader, Uint256), handle::Error> {
let (transmit, receive) = chan::bounded::<(Height, BlockHeader, Uint256)>(1);
fn get_tip(&self) -> Result<(Height, Header, Uint256), handle::Error> {
let (transmit, receive) = chan::bounded::<(Height, Header, Uint256)>(1);
self._command(Command::GetTip(transmit))?;

Ok(receive.recv()?)
}

fn get_block(&self, hash: &BlockHash) -> Result<Option<(Height, BlockHeader)>, handle::Error> {
fn get_block(&self, hash: &BlockHash) -> Result<Option<(Height, Header)>, handle::Error> {
let (transmit, receive) = chan::bounded(1);
self._command(Command::GetBlockByHash(*hash, transmit))?;

Ok(receive.recv()?)
}

fn get_block_by_height(&self, height: Height) -> Result<Option<BlockHeader>, handle::Error> {
fn get_block_by_height(&self, height: Height) -> Result<Option<Header>, handle::Error> {
let (sender, recvr) = chan::bounded(1);
self._command(Command::GetBlockByHeight(height, sender))?;

Expand All @@ -508,7 +519,7 @@ impl<W: Waker> handle::Handle for Handle<W> {
fn find_branch(
&self,
to: &BlockHash,
) -> Result<Option<(Height, NonEmpty<BlockHeader>)>, handle::Error> {
) -> Result<Option<(Height, NonEmpty<Header>)>, handle::Error> {
let to = *to;
let (transmit, receive) = chan::bounded(1);

Expand Down Expand Up @@ -602,22 +613,6 @@ impl<W: Waker> handle::Handle for Handle<W> {
Ok(())
}

fn import_headers(
&self,
headers: Vec<BlockHeader>,
) -> Result<Result<ImportResult, tree::Error>, handle::Error> {
let (transmit, receive) = chan::bounded::<Result<ImportResult, tree::Error>>(1);
self.command(Command::ImportHeaders(headers, transmit))?;

Ok(receive.recv()?)
}

fn import_addresses(&self, addrs: Vec<Address>) -> Result<(), handle::Error> {
self.command(Command::ImportAddresses(addrs))?;

Ok(())
}

fn submit_transaction(
&self,
tx: Transaction,
Expand All @@ -634,6 +629,22 @@ impl<W: Waker> handle::Handle for Handle<W> {
Ok(receive.recv()?)
}

fn import_headers(
&self,
headers: Vec<Header>,
) -> Result<Result<ImportResult, tree::Error>, handle::Error> {
let (transmit, receive) = chan::bounded::<Result<ImportResult, tree::Error>>(1);
self.command(Command::ImportHeaders(headers, transmit))?;

Ok(receive.recv()?)
}

fn import_addresses(&self, addrs: Vec<Address>) -> Result<(), handle::Error> {
self.command(Command::ImportAddresses(addrs))?;

Ok(())
}

fn wait<F, T>(&self, f: F) -> Result<T, handle::Error>
where
F: FnMut(fsm::Event) -> Option<T>,
Expand Down
84 changes: 82 additions & 2 deletions client/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ fn network(
let genesis = cfg.network.genesis();
let params = cfg.network.params();

let node = Client::<Reactor>::new()?;
let mut node = Client::<Reactor>::new()?;
let mut handle = node.handle();
handle.set_timeout(time::Duration::from_secs(5));
handle.set_timeout(time::Duration::from_secs(7));
node.configure_network(cfg.network.to_str(), cfg.p2p_v2);

let t = thread::spawn({
let params = params.clone();
Expand Down Expand Up @@ -114,6 +115,85 @@ fn test_full_sync() {
}
}

#[test]
fn test_full_sync_v2() {
logger::init(log::Level::Debug);

let cfgs = vec![
Config {
services: ServiceFlags::NETWORK | ServiceFlags::P2P_V2,
p2p_v2: true,
..Config::default()
};
3
];
let nodes = network(&cfgs).unwrap();
let (handle, _, _) = nodes.last().unwrap();
let headers = BITCOIN_HEADERS.tail.clone();
let height = headers.len() as Height;
let hash = headers.last().unwrap().block_hash();

// Ensure all peers are connected to misha,
// so that misha can effectively send blocks to
// all peers on time.
handle.wait_for_peers(2, Services::Chain).unwrap();

handle
.import_headers(headers)
.expect("command is successful")
.expect("chain is valid");

for (mut node, _, thread) in nodes.into_iter() {
node.set_timeout(std::time::Duration::from_secs(5));
assert_eq!(node.wait_for_height(height).unwrap(), hash);

node.shutdown().unwrap();
thread.join().unwrap();
}
}

#[test]
#[ignore = "failing"]
fn test_full_sync_v1_v2() {
logger::init(log::Level::Debug); // true true false falla / true false false / false true false /

let cfgs = vec![
Config {
services: ServiceFlags::NETWORK | ServiceFlags::P2P_V2,
p2p_v2: true,
..Config::default()
},
Config {
services: ServiceFlags::NETWORK | ServiceFlags::BLOOM,
p2p_v2: false,
..Config::default()
},
];
let nodes = network(&cfgs).unwrap();
let (handle, _, _) = nodes.last().unwrap();
let headers = BITCOIN_HEADERS.tail.clone();
let height = headers.len() as Height;
let hash = headers.last().unwrap().block_hash();

// Ensure all peers are connected to misha,
// so that misha can effectively send blocks to
// all peers on time.
handle.wait_for_peers(1, Services::Chain).unwrap();

handle
.import_headers(headers)
.expect("command is successful")
.expect("chain is valid");

for (mut node, _, thread) in nodes.into_iter() {
node.set_timeout(std::time::Duration::from_secs(6));
assert_eq!(node.wait_for_height(height).unwrap(), hash);

node.shutdown().unwrap();
thread.join().unwrap();
}
}

#[test]
fn test_wait_for_peers() {
logger::init(log::Level::Debug);
Expand Down
10 changes: 10 additions & 0 deletions common/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ impl Network {
}
}

/// Converts the network to String
pub fn to_str(self) -> String {
match self {
Network::Mainnet => String::from("mainnet"),
Network::Testnet => String::from("testnet"),
Network::Regtest => String::from("regtest"),
Network::Signet => String::from("signet"),
}
}

/// Blockchain checkpoints.
pub fn checkpoints(&self) -> Box<dyn Iterator<Item = (Height, BlockHash)>> {
use crate::block::checkpoints;
Expand Down
3 changes: 3 additions & 0 deletions net/poll/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ license = "MIT"

[dependencies]
nakamoto-net = { version = "0.4.0", path = ".." }
nakamoto-common = { version = "0.4.0", path = "../../common" }
nakamoto-p2p = { version = "0.4.0", path = "../../p2p" }
crossbeam-channel = { version = "0.5.6" }
popol = { version = "2" }
socket2 = { version = "0.4" }
libc = { version = "0.2" }
log = { version = "0.4" }
bip324 = "0.6.0"

[dev-dependencies]
fastrand = "1.3.5"
Expand Down
33 changes: 33 additions & 0 deletions net/poll/src/bip324_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use bip324::{Handshake, PacketHandler};
use nakamoto_net::{LocalDuration, LocalTime};

/// Version content is always empty for the current version of the protocol.
pub const VERSION_CONTENT: [u8; 0] = [];
/// Number of bytes for the garbage terminator.
pub const GARBAGE_TERMINATOR_BYTES: usize = 16;
/// Number of bytes used to indicate size when decrypting a message
pub const DEFAULT_SIZE_BYTES_V2: usize = 3;
/// Size of an ElliSwift key
pub const ELLI_SWIFT_KEY_SIZE: usize = 64;
/// Maxi possible size of the buffer containing the Elliswift key
pub const MAX_GARBAGE_BUFFER_BYTES: usize = 36;
/// Duration after a P2P_V2 should fall back to V1
pub const BIP324_HANDSHAKE_TIMEOUT: LocalDuration = LocalDuration::from_secs(5);

#[derive(Default)]
pub struct Bip324Info {
pub key_sent: Option<Vec<u8>>,
pub key_received: Option<Vec<u8>>,
pub terminator_sent: Option<Vec<u8>>,
pub garbage_received: Vec<u8>,
pub packet_handler: Option<PacketHandler>,
pub handshake: Option<Box<Handshake<'static>>>,
pub message_buffer: MessageBuffer,
pub handshake_started: Option<LocalTime>,
}

#[derive(Default)]
pub struct MessageBuffer {
pub pending_bytes: usize,
pub buffer: Vec<u8>,
}
1 change: 1 addition & 0 deletions net/poll/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub mod time;

pub use reactor::{Reactor, Waker};

mod bip324_info;
#[cfg(test)]
mod fallible;

Expand Down
Loading
Loading