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, + )) +}