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]]
+}