extern crate sapio_bitcoin as bitcoin; use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream}; use std::time::{SystemTime, UNIX_EPOCH}; use std::{env, process}; use std::io::{Write, BufReader}; use bitcoin::consensus::{encode, Decodable}; use bitcoin::network::{address, constants, message, message_network}; use bitcoin::secp256k1; use bitcoin::secp256k1::rand::Rng; 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 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); 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_all(encode::serialize(&first_message).as_slice()); println!("Sent version message"); // Setup StreamReader let read_stream = stream.try_clone().unwrap(); let mut stream_reader = BufReader::new(read_stream); loop { // Loop an retrieve new messages let reply = message::RawNetworkMessage::consensus_decode(&mut stream_reader).unwrap(); match reply.payload { message::NetworkMessage::Version(_) => { println!("Received version message: {:?}", reply.payload); let second_message = message::RawNetworkMessage { magic: constants::Network::Bitcoin.magic(), payload: message::NetworkMessage::Verack, }; let _ = stream.write_all(encode::serialize(&second_message).as_slice()); println!("Sent verack message"); } message::NetworkMessage::Verack => { println!("Received verack message: {:?}", reply.payload); break; } _ => { println!("Received unknown message: {:?}", reply.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 = secp256k1::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, )) }