From 9d212da0baa69114da870ffc6349d3eb9a88552e Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Mon, 17 Feb 2020 15:12:13 +0100 Subject: [PATCH 1/8] Added a simple handshake example. --- examples/handshake.rs | 102 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 examples/handshake.rs diff --git a/examples/handshake.rs b/examples/handshake.rs new file mode 100644 index 00000000..be40f7d9 --- /dev/null +++ b/examples/handshake.rs @@ -0,0 +1,102 @@ +use rand::Rng; +use std::io::Write; +use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use bitcoin::consensus::encode; +use bitcoin::network::address; +use bitcoin::network::constants; +use bitcoin::network::message; +use bitcoin::network::message_network; +use bitcoin::network::stream_reader::StreamReader; + +fn main() { + // This example establishes a connection to a Bitcoin node, sends the intial + // "version" message, waits for the reply, and finally closes the connection. + + let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(130, 149, 80, 221)), 8333); + + let version_message = build_version_message(address.clone()); + + let first_message = message::RawNetworkMessage { + magic: constants::Network::Bitcoin.magic(), + payload: version_message, + }; + + if let Ok(mut stream) = TcpStream::connect(address) { + // Send the message + let _ = stream.write(encode::serialize(&first_message).as_slice()); + println!("Sent version message"); + + // Wait for reply and close stream + + loop { + let reply = StreamReader::new(&mut stream, None).read_next(); + let unwrapped: message::RawNetworkMessage = reply.unwrap(); + match unwrapped.payload { + message::NetworkMessage::Version(_) => { + println!("Received version message: {:?}", unwrapped.payload); + + let second_message = message::RawNetworkMessage { + magic: constants::Network::Bitcoin.magic(), + payload: message::NetworkMessage::Verack, + }; + + let _ = stream.write(encode::serialize(&second_message).as_slice()); + println!("Sent verack message"); + } + message::NetworkMessage::Verack => { + println!("Received verack message: {:?}", unwrapped.payload); + break; + } + _ => { + println!("Received unknown message: {:?}", unwrapped.payload); + break; + } + } + } + let _ = stream.shutdown(Shutdown::Both); + } else { + eprintln!("Failed to open connection"); + } +} + +fn build_version_message(address: SocketAddr) -> message::NetworkMessage { + // Building version message, see https://en.bitcoin.it/wiki/Protocol_documentation#version + let my_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); + + // "bitfield of features to be enabled for this connection" + let services = constants::ServiceFlags::NONE; + + // "standard UNIX timestamp in seconds" + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time error") + .as_secs(); + + // "The network address of the node receiving this message" + let addr_recv = address::Address::new(&address, constants::ServiceFlags::NONE); + + // "The network address of the node emitting this message" + let addr_from = address::Address::new(&my_address, constants::ServiceFlags::NONE); + + // "Node random nonce, randomly generated every time a version packet is sent. This nonce is used to detect connections to self." + let nonce: u64 = rand::thread_rng().gen(); + + // "User Agent (0x00 if string is 0 bytes long)" + let user_agent = String::from("rust-example"); + + // "The last block received by the emitting node" + let start_height: i32 = 0; + + // Construct the message + message::NetworkMessage::Version(message_network::VersionMessage::new( + services, + timestamp as i64, + addr_recv, + addr_from, + nonce, + user_agent, + start_height, + )) +} From c4f0056856976a1f640b2b0ca49e4155f5acd157 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Mon, 17 Feb 2020 16:07:10 +0100 Subject: [PATCH 2/8] No clone needed. --- examples/handshake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/handshake.rs b/examples/handshake.rs index be40f7d9..fa67ca25 100644 --- a/examples/handshake.rs +++ b/examples/handshake.rs @@ -16,7 +16,7 @@ fn main() { let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(130, 149, 80, 221)), 8333); - let version_message = build_version_message(address.clone()); + let version_message = build_version_message(address); let first_message = message::RawNetworkMessage { magic: constants::Network::Bitcoin.magic(), From 2852083ff301842b77cf7aca5ade8ffc21b90827 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Mon, 17 Feb 2020 16:17:13 +0100 Subject: [PATCH 3/8] Using write_all(). --- examples/handshake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/handshake.rs b/examples/handshake.rs index fa67ca25..a8a1678a 100644 --- a/examples/handshake.rs +++ b/examples/handshake.rs @@ -25,7 +25,7 @@ fn main() { if let Ok(mut stream) = TcpStream::connect(address) { // Send the message - let _ = stream.write(encode::serialize(&first_message).as_slice()); + let _ = stream.write_all(encode::serialize(&first_message).as_slice()); println!("Sent version message"); // Wait for reply and close stream @@ -42,7 +42,7 @@ fn main() { payload: message::NetworkMessage::Verack, }; - let _ = stream.write(encode::serialize(&second_message).as_slice()); + let _ = stream.write_all(encode::serialize(&second_message).as_slice()); println!("Sent verack message"); } message::NetworkMessage::Verack => { From 62f5f8e3f9098b44e0ba9ec1eccdd007bc67b089 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Wed, 19 Feb 2020 08:46:26 +0100 Subject: [PATCH 4/8] Removed explicit dependency on rand. --- examples/handshake.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/handshake.rs b/examples/handshake.rs index a8a1678a..2536800d 100644 --- a/examples/handshake.rs +++ b/examples/handshake.rs @@ -1,4 +1,6 @@ -use rand::Rng; +extern crate bitcoin; + +use bitcoin::secp256k1::rand::Rng; use std::io::Write; use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -81,7 +83,7 @@ fn build_version_message(address: SocketAddr) -> message::NetworkMessage { let addr_from = address::Address::new(&my_address, constants::ServiceFlags::NONE); // "Node random nonce, randomly generated every time a version packet is sent. This nonce is used to detect connections to self." - let nonce: u64 = rand::thread_rng().gen(); + let nonce: u64 = secp256k1::rand::thread_rng().gen(); // "User Agent (0x00 if string is 0 bytes long)" let user_agent = String::from("rust-example"); From b5f5abe0b433328fbff118b0d42b195bedef160e Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Wed, 19 Feb 2020 08:52:39 +0100 Subject: [PATCH 5/8] Removed pre-configured IP, now takes IP as argument. --- examples/handshake.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/handshake.rs b/examples/handshake.rs index 2536800d..1d97717e 100644 --- a/examples/handshake.rs +++ b/examples/handshake.rs @@ -1,22 +1,28 @@ extern crate bitcoin; -use bitcoin::secp256k1::rand::Rng; -use std::io::Write; use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream}; use std::time::{SystemTime, UNIX_EPOCH}; +use std::{env, process, io::Write}; +use bitcoin::secp256k1::rand::Rng; use bitcoin::consensus::encode; -use bitcoin::network::address; -use bitcoin::network::constants; -use bitcoin::network::message; -use bitcoin::network::message_network; -use bitcoin::network::stream_reader::StreamReader; +use bitcoin::network::{address, constants, message, message_network, stream_reader::StreamReader}; fn main() { // This example establishes a connection to a Bitcoin node, sends the intial // "version" message, waits for the reply, and finally closes the connection. + let args: Vec = env::args().collect(); + if args.len() < 2 { + eprintln!("not enough arguments"); + process::exit(1); + } - let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(130, 149, 80, 221)), 8333); + let str_address = &args[1]; + + let address: SocketAddr = str_address.parse().unwrap_or_else(|error| { + eprintln!("Error parsing address: {:?}", error); + process::exit(1); + }); let version_message = build_version_message(address); From 40431f3d4f2f60a08d7593ee338e0689b5711072 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Wed, 19 Feb 2020 09:11:26 +0100 Subject: [PATCH 6/8] Formatted. --- examples/handshake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/handshake.rs b/examples/handshake.rs index 1d97717e..933c758b 100644 --- a/examples/handshake.rs +++ b/examples/handshake.rs @@ -2,11 +2,11 @@ extern crate bitcoin; use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream}; use std::time::{SystemTime, UNIX_EPOCH}; -use std::{env, process, io::Write}; +use std::{env, io::Write, process}; -use bitcoin::secp256k1::rand::Rng; use bitcoin::consensus::encode; use bitcoin::network::{address, constants, message, message_network, stream_reader::StreamReader}; +use bitcoin::secp256k1::rand::Rng; fn main() { // This example establishes a connection to a Bitcoin node, sends the intial From aa46618da7afddf7d5b23f56f81081f9c15a8616 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 28 Feb 2020 16:28:25 +0100 Subject: [PATCH 7/8] Intialize stream reader only once. --- examples/handshake.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/handshake.rs b/examples/handshake.rs index 933c758b..e04470a8 100644 --- a/examples/handshake.rs +++ b/examples/handshake.rs @@ -36,14 +36,15 @@ fn main() { let _ = stream.write_all(encode::serialize(&first_message).as_slice()); println!("Sent version message"); - // Wait for reply and close stream - + // Setup StreamReader + let read_stream = stream.try_clone().unwrap(); + let mut stream_reader = StreamReader::new(read_stream, None); loop { - let reply = StreamReader::new(&mut stream, None).read_next(); - let unwrapped: message::RawNetworkMessage = reply.unwrap(); - match unwrapped.payload { + // Loop an retrieve new messages + let reply: message::RawNetworkMessage = stream_reader.read_next().unwrap(); + match reply.payload { message::NetworkMessage::Version(_) => { - println!("Received version message: {:?}", unwrapped.payload); + println!("Received version message: {:?}", reply.payload); let second_message = message::RawNetworkMessage { magic: constants::Network::Bitcoin.magic(), @@ -54,11 +55,11 @@ fn main() { println!("Sent verack message"); } message::NetworkMessage::Verack => { - println!("Received verack message: {:?}", unwrapped.payload); + println!("Received verack message: {:?}", reply.payload); break; } _ => { - println!("Received unknown message: {:?}", unwrapped.payload); + println!("Received unknown message: {:?}", reply.payload); break; } } From 827d98d821bae184bf236053fd10b8da0f11d379 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 3 Mar 2020 12:18:37 +0100 Subject: [PATCH 8/8] Fixed for rust 1.22.0. --- examples/handshake.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/handshake.rs b/examples/handshake.rs index e04470a8..036d4223 100644 --- a/examples/handshake.rs +++ b/examples/handshake.rs @@ -2,10 +2,13 @@ extern crate bitcoin; use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream}; use std::time::{SystemTime, UNIX_EPOCH}; -use std::{env, io::Write, process}; +use std::{env, process}; +use std::io::Write; use bitcoin::consensus::encode; -use bitcoin::network::{address, constants, message, message_network, stream_reader::StreamReader}; +use bitcoin::network::{address, constants, message, message_network}; +use bitcoin::network::stream_reader::StreamReader; +use bitcoin::secp256k1; use bitcoin::secp256k1::rand::Rng; fn main() {