|
| 1 | +use dns_lookup::lookup_host; |
| 2 | +use std::{ |
| 3 | + fs::File, |
| 4 | + io::{self, Error, ErrorKind, Write}, |
| 5 | + net::{IpAddr, Ipv6Addr}, |
| 6 | + str::FromStr, |
| 7 | +}; |
| 8 | +use tokio::{ |
| 9 | + io::{AsyncReadExt, AsyncWriteExt}, |
| 10 | + net::TcpStream, |
| 11 | +}; |
| 12 | + |
| 13 | +use bitcoin::{ |
| 14 | + consensus::{ |
| 15 | + deserialize_partial, |
| 16 | + encode::{serialize_hex}, |
| 17 | + }, |
| 18 | + Block, |
| 19 | +}; |
| 20 | +use bitcoin::hashes::{sha256d, Hash}; |
| 21 | +use rand::seq::SliceRandom; |
| 22 | +use rand::thread_rng; |
| 23 | +use tokio::time::{timeout, Duration}; |
| 24 | + |
| 25 | +const PROTOCOL_VERSION: i32 = 70015; |
| 26 | + |
| 27 | +// Define the fields |
| 28 | +struct NetAddress { |
| 29 | +} |
| 30 | + |
| 31 | +// Define the fields |
| 32 | +struct VersionMessage { |
| 33 | +} |
| 34 | + |
| 35 | +const REGTEST_MAGIC: [u8; 4] = [0;4];// todo!(); |
| 36 | +const MAINNET_MAGIC: [u8; 4] = [0;4];// todo!(); |
| 37 | + |
| 38 | +fn serialize_net_address(addr: &NetAddress) -> Vec<u8> { |
| 39 | + todo!() |
| 40 | +} |
| 41 | + |
| 42 | +fn serialize_version_msg(msg: &VersionMessage) -> Vec<u8> { |
| 43 | + todo!() |
| 44 | +} |
| 45 | + |
| 46 | +fn hex_to_bytes(hex_string: &str) -> Result<Vec<u8>, std::num::ParseIntError> { |
| 47 | + // refer [hex to bytes conversion](https://github.com/bitcoin-dev-project/rust-for-bitcoiners/blob/main/tutorials/de_se_rialization/hex_bytes_conversions.md) |
| 48 | + todo!() |
| 49 | +} |
| 50 | + |
| 51 | +fn request_block_message(hash: &str) -> Vec<u8> { |
| 52 | + todo!("Given a block hash return the block message inventory for getdata command") |
| 53 | +} |
| 54 | + |
| 55 | +fn create_message(command: &str, payload: &[u8]) -> Vec<u8> { |
| 56 | + todo!("Given a command and payload prepare the message according to Message Structure section") |
| 57 | +} |
| 58 | + |
| 59 | +fn is_verack(data: &[u8]) -> bool { |
| 60 | + todo!("Check whether these bytes starts with a verack message") |
| 61 | +} |
| 62 | + |
| 63 | +fn randomize_slice<'a>(input: &'a [&'a str]) -> Vec<&'a str> { |
| 64 | + let mut rng = thread_rng(); |
| 65 | + let mut vec = input.to_vec(); // Convert slice to vector |
| 66 | + vec.shuffle(&mut rng); // Shuffle the vector |
| 67 | + vec // Return the vector (or convert to a slice if needed) |
| 68 | +} |
| 69 | + |
| 70 | +async fn get_valid_ip() -> Result<(TcpStream, IpAddr), String> { |
| 71 | + const DNS_SEEDS: [&str; 4] = [ |
| 72 | + "seed.bitcoin.sipa.be", |
| 73 | + "dnsseed.bluematt.me", |
| 74 | + "dnsseed.bitcoin.dashjr.org", |
| 75 | + "seed.bitcoinstats.com", |
| 76 | + ]; |
| 77 | + todo!("Initially test with regtest with debug=net option"); |
| 78 | + todo!("then test with your local full node"); |
| 79 | + todo!("Then choose an ip from randomly iterating over DNS_SEEDS") |
| 80 | +} |
| 81 | + |
| 82 | +// bitcoin messages did not end with any special character |
| 83 | +// So this function will keep reading from the stream until the read results in an error or 0 |
| 84 | +async fn till_read_succeeds(stream: &mut TcpStream, buffer: &mut Vec<u8>) { |
| 85 | + loop { |
| 86 | + let mut t = [0; 1024]; |
| 87 | + if let Ok(n) = stream.read(&mut t).await { |
| 88 | + if n == 0 { |
| 89 | + return; |
| 90 | + } |
| 91 | + buffer.extend(t); |
| 92 | + tracing::info!("read {n} bytes"); |
| 93 | + } else { |
| 94 | + tracing::error!("Error in read"); |
| 95 | + return; |
| 96 | + } |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +#[tokio::main] |
| 101 | +async fn main() -> io::Result<()> { |
| 102 | + let subscriber = tracing_subscriber::fmt() |
| 103 | + // Use a more compact, abbreviated log format |
| 104 | + .compact() |
| 105 | + // Display source code file paths |
| 106 | + .with_file(true) |
| 107 | + // Display source code line numbers |
| 108 | + .with_line_number(true) |
| 109 | + // Display the thread ID an event was recorded on |
| 110 | + .with_thread_ids(false) |
| 111 | + // Don't display the event's target (module path) |
| 112 | + .with_target(false) |
| 113 | + // Build the subscriber |
| 114 | + .finish(); |
| 115 | + tracing::subscriber::set_global_default(subscriber).unwrap(); |
| 116 | + let (mut stream, ip) = get_valid_ip().await.unwrap(); |
| 117 | + |
| 118 | + let ip6 = match ip { |
| 119 | + IpAddr::V4(addr) => addr.to_ipv6_mapped(), |
| 120 | + IpAddr::V6(addr) => addr, |
| 121 | + }; |
| 122 | + |
| 123 | + // Construct and send the version message |
| 124 | + let version_msg = todo!("prepare version message"); |
| 125 | + |
| 126 | + |
| 127 | + Ok(()) |
| 128 | +} |
| 129 | + |
| 130 | +// A bitcoin peer will be continuously sending you messages |
| 131 | +// At some point you need to pause reading them and process the messages |
| 132 | +// So this function reads till a specified timeout |
| 133 | +async fn get_data_with_timeout(mut stream: &mut TcpStream, mut buffer: &mut Vec<u8>) { |
| 134 | + let timeout_duration = Duration::from_secs(10); |
| 135 | + let _ = timeout( |
| 136 | + timeout_duration, |
| 137 | + till_read_succeeds(&mut stream, &mut buffer), |
| 138 | + ) |
| 139 | + .await; |
| 140 | +} |
| 141 | + |
| 142 | +fn get_block_payload(buffer: &[u8]) -> &[u8] { |
| 143 | + todo!("The bitcoin node will keep sending you messages like ping, inv etc.,"); |
| 144 | + todo!("One of them will be your required block message"); |
| 145 | + todo!("How will you identify that?") |
| 146 | +} |
| 147 | + |
| 148 | +fn starts_with_magic(buffer: &[u8]) -> bool { |
| 149 | + todo!("check whether the buffer strts with magic network characters") |
| 150 | +} |
0 commit comments