diff --git a/Cargo.lock b/Cargo.lock index a75db25..56ac54a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,9 +12,19 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" name = "bitcoin-handshake" version = "0.1.0" dependencies = [ + "sha2", "tokio", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bytes" version = "1.4.0" @@ -27,6 +37,45 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cpufeatures" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "hermit-abi" version = "0.2.6" @@ -103,6 +152,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "socket2" version = "0.4.9" @@ -153,12 +213,24 @@ dependencies = [ "syn", ] +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 0d3bc26..d6f907d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +sha2 = "0.10.6" tokio = { version = "1", features = ["net", "io-util", "macros", "rt-multi-thread"] } diff --git a/src/main.rs b/src/main.rs index 3e77965..01e1394 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,156 +1,129 @@ -use std::net::SocketAddr; +use sha2::{Digest, Sha256}; +use std::net::{IpAddr, SocketAddr}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; -static VERSION_MESSAGE: &[u8] = &[ - // header - 249, 190, 180, 217, // magic - 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 0, // "version" command - 86, 0, 0, 0, // payload len - 94, 38, 138, 233, // checksum - // payload - 113, 17, 1, 0, // protocol version - 0, 0, 0, 0, 0, 0, 0, 0, // services - 0, 0, 0, 0, 0, 0, 0, 0, // time - // 36, 99, 31, 100, 0, 0, 0, 0, // time - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 134, 195, 185, 52, 32, 141, // recipient address info - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, // sender address info - 0, 0, 0, 0, 0, 0, 0, 0, // Node ID, - // 131, 67, 161, 229, 100, 242, 249, 181, // Node ID, - 0, // "" sub-version string, 0 bytes long - 0, // relay - 0, 0, 0, 0, // last block sending node -]; - static VERACK_MESSAGE: &[u8] = &[ // header 249, 190, 180, 217, // magic - 18, 101, 114, 97, 99, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // payload len + 18, 101, 114, 97, 99, 107, 0, 0, 0, 0, 0, 0, // "verack" command + 0, 0, 0, 0, // payload len 93, 246, 224, 226, // checksum ]; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { - let address: SocketAddr = "134.195.185.52:8333".parse().unwrap(); + let address: SocketAddr = "134.195.185.52:8333".parse()?; let mut stream = TcpStream::connect(address).await?; - let _ = stream.write_all(VERSION_MESSAGE).await; + stream.write_all(build_version_message(address).as_slice()).await?; + println!("Version message sent"); + println!("Waiting for response..."); read_version_message(&mut stream).await?; + println!("Version message received"); - let _ = stream.write_all(VERACK_MESSAGE).await; + stream.write_all(VERACK_MESSAGE).await?; + println!("Verack message sent"); + println!("Waiting for response..."); read_verack_message(&mut stream).await?; + println!("Verack message received"); + Ok(()) } -async fn read_version_message(stream: &mut TcpStream) -> Result<(), Box<dyn std::error::Error>> { - let mut magic = vec![0; 4]; - stream.read_exact(&mut magic).await?; - - println!("{:?}", magic); +fn build_version_message(address: SocketAddr) -> Vec<u8> { + let mut payload: Vec<u8> = vec![ + 113, 17, 1, 0, // protocol version + 0, 0, 0, 0, 0, 0, 0, 0, // services + 0, 0, 0, 0, 0, 0, 0, 0, // time + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, // start of recipient address bytes + ]; + + let mut addr = socket_addr_to_vec(address); + payload.append(&mut addr); + payload.append(&mut vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, // sender address info + 0, 0, 0, 0, 0, 0, 0, 0, // Node ID, + 0, // "" sub-version string, 0 bytes long + 0, // relay + 0, 0, 0, 0, // last block sending node + ]); + + let mut header: Vec<u8> = vec![ + // header + 249, 190, 180, 217, // magic + 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 0, // "version" command + ]; + + // Add payload len + header.extend_from_slice(&mut (payload.len() as u32).to_le_bytes()); + + // Add checksum + let mut checksum = get_first_4_bytes_of_double_sha256(&payload); + header.extend_from_slice(&mut checksum); + header.extend(&payload); + header +} - let mut cmd = vec![0; 12]; - stream.read_exact(&mut cmd).await?; +fn socket_addr_to_vec(socket_addr: SocketAddr) -> Vec<u8> { + let ip = match socket_addr.ip() { + IpAddr::V4(ip) => ip.octets().to_vec(), + IpAddr::V6(ip) => ip.octets().to_vec(), + }; - println!("{:?}", cmd); + let port = socket_addr.port(); + let mut port_bytes = [0u8; 2]; + port_bytes.copy_from_slice(&(port as u16).to_be_bytes()); - let mut len = vec![0; 4]; - stream.read_exact(&mut len).await?; + [ip, port_bytes.to_vec()].concat() +} - println!("{:?}", len); +async fn read_version_message(stream: &mut TcpStream) -> Result<(), Box<dyn std::error::Error>> { + let mut header = vec![0; 24]; + stream.read_exact(&mut header).await?; - let mut checksum = vec![0; 4]; - stream.read_exact(&mut checksum).await?; + if "version" != std::str::from_utf8(&header[4..11])? { + return Err("Unexpected command".into()); + } - println!("{:?}", checksum); + let len = u32::from_le_bytes([header[16], header[17], header[18], header[19]]); - let mut payload = vec![0; 102]; + let mut payload = vec![0; len as usize]; stream.read_exact(&mut payload).await?; - println!("{:?}", payload); + let checksum = get_first_4_bytes_of_double_sha256(&payload); - println!("Finished reading version"); - Ok(()) + if checksum == &header[20..24] { + Ok(()) + } else { + Err("Invalid version message checksum".into()) + } } async fn read_verack_message(stream: &mut TcpStream) -> Result<(), Box<dyn std::error::Error>> { let mut verack = vec![0; 24]; - stream.read_exact(&mut verack).await.unwrap(); + stream.read_exact(&mut verack).await?; + + if "verack" != std::str::from_utf8(&verack[4..10])? { + return Err("Unexpected command".into()); + } - println!("{:?}", verack); + if [93, 246, 224, 226] != &verack[20..24] { + return Err("Invalid verack message checksum".into()); + } - println!("Finished reading verack"); Ok(()) } -// fn build_version_message_vec(address: SocketAddr) -> Vec<u8> { -// vec![ -// // header -// 249, 190, 180, 217, // magic -// 118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 0, // "version" command -// 86, 0, 0, 0, // payload len -// 94, 38, 138, 233, // checksum - -// // payload -// 113, 17, 1, 0, // protocol version -// 0, 0, 0, 0, 0, 0, 0, 0, // services -// 0, 0, 0, 0, 0, 0, 0, 0, // time -// // 36, 99, 31, 100, 0, 0, 0, 0, // time -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 134, 195, 185, 52, 32, 141, // recipient address info -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, // sender address info -// 0, 0, 0, 0, 0, 0, 0, 0, // Node ID, -// // 131, 67, 161, 229, 100, 242, 249, 181, // Node ID, -// 0, // "" sub-version string, 0 bytes long -// 0, // relay -// 0, 0, 0, 0, // last block sending node -// ] -// } - -// fn build_verack_message_vec() -> Vec<u8> { -// vec![ -// // header -// 249, 190, 180, 217, // magic -// 18, 101, 114, 97, 99, 107, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, // payload len -// 93, 246, 224, 226, // checksum -// ] -// } - -// 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(); -// 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(); -// 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, -// )) -// } +fn get_first_4_bytes_of_double_sha256(data: &[u8]) -> [u8; 4] { + let mut hasher = Sha256::new(); + hasher.update(data); + let hash = hasher.finalize(); + + let mut hasher2 = Sha256::new(); + hasher2.update(hash); + let hash2 = hasher2.finalize(); + + [hash2[0], hash2[1], hash2[2], hash2[3]] +}