From 96949cf201aab2003153176d1ef22ff7cc969ba0 Mon Sep 17 00:00:00 2001 From: Rob N Date: Fri, 9 Aug 2024 04:30:28 -1000 Subject: [PATCH] test(protocol): add handshake with Bitcoin Core --- protocol/Cargo.toml | 1 + protocol/tests/regtest.sh | 3 ++ protocol/tests/round_trips.rs | 99 +++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100755 protocol/tests/regtest.sh diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index d418a53..7f84d4f 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -19,6 +19,7 @@ bitcoin = { version = "0.32.0", default-features = false } [dev-dependencies] hex = { package = "hex-conservative", version = "0.2.0" } +bitcoincore-rpc = "0.19.0" [lib] name = "bip324" diff --git a/protocol/tests/regtest.sh b/protocol/tests/regtest.sh new file mode 100755 index 0000000..106222c --- /dev/null +++ b/protocol/tests/regtest.sh @@ -0,0 +1,3 @@ +bitcoind --chain=regtest --txindex --blockfilterindex --peerblockfilters --rpcport=18443 --rpcuser=test --rpcpassword=b324 --rest=1 --server=1 --listen=1 --v2transport=1 & +sleep 1 +cargo test regtest_handshake -- --nocapture \ No newline at end of file diff --git a/protocol/tests/round_trips.rs b/protocol/tests/round_trips.rs index 89172b9..c34e250 100644 --- a/protocol/tests/round_trips.rs +++ b/protocol/tests/round_trips.rs @@ -1,5 +1,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +const RPC_USER: &str = "test"; +const RPC_PASSWORD: &str = "b324"; +const HOST: &str = "http://localhost:18443"; +const PORT: u16 = 18444; + #[test] #[cfg(feature = "std")] fn hello_world_happy_path() { @@ -53,3 +58,97 @@ fn hello_world_happy_path() { .unwrap(); assert_eq!(message, messages.message.unwrap()); } + +#[test] +#[cfg(feature = "std")] +#[ignore = "CI"] +fn regtest_handshake() { + use std::{ + io::{Read, Write}, + net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}, + time::{Duration, SystemTime, UNIX_EPOCH}, + }; + + use bip324::{ + serde::{deserialize, serialize, NetworkMessage}, + Handshake, ReceivedMessage, + }; + use bitcoincore_rpc::{ + bitcoin::p2p::{message_network::VersionMessage, Address, ServiceFlags}, + RpcApi, + }; + + let rpc = bitcoincore_rpc::Client::new( + HOST, + bitcoincore_rpc::Auth::UserPass(RPC_USER.into(), RPC_PASSWORD.into()), + ).unwrap(); + let mut stream = TcpStream::connect(format!("127.0.0.1:{PORT}")).unwrap(); + let mut public_key = [0u8; 64]; + let mut handshake = Handshake::new( + bip324::Network::Regtest, + bip324::Role::Initiator, + None, + &mut public_key, + ) + .unwrap(); + dbg!("Writing public key to the remote node"); + stream.write_all(&public_key).unwrap(); + stream.flush().unwrap(); + let mut remote_public_key = [0u8; 64]; + dbg!("Reading the remote node public key"); + stream.read_exact(&mut remote_public_key).unwrap(); + let mut local_garbage_terminator_message = [0u8; 36]; + dbg!("Sending our garbage terminator"); + handshake + .complete_materials(remote_public_key, &mut local_garbage_terminator_message) + .unwrap(); + stream.write_all(&local_garbage_terminator_message).unwrap(); + stream.flush().unwrap(); + let mut max_response = [0; 4096]; + dbg!("Reading the response buffer"); + let size = stream.read(&mut max_response).unwrap(); + let response = &mut max_response[..size]; + dbg!("Authenticating the handshake"); + handshake + .authenticate_garbage_and_version(response) + .unwrap(); + dbg!("Finalizing the handshake"); + let packet_handler = handshake.finalize().unwrap(); + let (mut decrypter, mut encrypter) = packet_handler.into_split(); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("time went backwards") + .as_secs(); + let ip = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), PORT); + let from_and_recv = Address::new(&ip, ServiceFlags::NONE); + let msg = VersionMessage { + version: 70015, + services: ServiceFlags::NONE, + timestamp: now as i64, + receiver: from_and_recv.clone(), + sender: from_and_recv, + nonce: 1, + user_agent: "BIP324 Client".to_string(), + start_height: 0, + relay: false, + }; + let message = serialize(NetworkMessage::Version(msg)).unwrap(); + let packet = encrypter + .prepare_packet_with_alloc(&message, None, false) + .unwrap(); + dbg!("Serializing and writing version message"); + stream.write_all(&packet).unwrap(); + dbg!("Reading the response length buffer"); + let mut response_len = [0; 3]; + stream.read_exact(&mut response_len).unwrap(); + let message_len = decrypter.decypt_len(response_len); + let mut response_message = vec![0; message_len]; + stream.read_exact(&mut response_message).unwrap(); + let msg = decrypter.decrypt_contents_with_alloc(&response_message, None).unwrap(); + let message = ReceivedMessage::new(&msg.clone()).unwrap(); + let message = deserialize(&message.message.unwrap()).unwrap(); + dbg!("{}", message.cmd()); + assert_eq!(message.cmd(), "version"); + rpc.stop().unwrap(); + std::thread::sleep(Duration::from_secs(1)); +}