extern crate devp2p; extern crate rand; extern crate secp256k1; extern crate bigint; extern crate rlp; extern crate block; extern crate hexutil; #[macro_use] extern crate log; #[macro_use] extern crate futures; extern crate tokio_io; extern crate tokio_core; extern crate env_logger; extern crate url; extern crate sha3; use tokio_core::reactor::{Core, Timeout}; use secp256k1::{PublicKey, SecretKey}; use rand::os::OsRng; use futures::future; use futures::{Stream, Sink, Future}; use std::str::FromStr; use std::time::{Instant, Duration}; use devp2p::{ETHSendMessage, ETHReceiveMessage, ETHMessage, ETHStream, DevP2PConfig}; use devp2p::rlpx::RLPxNode; use devp2p::dpt::DPTNode; use bigint::{H256, U256, H512}; use url::Url; use sha3::{Digest, Keccak256}; use block::{Header, Block}; use hexutil::*; const GENESIS_HASH: &str = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"; const GENESIS_DIFFICULTY: usize = 17179869184; const NETWORK_ID: usize = 1; const ETC_DAO_BLOCK: &str = "f903cff9020fa0a218e2c611f21232d857e3c8cecdcdf1f65f25a4477f98f6f47e4063807f2308a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479461c808d82a3ac53231750dadc13c777b59310bd9a0614d7d358b03cbdaf0343529673be20ad45809d02487f023e047efdce9da8affa0d33068a7f21bff5018a00ca08a3566a06be4196dfe9e39f96e431565a619d455a07bda9aa65977800376129148cbfe89d35a016dd51c95d6e6dc1e76307d315468b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008638c3bf2616aa831d4c008347e7c08301482084578f7aa78fe4b883e5bda9e7a59ee4bb99e9b1bca0c52daa7054babe515b17ee98540c0889cf5e1595c5dd77496997ca84a68c8da18805276a600980199df901b9f86c018504a817c8008252089453d284357ec70ce289d6d64134dfac8e511c8a3d888b6cfa3afc058000801ba08d94a55c7ac7adbfa2285ef7f4b0c955ae1a02647452cd4ead03ee6f449675c6a067149821b74208176d78fc4dffbe37c8b64eecfd47532406b9727c4ae8eb7c9af86d018504a817c8008252089453d284357ec70ce289d6d64134dfac8e511c8a3d890116db7272d6d94000801ca06d31e3d59bfea97a34103d8ce767a8fe7a79b8e2f30af1e918df53f9e78e69aba0098e5b80e1cc436421aa54eb17e96b08fe80d28a2fbd46451b56f2bca7a321e7f86c018504a817c8008252089453d284357ec70ce289d6d64134dfac8e511c8a3d8814da2c24e0d37014801ba0fdbbc462a8a60ac3d8b13ee236b45af9b7991cf4f0f556d3af46aa5aeca242aba05de5dc03fdcb6cf6d14609dbe6f5ba4300b8ff917c7d190325d9ea2144a7a2fbf86c018504a817c8008252089453d284357ec70ce289d6d64134dfac8e511c8a3d880e301365046d5000801ba0bafb9f71cef873b9e0395b9ed89aac4f2a752e2a4b88ba3c9b6c1fea254eae73a01cef688f6718932f7705d9c1f0dd5a8aad9ddb196b826775f6e5703fdb997706c0"; const BOOTSTRAP_NODES: [&str; 10] = [ "enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303", "enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303", "enode://5fbfb426fbb46f8b8c1bd3dd140f5b511da558cd37d60844b525909ab82e13a25ee722293c829e52cb65c2305b1637fa9a2ea4d6634a224d5f400bfe244ac0de@162.243.55.45:30303", "enode://42d8f29d1db5f4b2947cd5c3d76c6d0d3697e6b9b3430c3d41e46b4bb77655433aeedc25d4b4ea9d8214b6a43008ba67199374a9b53633301bca0cd20c6928ab@104.155.176.151:30303", "enode://814920f1ec9510aa9ea1c8f79d8b6e6a462045f09caa2ae4055b0f34f7416fca6facd3dd45f1cf1673c0209e0503f02776b8ff94020e98b6679a0dc561b4eba0@104.154.136.117:30303", "enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303", "enode://5cd218959f8263bc3721d7789070806b0adff1a0ed3f95ec886fb469f9362c7507e3b32b256550b9a7964a23a938e8d42d45a0c34b332bfebc54b29081e83b93@35.187.57.94:30303", "enode://39abab9d2a41f53298c0c9dc6bbca57b0840c3ba9dccf42aa27316addc1b7e56ade32a0a9f7f52d6c5db4fe74d8824bcedfeaecf1a4e533cacb71cf8100a9442@144.76.238.49:30303", "enode://f50e675a34f471af2438b921914b5f06499c7438f3146f6b8936f1faeb50b8a91d0d0c24fb05a66f05865cd58c24da3e664d0def806172ddd0d4c5bdbf37747e@144.76.238.49:30306", "enode://6dd3ac8147fa82e46837ec8c3223d69ac24bcdbab04b036a3705c14f3a02e968f7f1adfcdb002aacec2db46e625c04bf8b5a1f85bb2d40a479b3cc9d45a444af@104.237.131.102:30303" ]; // const BOOTSTRAP_NODES: [&str; 1] = [ // "enode://1a686737c260539c2a80b8defe649a356806ca43f71e1915ae00c65245b893e2eee31bc0ca41f7733d31ba7cdcd60584e3c3f89cccabba08ca5bce889f44244c@127.0.0.1:30303" // ]; // const BOOTSTRAP_NODES: [&str; 1] = [ // "enode://52656243997655790c1015e4c62e1afefd2f7d6b30c4434ea0a1557523348ad8515d15d0014002bdec80daba786714aa9bc4970ce99afa9e4fd6b94c98782669@35.194.140.8:30303" // ]; // const BOOTSTRAP_NODES: [&str; 1] = [ // "enode://1a686737c260539c2a80b8defe649a356806ca43f71e1915ae00c65245b893e2eee31bc0ca41f7733d31ba7cdcd60584e3c3f89cccabba08ca5bce889f44244c@127.0.0.1:60606" // ]; // const BOOTSTRAP_NODES: [&str; 1] = [ // "enode://3321955ec86feb439a20a295189408ac498c5390933e269fea0db3de949d0b23b69c6bab276cdf2c8ab56d019cfa6a1548e773de761151353b4390e62ce81318@127.0.0.1:30303" // ]; pub fn keccak256(data: &[u8]) -> H256 { let mut hasher = Keccak256::new(); hasher.input(data); let out = hasher.result(); H256::from(out.as_ref()) } fn main() { env_logger::init(); let addr = "0.0.0.0:60606".parse().unwrap(); let public_addr = "127.0.0.1".parse().unwrap(); let mut core = Core::new().unwrap(); let handle = core.handle(); let mut client = ETHStream::new( &addr, &public_addr, &handle, SecretKey::random(&mut OsRng::new().unwrap()), "etclient Rust/0.1.0".to_string(), 1, H256::from_str(GENESIS_HASH).unwrap(), H256::from_str(GENESIS_HASH).unwrap(), U256::from(GENESIS_DIFFICULTY), BOOTSTRAP_NODES.iter().map(|v| DPTNode::from_url(&Url::parse(v).unwrap()).unwrap()).collect(), DevP2PConfig { ping_interval: Duration::new(600, 0), ping_timeout_interval: Duration::new(700, 0), optimal_peers_len: 25, optimal_peers_interval: Duration::new(5, 0), reconnect_dividend: 5, listen: false, }).unwrap(); let mut best_number: U256 = U256::zero(); let mut best_hash: H256 = H256::from_str(GENESIS_HASH).unwrap(); let mut got_bodies_for_current = true; let dur = Duration::new(10, 0); let req_max_headers = 2048; let mut when = Instant::now() + dur; let (mut client_sender, mut client_receiver) = client.split(); let mut client_future = client_receiver.into_future(); let mut timeout = Timeout::new(dur, &handle).unwrap().boxed(); let mut active_peers = 0; loop { let ret = match core.run( client_future .select2(timeout) ) { Ok(ret) => ret, Err(_) => break, }; let (val, new_client_receiver) = match ret { future::Either::A(((val, new_client), t)) => { timeout = t.boxed(); (val, new_client) }, future::Either::B((_, fu)) => { client_future = fu; println!("request downloading header ..."); client_sender = core.run(client_sender.send(ETHSendMessage { node: RLPxNode::Any, data: ETHMessage::GetBlockHeadersByHash { hash: best_hash, max_headers: req_max_headers, skip: 0, reverse: false, } })).unwrap(); timeout = Timeout::new(dur, &handle).unwrap().boxed(); continue; } }; if val.is_none() { break; } let val = val.unwrap(); match val { ETHReceiveMessage::Normal { node, data, version } => { match data { ETHMessage::Status { .. } => (), ETHMessage::Transactions(_) => { println!("received new transactions"); }, ETHMessage::GetBlockHeadersByNumber { number, max_headers, skip, reverse } => { if number == U256::from(1920000) { println!("requested DAO header"); let block_raw = read_hex(ETC_DAO_BLOCK).unwrap(); let block: Block = rlp::decode(&block_raw); client_sender = core.run(client_sender.send(ETHSendMessage { node: RLPxNode::Peer(node), data: ETHMessage::BlockHeaders(vec![ block.header ]), })).unwrap(); } else { println!("requested header {}", number); client_sender = core.run(client_sender.send(ETHSendMessage { node: RLPxNode::Peer(node), data: ETHMessage::BlockHeaders(Vec::new()), })).unwrap(); } }, ETHMessage::GetBlockHeadersByHash { hash, max_headers, skip, reverse } => { println!("requested header {}", hash); client_sender = core.run(client_sender.send(ETHSendMessage { node: RLPxNode::Peer(node), data: ETHMessage::BlockHeaders(Vec::new()), })).unwrap(); }, ETHMessage::GetBlockBodies(hash) => { println!("requested body {:?}", hash); client_sender = core.run(client_sender.send(ETHSendMessage { node: RLPxNode::Peer(node), data: ETHMessage::BlockBodies(Vec::new()), })).unwrap(); }, ETHMessage::BlockHeaders(ref headers) => { println!("received block headers of len {}", headers.len()); if got_bodies_for_current { for header in headers { if header.parent_hash == best_hash { best_hash = keccak256(&rlp::encode(header).to_vec()); best_number = header.number; println!("updated best number: {}", header.number); println!("updated best hash: 0x{:x}", best_hash); } } } client_sender = core.run(client_sender.send(ETHSendMessage { node: RLPxNode::Any, data: ETHMessage::GetBlockHeadersByHash { hash: best_hash, max_headers: req_max_headers, skip: 0, reverse: false, } })).unwrap(); timeout = Timeout::new(dur, &handle).unwrap().boxed(); }, ETHMessage::BlockBodies(ref bodies) => { println!("received block bodies of len {}", bodies.len()); }, msg => { println!("received {:?}", msg); }, } }, ETHReceiveMessage::Connected { .. } => { active_peers += 1; }, ETHReceiveMessage::Disconnected { .. } => { active_peers -= 1; }, } println!("current active peers: {}", active_peers); client_future = new_client_receiver.into_future(); } }