├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── NOTICE ├── README.md └── src ├── bin └── client.rs ├── chaindb.rs ├── constructor.rs ├── dispatcher.rs ├── dns.rs ├── downstream.rs ├── error.rs ├── hammersbald.rs ├── headercache.rs ├── headerdownload.rs ├── lib.rs ├── lightning.rs ├── p2p.rs ├── ping.rs └── timeout.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | *.iml 4 | Cargo.lock 5 | client.db.* 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | - beta 5 | - stable 6 | cache: cargo 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "murmel" 3 | version = "0.2.1" 4 | authors = ["Tamas Blummer "] 5 | license = "Apache-2.0" 6 | homepage = "https://github.com/rust-bitcoin/murmel/" 7 | repository = "https://github.com/rust-bitcoin/murmel/" 8 | documentation = "https://github.com/rust-bitcoin/murmel/" 9 | description = "Murmel Bitcoin node" 10 | keywords = [ "bitcoin" ] 11 | readme = "README.md" 12 | edition = "2018" 13 | 14 | [features] 15 | default = ["hammersbald"] 16 | 17 | [lib] 18 | name = "murmel" 19 | path = "src/lib.rs" 20 | 21 | [dependencies] 22 | lightning = { version ="0.0.9", optional=true } 23 | bitcoin = { version= "0.21", features=["use-serde"]} 24 | bitcoin_hashes = "0.7" 25 | mio = "0.6" 26 | rand = "0.7" 27 | log = "0.4" 28 | simple_logger = "0.5.0" 29 | byteorder = "1.2" 30 | lru-cache = "0.1.1" 31 | futures-preview = "=0.3.0-alpha.18" 32 | futures-timer = "0.3" 33 | serde="1" 34 | serde_derive="1" 35 | 36 | ## optional 37 | hammersbald = { version= "2.4", features=["bitcoin_support"], optional=true } 38 | 39 | [dev-dependencies] 40 | rustc-serialize = "0.3" 41 | hex = "0.3" 42 | tempfile = "3.0.2" 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Murmel - Bitcoin Node written by Tamas Blummer 2018-2019 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Safety Dance](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) 2 | 3 | # Murmel 4 | Murmel is a lightweight Bitcoin node. Its intended use is to serve a lightning network stack with a settlement layer. 5 | Its resource requirements are marginal if compared to a Bitcoin Core node. 6 | 7 | A Murmel determines the chain with most work on its own and is capable of doing further checks. Its 8 | security guarantee is at least as defined in the Simplified Payment Verification (SPV) section of Satoshi's white paper. 9 | 10 | The bitcoin network is governed by full nodes. Full nodes determine which blocks are valid and thereby decide if a miner gets paid 11 | for the block it creates. The chain with most work therefore communicates what full nodes think bitcoin is. Therefore following 12 | the chain with most work is not following miner, as in popular belief, but following the majority opinion of full nodes. 13 | Read more about this [here](https://medium.com/@tamas.blummer/follow-the-pow-d6d1d1f479bd). 14 | 15 | Murmel does not maintain a memory pool of transactions, as unconfirmed payments unsecure to accept. 16 | Use Murmel to accept confirmed payments or to underpin a Ligthning Network node. 17 | 18 | #### About the name 19 | Murmel is German for marble. Murmel is small, fast, hard and beautiful just like a marble. 20 | 21 | ## Design and Implementation notes 22 | Murmel implements a small and fast P2P engine using on [mio](https://crates.io/crates/mio). The network messages are routed 23 | to their respective processors and back through message queues. Processors of logically distinct tasks are otherwise 24 | de-coupled and run in their own thread. 25 | 26 | The blockchain data is persisted in a [Hammersbald](https://github.com/rust-bitcoin/hammersbald) database. 27 | 28 | Murmel's filter implementation [BIP158](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki) was moved into the rust-bitcoin project, 29 | [here](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/src/util/bip158.rs). 30 | 31 | Murmel will not use those filters until they are available committed into the bitcoin block chain as they are otherwise not safe to use: 32 | a disagreement between two sources of filters can not be resolved by a client without knowledge of the UTXO. Consulting a third node would evtl. give yet another answer. 33 | 34 | Filters that could be verified with the block content alone, as I (repeatedly) suggested on the 35 | [dev-list](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016646.html), 36 | were rejected in favor of the current design, that is in fact more convenient once committed, but only then. 37 | 38 | 39 | ## Status 40 | Under refactoring. 41 | 42 | ## How to run Murmel 43 | Murmel does not do anything useful yet. 44 | 45 | -------------------------------------------------------------------------------- /src/bin/client.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | extern crate bitcoin; 17 | extern crate log; 18 | extern crate murmel; 19 | extern crate rand; 20 | extern crate simple_logger; 21 | 22 | use bitcoin::network::constants::Network; 23 | use log::Level; 24 | use murmel::{ 25 | constructor::Constructor 26 | }; 27 | 28 | use std::{ 29 | env::args, 30 | net::{Ipv4Addr, SocketAddr, SocketAddrV4}, 31 | path::Path, 32 | str::FromStr, 33 | time::SystemTime 34 | }; 35 | 36 | pub fn main() { 37 | if find_opt("help") { 38 | println!("Murmel Client"); 39 | println!("{} [--help] [--log trace|debug|info|warn|error] [--connections n] [--peer ip_address:port] [--db database_file] [--network main|test]", args().next().unwrap()); 40 | println!("--log level: level is one of trace|debug|info|warn|error"); 41 | println!("--connections n: maintain at least n connections"); 42 | println!("--peer ip_address: connect to the given peer at start. You may use more than one --peer option."); 43 | println!("--db file: store data in the given sqlite database file. Created if does not exist."); 44 | println!("--network net: net is one of main|test|regtest for corresponding Bitcoin networks"); 45 | println!("--nodns : do not use dns seed"); 46 | println!("--birth unixtime : blocks will be downloaded if matching filters after this time stamp"); 47 | println!("defaults:"); 48 | println!("--peer 127.0.0.1:8333"); 49 | println!("--db client.db"); 50 | println!("--log debug"); 51 | println!("--nodns"); 52 | println!("--network main"); 53 | return; 54 | } 55 | if let Some (log) = find_arg("log") { 56 | match log.as_str() { 57 | "error" => simple_logger::init_with_level(Level::Error).unwrap(), 58 | "warn" => simple_logger::init_with_level(Level::Warn).unwrap(), 59 | "info" => simple_logger::init_with_level(Level::Info).unwrap(), 60 | "debug" => simple_logger::init_with_level(Level::Debug).unwrap(), 61 | "trace" => simple_logger::init_with_level(Level::Trace).unwrap(), 62 | _ => simple_logger::init_with_level(Level::Info).unwrap() 63 | } 64 | } 65 | else { 66 | simple_logger::init_with_level(Level::Debug).unwrap(); 67 | } 68 | 69 | let mut network = Network::Bitcoin; 70 | if let Some(net) = find_arg("network") { 71 | match net.as_str() { 72 | "main" => network = Network::Bitcoin, 73 | "test" => network = Network::Testnet, 74 | "regtest" => network = Network::Regtest, 75 | _ => network = Network::Bitcoin 76 | } 77 | } 78 | 79 | let mut peers = get_peers(); 80 | if peers.is_empty () { 81 | let port = match network { 82 | Network::Bitcoin => 8333, 83 | Network::Testnet => 18333, 84 | Network::Regtest => 18444, 85 | }; 86 | peers.push(SocketAddr::from(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port))); 87 | } 88 | let mut connections = 1; 89 | if let Some(numstring) = find_arg("connections") { 90 | connections = numstring.parse().unwrap(); 91 | } 92 | let listen = get_listeners(); 93 | let birth = if let Some(timestamp) = find_arg("birth") { 94 | timestamp.parse::().unwrap() 95 | } 96 | else { 97 | SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() 98 | }; 99 | 100 | let chaindb = 101 | if let Some(path) = find_arg("db") { 102 | Constructor::open_db(Some(&Path::new(path.as_str())), network, birth).unwrap() 103 | } else { 104 | Constructor::open_db(Some(&Path::new("client.db")), network, birth).unwrap() 105 | }; 106 | let mut spv = Constructor::new(network, listen, chaindb).unwrap(); 107 | spv.run(network, peers, connections).expect("can not start node"); 108 | } 109 | 110 | fn get_peers() -> Vec { 111 | find_args("peer").iter().map(|s| SocketAddr::from_str(s).unwrap()).collect() 112 | } 113 | 114 | fn get_listeners() -> Vec { 115 | find_args("listen").iter().map(|s| SocketAddr::from_str(s).unwrap()).collect() 116 | } 117 | 118 | // Returns key-value zipped iterator. 119 | fn zipped_args() -> impl Iterator { 120 | let key_args = args().filter(|arg| arg.starts_with("--")).map(|mut arg| arg.split_off(2)); 121 | let val_args = args().skip(1).filter(|arg| !arg.starts_with("--")); 122 | key_args.zip(val_args) 123 | } 124 | 125 | fn find_opt(key: &str) -> bool { 126 | let mut key_args = args().filter(|arg| arg.starts_with("--")).map(|mut arg| arg.split_off(2)); 127 | key_args.find(|ref k| k.as_str() == key).is_some() 128 | } 129 | 130 | fn find_arg(key: &str) -> Option { 131 | zipped_args().find(|&(ref k, _)| k.as_str() == key).map(|(_, v)| v) 132 | } 133 | 134 | fn find_args(key: &str) -> Vec { 135 | zipped_args().filter(|&(ref k, _)| k.as_str() == key).map(|(_, v)| v).collect() 136 | } 137 | -------------------------------------------------------------------------------- /src/chaindb.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Blockchain DB API for a node 18 | //! 19 | 20 | use std::sync::{Arc, RwLock}; 21 | 22 | use bitcoin::BitcoinHash; 23 | use bitcoin::blockdata::block::BlockHeader; 24 | 25 | use bitcoin_hashes::sha256d; 26 | 27 | use crate::error::Error; 28 | use crate::headercache::CachedHeader; 29 | 30 | use serde_derive::{Serialize, Deserialize}; 31 | 32 | /// Shared handle to a database storing the block chain 33 | /// protected by an RwLock 34 | pub type SharedChainDB = Arc>>; 35 | 36 | /// Blockchain DB API for a client node. 37 | pub trait ChainDB: Send + Sync { 38 | 39 | /// Initialize caches. 40 | fn init(&mut self) -> Result<(), Error>; 41 | 42 | /// Batch updates. Updates are permanent after finishing a batch. 43 | fn batch(&mut self) -> Result<(), Error>; 44 | 45 | /// Store a header. 46 | fn add_header(&mut self, header: &BlockHeader) -> Result>, Option>)>, Error>; 47 | 48 | /// Return position of hash on trunk if hash is on trunk. 49 | fn pos_on_trunk(&self, hash: &sha256d::Hash) -> Option; 50 | 51 | /// Iterate trunk [from .. tip]. 52 | fn iter_trunk<'a>(&'a self, from: u32) -> Box + 'a>; 53 | 54 | /// Iterate trunk [genesis .. from] in reverse order from is the tip if not specified. 55 | fn iter_trunk_rev<'a>(&'a self, from: Option) -> Box + 'a>; 56 | 57 | /// Retrieve the id of the block/header with most work. 58 | fn header_tip(&self) -> Option; 59 | 60 | /// Fetch a header by its id from cache. 61 | fn get_header(&self, id: &sha256d::Hash) -> Option; 62 | 63 | /// Fetch a header by its id from cache. 64 | fn get_header_for_height(&self, height: u32) -> Option; 65 | 66 | /// Locator for getheaders message. 67 | fn header_locators(&self) -> Vec; 68 | 69 | /// Store the header id with most work. 70 | fn store_header_tip(&mut self, tip: &sha256d::Hash) -> Result<(), Error>; 71 | 72 | /// Find header id with most work. 73 | fn fetch_header_tip(&self) -> Result, Error>; 74 | 75 | /// Read header from the DB. 76 | fn fetch_header(&self, id: &sha256d::Hash) -> Result, Error>; 77 | } 78 | 79 | /// A header enriched with information about its position on the blockchain 80 | #[derive(Debug, Clone, Serialize, Deserialize)] 81 | pub struct StoredHeader { 82 | /// header 83 | pub header: BlockHeader, 84 | /// chain height 85 | pub height: u32, 86 | /// log2 of total work 87 | pub log2work: f64, 88 | } 89 | 90 | // need to implement if put_hash_keyed and get_hash_keyed should be used 91 | impl BitcoinHash for StoredHeader { 92 | fn bitcoin_hash(&self) -> sha256d::Hash { 93 | self.header.bitcoin_hash() 94 | } 95 | } 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/constructor.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Construct the Murmel stack 18 | //! 19 | //! Assembles modules of this library to a complete service 20 | //! 21 | 22 | use bitcoin::{ 23 | network::{ 24 | constants::Network 25 | } 26 | }; 27 | use crate::hammersbald::Hammersbald; 28 | use crate::dispatcher::Dispatcher; 29 | use crate::dns::dns_seed; 30 | use crate::error::Error; 31 | use futures::{ 32 | executor::{ThreadPool, ThreadPoolBuilder}, 33 | future, 34 | Poll as Async, 35 | FutureExt, StreamExt, 36 | task::{SpawnExt, Context}, 37 | Future 38 | }; 39 | use std::pin::Pin; 40 | use futures_timer::Interval; 41 | use crate::headerdownload::HeaderDownload; 42 | #[cfg(feature = "lightning")] use crate::lightning::LightningConnector; 43 | use crate::p2p::{P2P, P2PControl, PeerMessageSender, PeerSource}; 44 | use crate::ping::Ping; 45 | use rand::{RngCore, thread_rng}; 46 | use std::{ 47 | collections::HashSet, 48 | net::SocketAddr, 49 | path::Path, 50 | sync::{Arc, mpsc, Mutex, RwLock, atomic::AtomicUsize}, 51 | }; 52 | use crate::timeout::Timeout; 53 | use crate::downstream::{DownStreamDummy, SharedDownstream}; 54 | use bitcoin::network::message::NetworkMessage; 55 | use bitcoin::network::message::RawNetworkMessage; 56 | use crate::p2p::BitcoinP2PConfig; 57 | use std::time::Duration; 58 | use crate::chaindb::SharedChainDB; 59 | 60 | const MAX_PROTOCOL_VERSION: u32 = 70001; 61 | const USER_AGENT: &'static str = concat!("/Murmel:", env!("CARGO_PKG_VERSION"), '/'); 62 | 63 | /// The complete stack 64 | pub struct Constructor { 65 | p2p: Arc>, 66 | /// this should be accessed by Lightning 67 | pub downstream: SharedDownstream 68 | } 69 | 70 | impl Constructor { 71 | /// open DBs 72 | pub fn open_db(path: Option<&Path>, network: Network, _birth: u64) -> Result { 73 | let mut chaindb = 74 | if let Some(path) = path { 75 | #[cfg(feature = "default")] 76 | Hammersbald::new(path, network)? 77 | } else { 78 | #[cfg(feature = "default")] 79 | Hammersbald::mem(network)? 80 | }; 81 | chaindb.init()?; 82 | Ok(Arc::new(RwLock::new(chaindb))) 83 | } 84 | 85 | /// Construct the stack 86 | pub fn new(network: Network, listen: Vec, chaindb: SharedChainDB) -> Result { 87 | const BACK_PRESSURE: usize = 10; 88 | 89 | let (to_dispatcher, from_p2p) = mpsc::sync_channel(BACK_PRESSURE); 90 | 91 | 92 | let p2pconfig = BitcoinP2PConfig { 93 | network, 94 | nonce: thread_rng().next_u64(), 95 | max_protocol_version: MAX_PROTOCOL_VERSION, 96 | user_agent: USER_AGENT.to_owned(), 97 | height: AtomicUsize::new(0), 98 | server: !listen.is_empty() 99 | }; 100 | 101 | let (p2p, p2p_control) = 102 | P2P::new(p2pconfig, PeerMessageSender::new(to_dispatcher), BACK_PRESSURE); 103 | 104 | #[cfg(feature = "lightning")] let lightning = Arc::new(Mutex::new(LightningConnector::new(network, p2p_control.clone()))); 105 | #[cfg(not(feature = "lightning"))] let lightning = Arc::new(Mutex::new(DownStreamDummy {})); 106 | 107 | 108 | let timeout = Arc::new(Mutex::new(Timeout::new(p2p_control.clone()))); 109 | 110 | let mut dispatcher = Dispatcher::new(from_p2p); 111 | 112 | dispatcher.add_listener(HeaderDownload::new(chaindb.clone(), p2p_control.clone(), timeout.clone(), lightning.clone())); 113 | dispatcher.add_listener(Ping::new(p2p_control.clone(), timeout.clone())); 114 | 115 | for addr in &listen { 116 | p2p_control.send(P2PControl::Bind(addr.clone())); 117 | } 118 | 119 | Ok(Constructor { p2p, downstream: lightning }) 120 | } 121 | 122 | /// Run the stack. This should be called AFTER registering listener of the ChainWatchInterface, 123 | /// so they are called as the stack catches up with the blockchain 124 | /// * peers - connect to these peers at startup (might be empty) 125 | /// * min_connections - keep connections with at least this number of peers. Peers will be randomly chosen 126 | /// from those discovered in earlier runs 127 | pub fn run(&mut self, network: Network, peers: Vec, min_connections: usize) -> Result<(), Error> { 128 | 129 | let mut executor = ThreadPoolBuilder::new().name_prefix("bitcoin-connect").pool_size(2).create().expect("can not start futures thread pool"); 130 | 131 | let p2p = self.p2p.clone(); 132 | for addr in &peers { 133 | executor.spawn(p2p.add_peer("bitcoin", PeerSource::Outgoing(addr.clone())).map(|_|())).expect("can not spawn task for peers"); 134 | } 135 | 136 | let keep_connected = KeepConnected { 137 | min_connections, p2p: self.p2p.clone(), 138 | earlier: HashSet::new(), 139 | dns: dns_seed(network), 140 | cex: executor.clone() 141 | }; 142 | executor.spawn(Interval::new(Duration::new(10, 0)).for_each(move |_| keep_connected.clone())).expect("can not keep connected"); 143 | 144 | let p2p = self.p2p.clone(); 145 | let mut cex = executor.clone(); 146 | executor.run(future::poll_fn(move |_| { 147 | let needed_services = 0; 148 | p2p.poll_events("bitcoin", needed_services, &mut cex); 149 | Async::Ready(()) 150 | })); 151 | Ok(()) 152 | } 153 | } 154 | 155 | #[derive(Clone)] 156 | struct KeepConnected { 157 | cex: ThreadPool, 158 | dns: Vec, 159 | earlier: HashSet, 160 | p2p: Arc>, 161 | min_connections: usize 162 | } 163 | 164 | impl Future for KeepConnected { 165 | type Output = (); 166 | 167 | fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Async { 168 | if self.p2p.n_connected_peers() < self.min_connections { 169 | let eligible = self.dns.iter().cloned().filter(|a| !self.earlier.contains(a)).collect::>(); 170 | if eligible.len() > 0 { 171 | let mut rng = thread_rng(); 172 | let choice = eligible[(rng.next_u32() as usize) % eligible.len()]; 173 | self.earlier.insert(choice.clone()); 174 | let add = self.p2p.add_peer("bitcoin", PeerSource::Outgoing(choice)).map(|_| ()); 175 | self.cex.spawn(add).expect("can not add peer for outgoing connection"); 176 | } 177 | } 178 | Async::Ready(()) 179 | } 180 | } -------------------------------------------------------------------------------- /src/dispatcher.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Messsage dispatcher 18 | //! 19 | 20 | 21 | use crate::p2p::{PeerMessageReceiver, PeerMessageSender}; 22 | use std::{ 23 | thread, 24 | sync::{Arc, Mutex} 25 | }; 26 | 27 | /// Dispatcher of incoming messages 28 | pub struct Dispatcher { 29 | listener: Arc>>> 30 | } 31 | 32 | impl Dispatcher { 33 | pub fn new(incoming: PeerMessageReceiver) -> Dispatcher { 34 | let listener = Arc::new(Mutex::new(Vec::new())); 35 | let l2 = listener.clone(); 36 | thread::Builder::new().name("dispatcher".to_string()).spawn( move || { Self::incoming_messages_loop (incoming, l2) }).unwrap(); 37 | Dispatcher{listener} 38 | } 39 | 40 | pub fn add_listener(&mut self, listener: PeerMessageSender) { 41 | let mut list = self.listener.lock().unwrap(); 42 | list.push(listener); 43 | } 44 | 45 | fn incoming_messages_loop (incoming: PeerMessageReceiver, listener: Arc>>>) { 46 | while let Ok(pm) = incoming.recv() { 47 | let list = listener.lock().unwrap(); 48 | for listener in list.iter() { 49 | listener.send(pm.clone()); 50 | } 51 | } 52 | panic!("dispatcher failed"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/dns.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Bitcoin DNS lookup 18 | //! 19 | //! Find initial Bitcoin node addresses by looking up trusted DNS servers 20 | //! This should only be used if the peer has no own knowledge where to find a node of the 21 | //! Bitcoin network 22 | //! 23 | //! 24 | 25 | use bitcoin::network::constants::Network; 26 | use log::{info, trace}; 27 | use std::net::{SocketAddr, ToSocketAddrs}; 28 | 29 | const MAIN_SEEDER: [&str; 9] = [ 30 | "seed.bitcoin.sipa.be", 31 | "dnsseed.bluematt.me", 32 | "dnsseed.bitcoin.dashjr.org", 33 | "seed.bitcoinstats.com", 34 | "seed.bitcoin.jonasschnelli.ch", 35 | "seed.btc.petertodd.org", 36 | "seed.bitcoin.sprovoost.nl", 37 | "dnsseed.emzy.de", 38 | "seed.bitcoin.wiz.biz", 39 | ]; 40 | 41 | const TEST_SEEDER: [&str; 4] = [ 42 | "testnet-seed.bitcoin.jonasschnelli.ch", 43 | "seed.tbtc.petertodd.org", 44 | "seed.testnet.bitcoin.sprovoost.nl", 45 | "testnet-seed.bluematt.me", 46 | ]; 47 | 48 | pub fn dns_seed(network: Network) -> Vec { 49 | let mut seeds = Vec::new(); 50 | if network == Network::Bitcoin { 51 | info!("reaching out for DNS seed..."); 52 | for seedhost in MAIN_SEEDER.iter() { 53 | if let Ok(lookup) = (*seedhost, 8333).to_socket_addrs() { 54 | for host in lookup { 55 | seeds.push(host); 56 | } 57 | } else { 58 | trace!("{} did not answer", seedhost); 59 | } 60 | } 61 | info!("received {} DNS seeds", seeds.len()); 62 | } 63 | if network == Network::Testnet { 64 | info!("reaching out for DNS seed..."); 65 | for seedhost in TEST_SEEDER.iter() { 66 | if let Ok(lookup) = (*seedhost, 18333).to_socket_addrs() { 67 | for host in lookup { 68 | seeds.push(host); 69 | } 70 | } else { 71 | trace!("{} did not answer", seedhost); 72 | } 73 | } 74 | info!("received {} DNS seeds", seeds.len()); 75 | } 76 | seeds 77 | } 78 | -------------------------------------------------------------------------------- /src/downstream.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Connector to downstream modules 18 | //! 19 | 20 | use bitcoin::{ 21 | blockdata::{ 22 | block::{Block, BlockHeader} 23 | }, 24 | }; 25 | 26 | use std::sync::{Arc, Mutex}; 27 | 28 | pub type SharedDownstream = Arc>; 29 | 30 | pub trait Downstream : Send + Sync { 31 | /// called by the node if new block added to trunk (longest chain) 32 | fn block_connected(&mut self, block: &Block, height: u32); 33 | 34 | /// called by the node if new header added to trunk (longest chain) 35 | fn header_connected(&mut self, header: &BlockHeader, height: u32); 36 | 37 | /// called by the node if a block is removed from trunk (orphaned from longest chain) 38 | fn block_disconnected(&mut self, header: &BlockHeader); 39 | } 40 | 41 | pub struct DownStreamDummy {} 42 | 43 | impl Downstream for DownStreamDummy { 44 | fn block_connected(&mut self, _block: &Block, _height: u32) {} 45 | 46 | fn header_connected(&mut self, _header: &BlockHeader, _height: u32) {} 47 | 48 | fn block_disconnected(&mut self, _header: &BlockHeader) {} 49 | } -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Murmel Error 18 | //! 19 | //! All modules of this library use this error class to indicate problems. 20 | //! 21 | 22 | use bitcoin::consensus::encode; 23 | use bitcoin::util; 24 | use bitcoin::util::bip158; 25 | use hammersbald; 26 | use std::convert; 27 | use std::fmt; 28 | use std::io; 29 | 30 | /// An error class to offer a unified error interface upstream 31 | pub enum Error { 32 | /// the block's work target is not correct 33 | SpvBadTarget, 34 | /// bad proof of work 35 | SpvBadProofOfWork, 36 | /// unconnected header chain detected 37 | UnconnectedHeader, 38 | /// no chain tip found 39 | NoTip, 40 | /// no peers to connect to 41 | NoPeers, 42 | /// unknown UTXO referred 43 | UnknownUTXO, 44 | /// Merkle root of block does not match the header 45 | BadMerkleRoot, 46 | /// downstream error 47 | Downstream(String), 48 | /// Network IO error 49 | IO(io::Error), 50 | /// Bitcoin util error 51 | Util(util::Error), 52 | /// Bitcoin serialize error 53 | Serialize(encode::Error), 54 | /// Hammersbald error 55 | Hammersbald(hammersbald::Error), 56 | /// Handshake failure 57 | Handshake, 58 | /// lost connection 59 | Lost(String) 60 | } 61 | 62 | impl std::error::Error for Error { 63 | fn description(&self) -> &str { 64 | "description() is deprecated; use Display" 65 | } 66 | 67 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 68 | match *self { 69 | Error::SpvBadTarget => None, 70 | Error::SpvBadProofOfWork => None, 71 | Error::UnconnectedHeader => None, 72 | Error::NoTip => None, 73 | Error::NoPeers => None, 74 | Error::UnknownUTXO => None, 75 | Error::Downstream(_) => None, 76 | Error::BadMerkleRoot => None, 77 | Error::IO(ref err) => Some(err), 78 | Error::Util(ref err) => Some(err), 79 | Error::Hammersbald(ref err) => Some(err), 80 | Error::Serialize(ref err) => Some(err), 81 | Error::Handshake => None, 82 | Error::Lost(_) => None 83 | } 84 | } 85 | } 86 | 87 | impl fmt::Display for Error { 88 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 89 | match *self { 90 | Error::SpvBadTarget => write!(f, "bad proof of work target"), 91 | Error::SpvBadProofOfWork => write!(f, "bad proof of work"), 92 | Error::UnconnectedHeader => write!(f, "unconnected header"), 93 | Error::NoTip => write!(f, "no chain tip found"), 94 | Error::UnknownUTXO => write!(f, "unknown utxo"), 95 | Error::NoPeers => write!(f, "no peers"), 96 | Error::BadMerkleRoot => 97 | write!(f, "merkle root of header does not match transaction list"), 98 | Error::Handshake => write!(f, "handshake"), 99 | Error::Lost(ref s) => write!(f, "lost connection: {}", s), 100 | Error::Downstream(ref s) => write!(f, "downstream error: {}", s), 101 | // The underlying errors already impl `Display`, so we defer to their implementations. 102 | Error::IO(ref err) => write!(f, "IO error: {}", err), 103 | Error::Util(ref err) => write!(f, "Util error: {}", err), 104 | Error::Hammersbald(ref err) => write!(f, "Hammersbald error: {}", err), 105 | Error::Serialize(ref err) => write!(f, "Serialize error: {}", err), 106 | } 107 | } 108 | } 109 | 110 | impl fmt::Debug for Error { 111 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 112 | (self as &dyn fmt::Display).fmt(f) 113 | } 114 | } 115 | 116 | impl convert::From for io::Error { 117 | fn from(err: Error) -> io::Error { 118 | match err { 119 | Error::IO(e) => e, 120 | _ => { 121 | io::Error::new(io::ErrorKind::Other, err.to_string()) 122 | } 123 | } 124 | } 125 | } 126 | 127 | impl convert::From for Error { 128 | fn from(err: io::Error) -> Error { 129 | Error::IO(err) 130 | } 131 | } 132 | 133 | 134 | impl convert::From for Error { 135 | fn from(err: util::Error) -> Error { 136 | Error::Util(err) 137 | } 138 | } 139 | 140 | impl convert::From for Error { 141 | fn from(err: hammersbald::Error) -> Error { 142 | Error::Hammersbald(err) 143 | } 144 | } 145 | 146 | impl convert::From for Error { 147 | fn from(err: encode::Error) -> Error { 148 | Error::Serialize(err) 149 | } 150 | } 151 | 152 | impl convert::From> for Error { 153 | fn from(err: Box) -> Self { 154 | Error::Downstream(err.to_string()) 155 | } 156 | } 157 | 158 | impl convert::From for Error { 159 | fn from(err: bip158::Error) -> Self { 160 | match err { 161 | bip158::Error::Io(io) => Error::IO(io), 162 | bip158::Error::UtxoMissing(_) => Error::UnknownUTXO 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /src/hammersbald.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Blockchain DB for a node 18 | //! 19 | 20 | use std::path::Path; 21 | 22 | use bitcoin::{BitcoinHash, Network}; 23 | use bitcoin::blockdata::block::BlockHeader; 24 | use bitcoin::blockdata::constants::genesis_block; 25 | 26 | use bitcoin_hashes::sha256d; 27 | use hammersbald::{BitcoinAdaptor, HammersbaldAPI, persistent, transient}; 28 | 29 | use crate::error::Error; 30 | use crate::headercache::{CachedHeader, HeaderCache}; 31 | use log::{debug, info, warn, error}; 32 | use crate::chaindb::StoredHeader; 33 | use crate::chaindb::ChainDB; 34 | 35 | /// Database storing the block chain 36 | pub struct Hammersbald { 37 | db: BitcoinAdaptor, 38 | headercache: HeaderCache, 39 | network: Network, 40 | } 41 | 42 | 43 | impl Hammersbald { 44 | 45 | /// Create an in-memory database instance 46 | pub fn mem(network: Network) -> Result, Error> { 47 | info!("working with in memory chain db"); 48 | let db = BitcoinAdaptor::new(transient(2)?); 49 | let headercache = HeaderCache::new(network); 50 | Ok(Box::from(Hammersbald { db, network, headercache })) 51 | } 52 | 53 | /// Create or open a persistent database instance identified by the path 54 | pub fn new(path: &Path, network: Network) -> Result, Error> { 55 | let basename = path.to_str().unwrap().to_string(); 56 | let db = BitcoinAdaptor::new(persistent((basename.clone()).as_str(), 100, 2)?); 57 | let headercache = HeaderCache::new(network); 58 | Ok(Box::from(Hammersbald { db, network, headercache })) 59 | } 60 | 61 | fn init_headers(&mut self) -> Result<(), Error> { 62 | if let Some(tip) = self.fetch_header_tip()? { 63 | info!("reading stored header chain from tip {}", tip); 64 | if self.fetch_header(&tip)?.is_some() { 65 | let mut h = tip; 66 | while let Some(stored) = self.fetch_header(&h)? { 67 | debug!("read stored header {}", &stored.bitcoin_hash()); 68 | self.headercache.add_header_unchecked(&h, &stored); 69 | if stored.header.prev_blockhash != sha256d::Hash::default() { 70 | h = stored.header.prev_blockhash; 71 | } else { 72 | break; 73 | } 74 | } 75 | self.headercache.reverse_trunk(); 76 | info!("read {} headers", self.headercache.len()); 77 | } else { 78 | warn!("unable to read header for tip {}", tip); 79 | self.init_to_genesis()?; 80 | } 81 | } else { 82 | info!("no header tip found"); 83 | self.init_to_genesis()?; 84 | } 85 | Ok(()) 86 | } 87 | 88 | fn init_to_genesis(&mut self) -> Result<(), Error> { 89 | let genesis = genesis_block(self.network).header; 90 | if let Some((cached, _, _)) = self.headercache.add_header(&genesis)? { 91 | info!("initialized with genesis header {}", genesis.bitcoin_hash()); 92 | self.db.put_hash_keyed(&cached.stored)?; 93 | self.db.batch()?; 94 | self.store_header_tip(&cached.bitcoin_hash())?; 95 | self.db.batch()?; 96 | } else { 97 | error!("failed to initialize with genesis header"); 98 | return Err(Error::NoTip); 99 | } 100 | Ok(()) 101 | } 102 | } 103 | 104 | impl ChainDB for Hammersbald { 105 | 106 | /// Initialize caches 107 | fn init(&mut self) -> Result<(), Error> { 108 | self.init_headers()?; 109 | Ok(()) 110 | } 111 | 112 | /// Batch updates. Updates are permanent after finishing a batch. 113 | fn batch(&mut self) -> Result<(), Error> { 114 | self.db.batch()?; 115 | Ok(()) 116 | } 117 | 118 | /// Store a header 119 | fn add_header(&mut self, header: &BlockHeader) -> Result>, Option>)>, Error> { 120 | if let Some((cached, unwinds, forward)) = self.headercache.add_header(header)? { 121 | self.db.put_hash_keyed(&cached.stored)?; 122 | if let Some(forward) = forward.clone() { 123 | if forward.len() > 0 { 124 | self.store_header_tip(forward.last().unwrap())?; 125 | } 126 | } 127 | return Ok(Some((cached.stored, unwinds, forward))); 128 | } 129 | Ok(None) 130 | } 131 | 132 | /// return position of hash on trunk if hash is on trunk 133 | fn pos_on_trunk(&self, hash: &sha256d::Hash) -> Option { 134 | self.headercache.pos_on_trunk(hash) 135 | } 136 | 137 | /// iterate trunk [from .. tip] 138 | fn iter_trunk<'a>(&'a self, from: u32) -> Box +'a> { 139 | self.headercache.iter_trunk(from) 140 | } 141 | 142 | /// iterate trunk [genesis .. from] in reverse order from is the tip if not specified 143 | fn iter_trunk_rev<'a>(&'a self, from: Option) -> Box +'a> { 144 | self.headercache.iter_trunk_rev(from) 145 | } 146 | 147 | /// retrieve the id of the block/header with most work 148 | fn header_tip(&self) -> Option { 149 | self.headercache.tip() 150 | } 151 | 152 | /// Fetch a header by its id from cache 153 | fn get_header(&self, id: &sha256d::Hash) -> Option { 154 | self.headercache.get_header(id) 155 | } 156 | 157 | /// Fetch a header by its id from cache 158 | fn get_header_for_height(&self, height: u32) -> Option { 159 | self.headercache.get_header_for_height(height) 160 | } 161 | 162 | /// locator for getheaders message 163 | fn header_locators(&self) -> Vec { 164 | self.headercache.locator_hashes() 165 | } 166 | 167 | /// Store the header id with most work 168 | fn store_header_tip(&mut self, tip: &sha256d::Hash) -> Result<(), Error> { 169 | self.db.put_keyed_encodable(HEADER_TIP_KEY, tip)?; 170 | Ok(()) 171 | } 172 | 173 | /// Find header id with most work 174 | fn fetch_header_tip(&self) -> Result, Error> { 175 | Ok(self.db.get_keyed_decodable::(HEADER_TIP_KEY)?.map(|(_, h)| h.clone())) 176 | } 177 | 178 | /// Read header from the DB 179 | fn fetch_header(&self, id: &sha256d::Hash) -> Result, Error> { 180 | Ok(self.db.get_hash_keyed::(id)?.map(|(_, header)| header)) 181 | } 182 | } 183 | 184 | const HEADER_TIP_KEY: &[u8] = &[0u8; 1]; 185 | 186 | #[cfg(test)] 187 | mod test { 188 | use bitcoin::{Network, BitcoinHash}; 189 | use bitcoin_hashes::sha256d::Hash; 190 | use bitcoin::blockdata::constants::genesis_block; 191 | 192 | use crate::hammersbald::Hammersbald; 193 | 194 | #[test] 195 | fn init_tip_header() { 196 | let network = Network::Testnet; 197 | let genesis_header = genesis_block(network).header; 198 | 199 | let mut chaindb = Hammersbald::mem(network).unwrap(); 200 | chaindb.init().unwrap(); 201 | chaindb.init().unwrap(); 202 | 203 | let header_tip = chaindb.header_tip(); 204 | assert!(header_tip.is_some(), "failed to get header for tip"); 205 | assert!(header_tip.unwrap().stored.bitcoin_hash().eq(&genesis_header.bitcoin_hash())) 206 | } 207 | 208 | #[test] 209 | fn init_recover_if_missing_tip_header() { 210 | let network = Network::Testnet; 211 | let genesis_header = genesis_block(network).header; 212 | 213 | let mut chaindb = Hammersbald::mem(network).unwrap(); 214 | let missing_tip_header_hash: Hash = "6cfb35868c4465b7c289d7d5641563aa973db6a929655282a7bf95c8257f53ef".parse().unwrap(); 215 | chaindb.store_header_tip(&missing_tip_header_hash).unwrap(); 216 | 217 | chaindb.init().unwrap(); 218 | 219 | let header_tip = chaindb.header_tip(); 220 | assert!(header_tip.is_some(), "failed to get header for tip"); 221 | assert!(header_tip.unwrap().stored.bitcoin_hash().eq(&genesis_header.bitcoin_hash())) 222 | } 223 | } 224 | 225 | 226 | -------------------------------------------------------------------------------- /src/headercache.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Cache of headers and the chain with most work 18 | //! 19 | 20 | use bitcoin::{ 21 | BitcoinHash, 22 | blockdata::block::BlockHeader, 23 | network::constants::Network, 24 | util::{ 25 | uint::Uint256, 26 | }, 27 | }; 28 | use bitcoin_hashes::sha256d::Hash as Sha256dHash; 29 | use bitcoin_hashes::Hash; 30 | use crate::chaindb::StoredHeader; 31 | use crate::error::Error; 32 | use log::trace; 33 | use std::{ 34 | collections::HashMap 35 | }; 36 | 37 | #[derive(Clone)] 38 | pub struct CachedHeader { 39 | pub stored : StoredHeader, 40 | id: Sha256dHash 41 | } 42 | 43 | impl CachedHeader { 44 | pub fn new (id: &Sha256dHash, header: StoredHeader) -> CachedHeader { 45 | CachedHeader{ stored: header, id: id.clone() } 46 | } 47 | 48 | /// Computes the target [0, T] that a blockhash must land in to be valid 49 | pub fn target(&self) -> Uint256 { 50 | // This is a floating-point "compact" encoding originally used by 51 | // OpenSSL, which satoshi put into consensus code, so we're stuck 52 | // with it. The exponent needs to have 3 subtracted from it, hence 53 | // this goofy decoding code: 54 | let (mant, expt) = { 55 | let unshifted_expt = self.stored.header.bits >> 24; 56 | if unshifted_expt <= 3 { 57 | ((self.stored.header.bits & 0xFFFFFF) >> (8 * (3 - unshifted_expt)), 0) 58 | } else { 59 | (self.stored.header.bits & 0xFFFFFF, 8 * ((self.stored.header.bits >> 24) - 3)) 60 | } 61 | }; 62 | 63 | // The mantissa is signed but may not be negative 64 | if mant > 0x7FFFFF { 65 | Default::default() 66 | } else { 67 | Uint256::from_u64(mant as u64).unwrap() << (expt as usize) 68 | } 69 | } 70 | 71 | /// Performs an SPV validation of a block, which confirms that the proof-of-work 72 | /// is correct, but does not verify that the transactions are valid or encoded 73 | /// correctly. 74 | pub fn spv_validate(&self, required_target: &Uint256) -> Result<(), Error> { 75 | use byteorder::{ByteOrder, LittleEndian}; 76 | 77 | let target = &self.target(); 78 | if target != required_target { 79 | return Err(Error::SpvBadTarget); 80 | } 81 | let data: [u8; 32] = self.bitcoin_hash().into_inner(); 82 | let mut ret = [0u64; 4]; 83 | LittleEndian::read_u64_into(&data, &mut ret); 84 | let hash = &Uint256(ret); 85 | if hash <= target { Ok(()) } else { Err(Error::SpvBadProofOfWork) } 86 | } 87 | 88 | /// Returns the total work of the block 89 | pub fn work(&self) -> Uint256 { 90 | // 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind) 91 | let mut ret = !self.target(); 92 | let mut ret1 = self.target(); 93 | ret1.increment(); 94 | ret = ret / ret1; 95 | ret.increment(); 96 | ret 97 | } 98 | } 99 | 100 | impl BitcoinHash for CachedHeader { 101 | fn bitcoin_hash(&self) -> Sha256dHash { 102 | self.id 103 | } 104 | } 105 | 106 | pub struct HeaderCache { 107 | // network 108 | network: Network, 109 | // all known headers 110 | headers: HashMap, 111 | // header chain with most work 112 | trunk: Vec, 113 | } 114 | 115 | const EXPECTED_CHAIN_LENGTH: usize = 600000; 116 | 117 | impl HeaderCache { 118 | pub fn new(network: Network) -> HeaderCache { 119 | HeaderCache { network, headers: HashMap::with_capacity(EXPECTED_CHAIN_LENGTH), trunk: Vec::with_capacity(EXPECTED_CHAIN_LENGTH) } 120 | } 121 | 122 | pub fn add_header_unchecked(&mut self, id: &Sha256dHash, stored: &StoredHeader) { 123 | let cached = CachedHeader::new(id, stored.clone()); 124 | self.headers.insert(id.clone(), cached); 125 | self.trunk.push(id.clone()); 126 | } 127 | 128 | pub fn reverse_trunk(&mut self) { 129 | self.trunk.reverse() 130 | } 131 | 132 | pub fn len (&self) -> usize { 133 | self.trunk.len() 134 | } 135 | 136 | /// add a Bitcoin header 137 | pub fn add_header(&mut self, header: &BlockHeader) -> Result>, Option>)>, Error> { 138 | if self.headers.get(&header.bitcoin_hash()).is_some() { 139 | // ignore already known header 140 | return Ok(None); 141 | } 142 | if header.prev_blockhash != Sha256dHash::default() { 143 | // regular update 144 | let previous; 145 | if let Some(prev) = self.headers.get(&header.prev_blockhash) { 146 | previous = prev.clone(); 147 | } else { 148 | // reject unconnected 149 | trace!("previous header not in cache {}", &header.prev_blockhash); 150 | return Err(Error::UnconnectedHeader); 151 | } 152 | // add to tree 153 | return Ok(Some(self.add_header_to_tree(&previous, header)?)); 154 | } else { 155 | // insert genesis 156 | let new_tip = header.bitcoin_hash(); 157 | let stored = CachedHeader::new(&new_tip, StoredHeader { 158 | header: header.clone(), 159 | height: 0, 160 | log2work: Self::log2(header.work()) 161 | }); 162 | self.trunk.push(new_tip.clone()); 163 | self.headers.insert(new_tip.clone(), stored.clone()); 164 | return Ok(Some((stored, None, Some(vec!(new_tip))))); 165 | } 166 | } 167 | 168 | fn log2(work: Uint256) -> f64 { 169 | // we will have u256 faster in Rust than 2^128 total work in Bitcoin 170 | assert!(work.0[2] == 0 && work.0[3] == 0); 171 | ((work.0[0] as u128 + ((work.0[1] as u128) << 64)) as f64).log2() 172 | } 173 | 174 | fn exp2(n: f64) -> Uint256 { 175 | // we will have u256 faster in Rust than 2^128 total work in Bitcoin 176 | assert!(n < 128.0); 177 | let e: u128 = n.exp2() as u128; 178 | let mut b = [0u64; 4]; 179 | b[0] = e as u64; 180 | b[1] = (e >> 64) as u64; 181 | Uint256(b) 182 | } 183 | 184 | fn max_target() -> Uint256 { 185 | Uint256::from_u64(0xFFFF).unwrap() << 208 186 | } 187 | 188 | // add header to tree, return stored, optional list of unwinds, optional list of extensions 189 | fn add_header_to_tree(&mut self, prev: &CachedHeader, next: &BlockHeader) -> Result<(CachedHeader, Option>, Option>), Error> { 190 | const DIFFCHANGE_INTERVAL: u32 = 2016; 191 | const DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600; 192 | const TARGET_BLOCK_SPACING: u32 = 600; 193 | 194 | let required_work = 195 | // Compute required difficulty if this is a diffchange block 196 | if (prev.stored.height + 1) % DIFFCHANGE_INTERVAL == 0 { 197 | let timespan = { 198 | // Scan back DIFFCHANGE_INTERVAL blocks 199 | let mut scan = prev.clone(); 200 | if self.tip_hash() == Some(scan.stored.header.prev_blockhash) { 201 | scan = self.headers.get(&self.trunk[self.trunk.len() - DIFFCHANGE_INTERVAL as usize - 2]).unwrap().clone(); 202 | } else { 203 | for _ in 0..(DIFFCHANGE_INTERVAL - 1) { 204 | if let Some(header) = self.headers.get(&scan.stored.header.prev_blockhash) { 205 | scan = header.clone(); 206 | } else { 207 | trace!("previous header not in cache (diff change) {}", &scan.stored.header.prev_blockhash); 208 | return Err(Error::UnconnectedHeader); 209 | } 210 | } 211 | } 212 | // Get clamped timespan between first and last blocks 213 | match prev.stored.header.time - scan.stored.header.time { 214 | n if n < DIFFCHANGE_TIMESPAN / 4 => DIFFCHANGE_TIMESPAN / 4, 215 | n if n > DIFFCHANGE_TIMESPAN * 4 => DIFFCHANGE_TIMESPAN * 4, 216 | n => n 217 | } 218 | }; 219 | // Compute new target 220 | let mut target = prev.stored.header.target(); 221 | target = target.mul_u32(timespan); 222 | target = target / Uint256::from_u64(DIFFCHANGE_TIMESPAN as u64).unwrap(); 223 | // Clamp below MAX_TARGET (difficulty 1) 224 | let max = Self::max_target(); 225 | if target > max { target = max }; 226 | // Compactify (make expressible in the 8+24 nBits float format) 227 | Self::satoshi_the_precision(target) 228 | // On non-diffchange blocks, Testnet has a rule that any 20-minute-long 229 | // block interval resets the difficulty to 1 230 | } else if self.network == Network::Testnet && 231 | next.time > prev.stored.header.time + 2 * TARGET_BLOCK_SPACING { 232 | Self::max_target() 233 | // On the other hand, if we are in Testnet and the block interval is less 234 | // than 20 minutes, we need to scan backward to find a block for which the 235 | // previous rule did not apply, to find the "real" difficulty. 236 | } else if self.network == Network::Testnet { 237 | // Scan back DIFFCHANGE_INTERVAL blocks 238 | let mut scan = prev.clone(); 239 | let mut height = prev.stored.height; 240 | let max_target = Self::max_target(); 241 | while height % DIFFCHANGE_INTERVAL != 0 && scan.stored.header.prev_blockhash != Sha256dHash::default() && scan.stored.header.target() == max_target { 242 | if let Some(header) = self.headers.get(&scan.stored.header.prev_blockhash) { 243 | scan = header.clone(); 244 | height = header.stored.height; 245 | } else { 246 | trace!("previous header not in cache (testnet) {}", &scan.stored.header.prev_blockhash); 247 | return Err(Error::UnconnectedHeader); 248 | } 249 | } 250 | scan.stored.header.target() 251 | // Otherwise just use the last block's difficulty 252 | } else { 253 | prev.stored.header.target() 254 | }; 255 | 256 | let cached = CachedHeader::new(&next.bitcoin_hash(), StoredHeader { 257 | header: next.clone(), 258 | height: prev.stored.height + 1, 259 | log2work: Self::log2(next.work() + Self::exp2(prev.stored.log2work)) 260 | }); 261 | 262 | // Check POW 263 | if cached.spv_validate(&required_work).is_err() { 264 | return Err(Error::SpvBadProofOfWork); 265 | } 266 | 267 | let next_hash = cached.bitcoin_hash(); 268 | 269 | // store header in cache 270 | self.headers.insert(next_hash.clone(), cached.clone()); 271 | if let Some(tip) = self.tip() { 272 | if tip.stored.log2work < cached.stored.log2work { 273 | // higher POW than previous tip 274 | 275 | // compute path to new tip 276 | let mut forks_at = next.prev_blockhash; 277 | let mut path_to_new_tip = Vec::new(); 278 | while self.pos_on_trunk(&forks_at).is_none() { 279 | if let Some(h) = self.headers.get(&forks_at) { 280 | forks_at = h.stored.header.prev_blockhash; 281 | path_to_new_tip.push(forks_at); 282 | } else { 283 | trace!("previous header not in cache (path to new tip) {}", &forks_at); 284 | return Err(Error::UnconnectedHeader); 285 | } 286 | } 287 | path_to_new_tip.reverse(); 288 | path_to_new_tip.push(next_hash); 289 | 290 | 291 | // compute list of headers no longer on trunk 292 | if forks_at != next.prev_blockhash { 293 | let mut unwinds = Vec::new(); 294 | 295 | if let Some(pos) = self.trunk.iter().rposition(|h| { *h == forks_at }) { 296 | if pos < self.trunk.len() - 1 { 297 | // store and cut headers that are no longer on trunk 298 | unwinds.extend(self.trunk[pos + 1..].iter().rev().map(|h| *h)); 299 | self.trunk.truncate(pos + 1); 300 | } 301 | } else { 302 | trace!("previous header not in cache (header no longer on trunk) {}", &forks_at); 303 | return Err(Error::UnconnectedHeader); 304 | } 305 | self.trunk.extend(path_to_new_tip.iter().map(|h| { *h })); 306 | return Ok((cached, Some(unwinds), Some(path_to_new_tip))); 307 | } else { 308 | self.trunk.extend(path_to_new_tip.iter().map(|h| { *h })); 309 | return Ok((cached, None, Some(path_to_new_tip))); 310 | } 311 | } else { 312 | return Ok((cached, None, None)); 313 | } 314 | } else { 315 | return Err(Error::NoTip); 316 | } 317 | } 318 | 319 | /// position on trunk (chain with most work from genesis to tip) 320 | pub fn pos_on_trunk(&self, hash: &Sha256dHash) -> Option { 321 | self.trunk.iter().rev().position(|e| { *e == *hash }).map(|p| (self.trunk.len() - p - 1) as u32) 322 | } 323 | 324 | /// retrieve the id of the block/header with most work 325 | pub fn tip(&self) -> Option { 326 | if let Some(id) = self.tip_hash() { 327 | return self.get_header(&id); 328 | } 329 | None 330 | } 331 | 332 | pub fn tip_hash(&self) -> Option { 333 | if let Some(tip) = self.trunk.last() { 334 | return Some(*tip); 335 | } 336 | None 337 | } 338 | 339 | /// taken from an early rust-bitcoin by Andrew Poelstra: 340 | /// This function emulates the `GetCompact(SetCompact(n))` in the Satoshi code, 341 | /// which drops the precision to something that can be encoded precisely in 342 | /// the nBits block header field. Savour the perversity. This is in Bitcoin 343 | /// consensus code. What. Gaah! 344 | fn satoshi_the_precision(n: Uint256) -> Uint256 { 345 | use bitcoin::util::BitArray; 346 | 347 | // Shift by B bits right then left to turn the low bits to zero 348 | let bits = 8 * ((n.bits() + 7) / 8 - 3); 349 | let mut ret = n >> bits; 350 | // Oh, did I say B was that fucked up formula? I meant sometimes also + 8. 351 | if ret.bit(23) { 352 | ret = (ret >> 8) << 8; 353 | } 354 | ret << bits 355 | } 356 | 357 | /// Fetch a header by its id from cache 358 | pub fn get_header(&self, id: &Sha256dHash) -> Option { 359 | if let Some(header) = self.headers.get(id) { 360 | return Some(header.clone()); 361 | } 362 | None 363 | } 364 | 365 | pub fn get_header_for_height(&self, height: u32) -> Option { 366 | if height < self.trunk.len() as u32 { 367 | self.headers.get(&self.trunk[height as usize]).cloned() 368 | } 369 | else { 370 | None 371 | } 372 | } 373 | 374 | pub fn iter_trunk<'a> (&'a self, from: u32) -> Box +'a> { 375 | Box::new(self.trunk.iter().skip(from as usize).map(move |a| self.headers.get(&*a).unwrap())) 376 | } 377 | 378 | pub fn iter_trunk_rev<'a> (&'a self, from: Option) -> Box +'a> { 379 | let len = self.trunk.len(); 380 | if let Some(from) = from { 381 | Box::new(self.trunk.iter().rev().skip(len - from as usize).map(move |a| self.headers.get(&*a).unwrap())) 382 | } 383 | else { 384 | Box::new(self.trunk.iter().rev().map(move |a| self.headers.get(&*a).unwrap())) 385 | } 386 | } 387 | 388 | // locator for getheaders message 389 | pub fn locator_hashes(&self) -> Vec { 390 | let mut locator = vec!(); 391 | let mut skip = 1; 392 | let mut count = 0; 393 | let mut s = 0; 394 | 395 | let iterator = self.trunk.iter().rev(); 396 | for h in iterator { 397 | if s == 0 { 398 | locator.push(h.clone()); 399 | count += 1; 400 | s = skip; 401 | if count > 10 { 402 | skip *= 2; 403 | } 404 | } 405 | s -= 1; 406 | } 407 | 408 | locator 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /src/headerdownload.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Download headers 18 | //! 19 | use bitcoin::{BitcoinHash, network::{ 20 | message::NetworkMessage, 21 | message_blockdata::{GetHeadersMessage, Inventory, InvType}, 22 | }, BlockHeader}; 23 | use bitcoin_hashes::sha256d::Hash as Sha256dHash; 24 | use crate::chaindb::SharedChainDB; 25 | use crate::error::Error; 26 | use crate::p2p::{P2PControl, P2PControlSender, PeerId, PeerMessage, PeerMessageReceiver, PeerMessageSender, SERVICE_BLOCKS}; 27 | use log::{info, trace, debug, error}; 28 | use std::{ 29 | collections::VecDeque, 30 | sync::mpsc, 31 | thread, 32 | time::Duration, 33 | }; 34 | use crate::timeout::{ExpectedReply, SharedTimeout}; 35 | use crate::downstream::SharedDownstream; 36 | 37 | pub struct HeaderDownload { 38 | p2p: P2PControlSender, 39 | chaindb: SharedChainDB, 40 | timeout: SharedTimeout, 41 | downstream: SharedDownstream 42 | } 43 | 44 | impl HeaderDownload { 45 | pub fn new(chaindb: SharedChainDB, p2p: P2PControlSender, timeout: SharedTimeout, downstream: SharedDownstream) -> PeerMessageSender { 46 | let (sender, receiver) = mpsc::sync_channel(p2p.back_pressure); 47 | 48 | let mut headerdownload = HeaderDownload { chaindb, p2p, timeout, downstream: downstream }; 49 | 50 | thread::Builder::new().name("header download".to_string()).spawn(move || { headerdownload.run(receiver) }).unwrap(); 51 | 52 | PeerMessageSender::new(sender) 53 | } 54 | 55 | fn run(&mut self, receiver: PeerMessageReceiver) { 56 | loop { 57 | while let Ok(msg) = receiver.recv_timeout(Duration::from_millis(1000)) { 58 | if let Err(e) = match msg { 59 | PeerMessage::Connected(pid,_) => { 60 | if self.is_serving_blocks(pid) { 61 | trace!("serving blocks peer={}", pid); 62 | self.get_headers(pid) 63 | } else { 64 | Ok(()) 65 | } 66 | } 67 | PeerMessage::Disconnected(_,_) => { 68 | Ok(()) 69 | } 70 | PeerMessage::Incoming(pid, msg) => { 71 | match msg { 72 | NetworkMessage::Headers(ref headers) => if self.is_serving_blocks(pid) { self.headers(headers, pid) } else { Ok(()) }, 73 | NetworkMessage::Inv(ref inv) => if self.is_serving_blocks(pid) { self.inv(inv, pid) } else { Ok(()) }, 74 | NetworkMessage::Ping(_) => { Ok(()) } 75 | _ => { Ok(()) } 76 | } 77 | }, 78 | _ => { Ok(())} 79 | } { 80 | error!("Error processing headers: {}", e); 81 | } 82 | } 83 | self.timeout.lock().unwrap().check(vec!(ExpectedReply::Headers)); 84 | } 85 | } 86 | 87 | fn is_serving_blocks(&self, peer: PeerId) -> bool { 88 | if let Some(peer_version) = self.p2p.peer_version(peer) { 89 | return peer_version.services & SERVICE_BLOCKS != 0; 90 | } 91 | false 92 | } 93 | 94 | // process an incoming inventory announcement 95 | fn inv(&mut self, v: &Vec, peer: PeerId) -> Result<(), Error> { 96 | let mut ask_for_headers = false; 97 | for inventory in v { 98 | // only care for blocks 99 | if inventory.inv_type == InvType::Block { 100 | let chaindb = self.chaindb.read().unwrap(); 101 | if chaindb.get_header(&inventory.hash).is_none() { 102 | debug!("received inv for new block {} peer={}", inventory.hash, peer); 103 | // ask for header(s) if observing a new block 104 | ask_for_headers = true; 105 | } 106 | } 107 | } 108 | if ask_for_headers { 109 | self.get_headers(peer)?; 110 | } 111 | Ok(()) 112 | } 113 | 114 | /// get headers this peer is ahead of us 115 | fn get_headers(&mut self, peer: PeerId) -> Result<(), Error> { 116 | if self.timeout.lock().unwrap().is_busy_with(peer, ExpectedReply::Headers) { 117 | return Ok(()); 118 | } 119 | let chaindb = self.chaindb.read().unwrap(); 120 | let locator = chaindb.header_locators(); 121 | if locator.len() > 0 { 122 | let first = if locator.len() > 0 { 123 | *locator.first().unwrap() 124 | } else { 125 | Sha256dHash::default() 126 | }; 127 | self.timeout.lock().unwrap().expect(peer, 1, ExpectedReply::Headers); 128 | self.p2p.send_network(peer, NetworkMessage::GetHeaders(GetHeadersMessage::new(locator, first))); 129 | } 130 | Ok(()) 131 | } 132 | 133 | fn headers(&mut self, headers: &Vec, peer: PeerId) -> Result<(), Error> { 134 | self.timeout.lock().unwrap().received(peer, 1, ExpectedReply::Headers); 135 | 136 | if headers.len() > 0 { 137 | // current height 138 | let mut height; 139 | // some received headers were not yet known 140 | let mut some_new = false; 141 | let mut moved_tip = None; 142 | { 143 | let chaindb = self.chaindb.read().unwrap(); 144 | 145 | if let Some(tip) = chaindb.header_tip() { 146 | height = tip.stored.height; 147 | } else { 148 | return Err(Error::NoTip); 149 | } 150 | } 151 | 152 | let mut headers_queue = VecDeque::new(); 153 | headers_queue.extend(headers.iter()); 154 | while !headers_queue.is_empty() { 155 | let mut disconnected_headers = Vec::new(); 156 | let mut connected_headers = Vec::new(); 157 | { 158 | let mut chaindb = self.chaindb.write().unwrap(); 159 | while let Some(header) = headers_queue.pop_front() { 160 | // add to blockchain - this also checks proof of work 161 | match chaindb.add_header(&header) { 162 | Ok(Some((stored, unwinds, forwards))) => { 163 | connected_headers.push((stored.header.clone(), stored.height)); 164 | // POW is ok, stored top chaindb 165 | some_new = true; 166 | 167 | if let Some(forwards) = forwards { 168 | moved_tip = Some(forwards.last().unwrap().clone()); 169 | } 170 | height = stored.height; 171 | 172 | if let Some(unwinds) = unwinds { 173 | disconnected_headers.extend(unwinds.iter() 174 | .map(|h| chaindb.get_header(h).unwrap().stored.header)); 175 | break; 176 | } 177 | } 178 | Ok(None) => {} 179 | Err(Error::SpvBadProofOfWork) => { 180 | info!("Incorrect POW, banning peer={}", peer); 181 | self.p2p.ban(peer, 100); 182 | return Ok(()); 183 | } 184 | Err(e) => { 185 | debug!("error {} processing header {} ", e, header.bitcoin_hash()); 186 | return Ok(()); 187 | } 188 | } 189 | } 190 | chaindb.batch()?; 191 | } 192 | // must call downstream outside of chaindb lock as it might also lock chaindb 193 | let mut downstream = self.downstream.lock().unwrap(); 194 | for header in &disconnected_headers { 195 | downstream.block_disconnected(header); 196 | } 197 | for (header, height) in &connected_headers { 198 | downstream.header_connected(header, *height); 199 | } 200 | } 201 | 202 | if some_new { 203 | // ask if peer knows even more 204 | self.get_headers(peer)?; 205 | } 206 | 207 | if let Some(new_tip) = moved_tip { 208 | info!("received {} headers new tip={} from peer={}", headers.len(), new_tip, peer); 209 | self.p2p.send(P2PControl::Height(height)); 210 | } else { 211 | debug!("received {} known or orphan headers [{} .. {}] from peer={}", headers.len(), headers[0].bitcoin_hash(), headers[headers.len()-1].bitcoin_hash(), peer); 212 | } 213 | } 214 | Ok(()) 215 | } 216 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Murmel Bitcoin node 18 | //! 19 | //! This library implements a Simplified Payment Verification (SPV) of Bitcoin 20 | //! 21 | 22 | #![deny(non_upper_case_globals)] 23 | #![deny(non_camel_case_types)] 24 | #![deny(non_snake_case)] 25 | #![deny(unused_mut)] 26 | #![deny(unused_must_use)] 27 | #![forbid(unsafe_code)] 28 | 29 | #[cfg(feature="lightning")] mod lightning; 30 | mod headercache; 31 | 32 | pub mod ping; 33 | pub mod dns; 34 | pub mod timeout; 35 | pub mod headerdownload; 36 | pub mod downstream; 37 | pub mod dispatcher; 38 | pub mod p2p; 39 | pub mod error; 40 | pub mod chaindb; 41 | #[cfg(feature = "default")] pub mod hammersbald; 42 | pub mod constructor; 43 | 44 | pub use error::Error; -------------------------------------------------------------------------------- /src/lightning.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Connector to serve a lightning network implementation 18 | //! 19 | //! This implements an interface to higher level applications 20 | //! 21 | 22 | use bitcoin::{ 23 | blockdata::{ 24 | block::{Block, BlockHeader}, 25 | transaction::Transaction, 26 | script::Script, 27 | }, 28 | network::{ 29 | message::NetworkMessage, 30 | constants::Network 31 | } 32 | }; 33 | 34 | use bitcoin_hashes::sha256d::Hash as Sha256dHash; 35 | 36 | use lightning::{ 37 | chain::chaininterface::{ChainListener, ChainWatchInterface, ChainWatchInterfaceUtil,ChainError}, 38 | util::logger::{Level, Logger, Record} 39 | }; 40 | 41 | use downstream::Downstream; 42 | 43 | use p2p::P2PControlSender; 44 | 45 | use std::sync::{Arc, Weak, Mutex}; 46 | 47 | struct LightningLogger{ 48 | level: Level 49 | } 50 | 51 | impl Logger for LightningLogger { 52 | fn log(&self, record: &Record) { 53 | if self.level >= record.level { 54 | debug!("{:<5} [{} : {}, {}] {}", record.level.to_string(), record.module_path, record.file, record.line, record.args); 55 | } 56 | } 57 | } 58 | 59 | pub type SharedLightningConnector = Arc>; 60 | 61 | /// connector to lightning network 62 | pub struct LightningConnector { 63 | util: ChainWatchInterfaceUtil, 64 | p2p: P2PControlSender 65 | } 66 | 67 | impl Downstream for LightningConnector { 68 | /// called by the node if new block added to trunk (longest chain) 69 | /// this will notify listeners on lightning side 70 | fn block_connected(&mut self, block: &Block, height: u32) { 71 | self.util.block_connected_with_filtering(block, height) 72 | } 73 | 74 | fn header_connected(&mut self, block: &BlockHeader, height: u32) {} 75 | 76 | /// called by the node if a block is removed from trunk (orphaned from longest chain) 77 | /// this will notify listeners on lightning side 78 | fn block_disconnected(&mut self, header: &BlockHeader) { 79 | self.util.block_disconnected(header) 80 | } 81 | } 82 | 83 | impl LightningConnector { 84 | /// create a connector 85 | pub fn new (network: Network, p2p: P2PControlSender) -> LightningConnector { 86 | LightningConnector { 87 | util: ChainWatchInterfaceUtil::new(network, Arc::new(LightningLogger{level: Level::Info})), 88 | p2p 89 | } 90 | } 91 | 92 | /// broadcast transaction to all connected peers 93 | pub fn broadcast (&self, tx: Transaction) { 94 | self.p2p.broadcast(NetworkMessage::Tx(tx)) 95 | } 96 | } 97 | 98 | impl ChainWatchInterface for LightningConnector { 99 | 100 | fn install_watch_tx(&self, _txid: &Sha256dHash, _script_pub_key: &Script) { 101 | unimplemented!() 102 | } 103 | 104 | /// install a listener to be called with transactions that spend the outpoint 105 | fn install_watch_outpoint(&self, outpoint: (Sha256dHash, u32), out_script: &Script) { 106 | self.util.install_watch_outpoint(outpoint, out_script) 107 | } 108 | 109 | /// install a listener to be called for every transaction 110 | fn watch_all_txn(&self) { 111 | self.util.watch_all_txn() 112 | } 113 | 114 | /// install a listener for blocks added to or removed from trunk 115 | fn register_listener(&self, listener: Weak) { 116 | self.util.register_listener(listener) 117 | } 118 | 119 | fn get_chain_utxo(&self, _genesis_hash: Sha256dHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { 120 | Err(ChainError::NotSupported) 121 | } 122 | } -------------------------------------------------------------------------------- /src/p2p.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # P2P network communication 18 | //! 19 | //! This module establishes network connections and routes messages between the P2P network and this node 20 | //! 21 | 22 | use bitcoin::{ 23 | consensus::{Decodable, encode} 24 | }; 25 | use bitcoin::network::{ 26 | address::Address, 27 | constants::Network, 28 | message::{NetworkMessage, RawNetworkMessage}, 29 | message_network::VersionMessage 30 | }; 31 | 32 | use crate::error::Error; 33 | use futures::{Poll as Async, Future, future, FutureExt, task::{Waker}, TryFutureExt}; 34 | use log::{info, trace, debug, error}; 35 | use mio::{ 36 | Event, Events, net::{TcpListener, TcpStream}, Poll, PollOpt, Ready, 37 | Token, 38 | unix::UnixReady 39 | }; 40 | use rand::{RngCore, thread_rng}; 41 | use std::{ 42 | cmp::{max, min}, 43 | collections::{HashMap, VecDeque}, 44 | fmt, 45 | io, 46 | io::{Read, Write}, 47 | net::{Shutdown, SocketAddr}, 48 | str::FromStr, 49 | sync::{Arc, atomic::{AtomicBool, AtomicUsize, Ordering}, mpsc, Mutex, 50 | RwLock 51 | }, 52 | thread, 53 | time::{Duration, SystemTime, UNIX_EPOCH} 54 | }; 55 | use std::marker::PhantomData; 56 | use bitcoin::consensus::serialize; 57 | use futures::task::{Spawn, SpawnExt}; 58 | 59 | const IO_BUFFER_SIZE:usize = 1024*1024; 60 | const EVENT_BUFFER_SIZE:usize = 1024; 61 | const CONNECT_TIMEOUT_SECONDS: u64 = 5; 62 | const BAN :u32 = 100; 63 | 64 | /// do we serve blocks? 65 | pub const SERVICE_BLOCKS:u64 = 1; 66 | /// requires segwit support 67 | pub const SERVICE_WITNESS:u64 = 1 << 3; 68 | /// require filters 69 | pub const SERVICE_FILTERS:u64 = 1 << 6; 70 | /// A peer's Id 71 | #[derive(Hash, Eq, PartialEq, Copy, Clone)] 72 | pub struct PeerId { 73 | network: &'static str, 74 | // mio token used in networking 75 | token: Token 76 | } 77 | 78 | impl fmt::Display for PeerId { 79 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 80 | write!(f, "{}-{}", self.network, self.token.0)?; 81 | Ok(()) 82 | } 83 | } 84 | type PeerMap = HashMap>>; 85 | 86 | /// A message from network to downstream 87 | #[derive(Clone)] 88 | pub enum PeerMessage { 89 | Outgoing(Message), 90 | Incoming(PeerId, Message), 91 | Connected(PeerId, Option), 92 | Disconnected(PeerId, bool) // true if banned 93 | } 94 | 95 | pub enum P2PControl { 96 | Send(PeerId, Message), 97 | Broadcast(Message), 98 | Ban(PeerId, u32), 99 | Disconnect(PeerId), 100 | Height(u32), 101 | Bind(SocketAddr) 102 | } 103 | 104 | type P2PControlReceiver = mpsc::Receiver>; 105 | 106 | #[derive(Clone)] 107 | pub struct P2PControlSender { 108 | sender: Arc>>>, 109 | peers: Arc>>, 110 | pub back_pressure: usize 111 | } 112 | 113 | impl P2PControlSender { 114 | fn new (sender: mpsc::Sender>, peers: Arc>>, back_pressure: usize) -> P2PControlSender { 115 | P2PControlSender { sender: Arc::new(Mutex::new(sender)), peers, back_pressure } 116 | } 117 | 118 | pub fn send (&self, control: P2PControl) { 119 | self.sender.lock().unwrap().send(control).expect("P2P control send failed"); 120 | } 121 | 122 | pub fn send_network (&self, peer: PeerId, msg: Message) { 123 | self.send(P2PControl::Send(peer, msg)) 124 | } 125 | 126 | pub fn send_random_network (&self, msg: Message) -> Option { 127 | let peers = self.peers.read().unwrap().keys().cloned().collect::>(); 128 | if peers.len() > 0 { 129 | let peer = peers[(thread_rng().next_u32() % peers.len() as u32) as usize]; 130 | self.send(P2PControl::Send(peer, msg)); 131 | return Some(peer); 132 | } 133 | None 134 | } 135 | 136 | pub fn broadcast (&self, msg: Message) { 137 | self.send(P2PControl::Broadcast(msg)) 138 | } 139 | 140 | pub fn ban(&self, peer: PeerId, increment: u32) { 141 | debug!("increase ban score with {} peer={}", increment, peer); 142 | self.send(P2PControl::Ban(peer, increment)) 143 | } 144 | 145 | pub fn peer_version (&self, peer: PeerId) -> Option { 146 | if let Some(peer) = self.peers.read().unwrap().get(&peer) { 147 | let locked_peer = peer.lock().unwrap(); 148 | return locked_peer.version.clone(); 149 | } 150 | None 151 | } 152 | 153 | pub fn peers (&self) -> Vec { 154 | self.peers.read().unwrap().keys().cloned().collect::>() 155 | } 156 | } 157 | 158 | #[derive(Clone)] 159 | pub enum PeerSource { 160 | Outgoing(SocketAddr), 161 | Incoming(Arc) 162 | } 163 | 164 | /// a map of peer id to peers 165 | pub type PeerMessageReceiver = mpsc::Receiver>; 166 | 167 | #[derive(Clone)] 168 | pub struct PeerMessageSender { 169 | sender: Option>>>> 170 | } 171 | 172 | impl PeerMessageSender { 173 | pub fn new (sender: mpsc::SyncSender>) -> PeerMessageSender { 174 | PeerMessageSender { sender: Some(Arc::new(Mutex::new(sender))) } 175 | } 176 | 177 | pub fn dummy () -> PeerMessageSender { 178 | PeerMessageSender{ sender: None } 179 | } 180 | 181 | pub fn send (&self, msg: PeerMessage) { 182 | if let Some(ref sender) = self.sender { 183 | sender.lock().unwrap().send(msg).expect("P2P message send failed"); 184 | } 185 | } 186 | } 187 | 188 | pub trait Command { 189 | fn command(&self)->String; 190 | } 191 | 192 | impl Command for RawNetworkMessage { 193 | fn command(&self) -> String { 194 | self.command() 195 | } 196 | } 197 | 198 | pub trait Version { 199 | fn is_verack(&self) ->bool; 200 | fn is_version(&self) -> Option; 201 | } 202 | 203 | #[derive(Clone)] 204 | pub struct VersionCarrier { 205 | /// The P2P network protocol version 206 | pub version: u32, 207 | /// A bitmask describing the services supported by this node 208 | pub services: u64, 209 | /// The time at which the `version` message was sent 210 | pub timestamp: u64, 211 | /// The network address of the peer receiving the message 212 | pub receiver: Address, 213 | /// The network address of the peer sending the message 214 | pub sender: Address, 215 | /// A random nonce used to detect loops in the network 216 | pub nonce: u64, 217 | /// A string describing the peer's software 218 | pub user_agent: String, 219 | /// The height of the maximum-work blockchain that the peer is aware of 220 | pub start_height: u32, 221 | /// Whether the receiving peer should relay messages to the sender; used 222 | /// if the sender is bandwidth-limited and would like to support bloom 223 | /// filtering. Defaults to true. 224 | pub relay: bool 225 | } 226 | 227 | impl Version for NetworkMessage { 228 | fn is_version(&self) -> Option { 229 | match self { 230 | NetworkMessage::Version(v) => { 231 | Some(VersionCarrier { 232 | version: v.version, 233 | services: v.services, 234 | timestamp: v.timestamp as u64, 235 | receiver: v.receiver.clone(), 236 | sender: v.sender.clone(), 237 | nonce: v.nonce, 238 | user_agent: v.user_agent.clone(), 239 | start_height: v.start_height as u32, 240 | relay: v.relay 241 | }) 242 | }, 243 | _ => None 244 | } 245 | } 246 | 247 | fn is_verack(&self) -> bool { 248 | match self { 249 | NetworkMessage::Verack => true, 250 | _ => false 251 | } 252 | } 253 | 254 | } 255 | 256 | pub trait P2PConfig { 257 | fn version (&self, remote: &SocketAddr, max_protocol_version: u32) -> Message; 258 | fn nonce(&self) -> u64; 259 | fn magic(&self) -> u32; 260 | fn user_agent(&self) -> &str; 261 | fn get_height(&self) -> u32; 262 | fn set_height(&self, height: u32); 263 | fn max_protocol_version(&self) -> u32; 264 | fn min_protocol_version(&self) -> u32; 265 | fn verack(&self) -> Message; 266 | fn wrap(&self, m: Message) -> Envelope; 267 | fn unwrap(&self, e: Envelope) -> Result; 268 | fn encode(&self, item: &Envelope, dst: &mut Buffer) -> Result<(), io::Error>; 269 | fn decode(&self, src: &mut Buffer) -> Result, io::Error>; 270 | } 271 | 272 | pub struct BitcoinP2PConfig { 273 | pub network: Network, 274 | // This node's identifier on the network (random) 275 | pub nonce: u64, 276 | // height of the blockchain tree trunk 277 | pub height: AtomicUsize, 278 | // This node's human readable type identification 279 | pub user_agent: String, 280 | // this node's maximum protocol version 281 | pub max_protocol_version: u32, 282 | // serving others 283 | pub server: bool, 284 | } 285 | 286 | struct PassThroughBufferReader<'a> { 287 | buffer: &'a mut Buffer 288 | } 289 | 290 | impl<'a> io::Read for PassThroughBufferReader<'a> { 291 | fn read(&mut self, buf: &mut [u8]) -> Result { 292 | self.buffer.read(buf) 293 | } 294 | } 295 | 296 | impl P2PConfig for BitcoinP2PConfig { 297 | // compile this node's version message for outgoing connections 298 | fn version (&self, remote: &SocketAddr, max_protocol_version: u32) -> NetworkMessage { 299 | // now in unix time 300 | let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64; 301 | 302 | let services = if !self.server { 303 | 0 304 | } else { 305 | SERVICE_BLOCKS + SERVICE_WITNESS + 306 | // announce that this node is capable of serving BIP157 messages 307 | SERVICE_FILTERS 308 | }; 309 | 310 | // build message 311 | NetworkMessage::Version(VersionMessage { 312 | version: min(max_protocol_version, self.max_protocol_version), 313 | services, 314 | timestamp, 315 | receiver: Address::new(remote, 1), 316 | // sender is only dummy 317 | sender: Address::new(remote, 1), 318 | nonce: self.nonce, 319 | user_agent: self.user_agent.clone(), 320 | start_height: self.height.load(Ordering::Relaxed) as i32, 321 | relay: true, 322 | }) 323 | } 324 | 325 | fn nonce(&self) -> u64 { 326 | self.nonce 327 | } 328 | 329 | fn magic(&self) -> u32 { 330 | self.network.magic() 331 | } 332 | 333 | fn user_agent(&self) -> &str { 334 | self.user_agent.as_str() 335 | } 336 | 337 | fn get_height(&self) -> u32 { 338 | self.height.load(Ordering::Relaxed) as u32 339 | } 340 | 341 | fn set_height(&self, height: u32) { 342 | self.height.store (height as usize, Ordering::Relaxed) 343 | } 344 | 345 | fn max_protocol_version(&self) -> u32 { 346 | self.max_protocol_version 347 | } 348 | 349 | fn min_protocol_version(&self) -> u32 { 350 | 70001 351 | } 352 | 353 | 354 | fn verack(&self) -> NetworkMessage { 355 | NetworkMessage::Verack 356 | } 357 | 358 | fn wrap(&self, m: NetworkMessage) -> RawNetworkMessage { 359 | RawNetworkMessage{magic: self.network.magic(), payload: m} 360 | } 361 | 362 | fn unwrap(&self, e: RawNetworkMessage) -> Result { 363 | Ok(e.payload) 364 | } 365 | 366 | // encode a message in Bitcoin's wire format extending the given buffer 367 | fn encode(&self, item: &RawNetworkMessage, dst: &mut Buffer) -> Result<(), io::Error> { 368 | dst.write_all(serialize(item).as_slice()) 369 | } 370 | 371 | // decode a message from the buffer if possible 372 | fn decode(&self, src: &mut Buffer) -> Result, io::Error> { 373 | // attempt to decode 374 | let passthrough = PassThroughBufferReader{buffer: src}; 375 | let decode: Result = 376 | Decodable::consensus_decode(passthrough); 377 | 378 | match decode { 379 | Ok(m) => { 380 | // success: free the read data in buffer and return the message 381 | src.commit(); 382 | Ok(Some(m)) 383 | } 384 | Err(encode::Error::Io(e)) => { 385 | if e.kind() == io::ErrorKind::UnexpectedEof { 386 | // need more data, rollback and retry after additional read 387 | src.rollback(); 388 | return Ok(None) 389 | } else { 390 | error!("{:?}", e); 391 | src.commit(); 392 | return Err(e); 393 | } 394 | }, 395 | Err(e) => { 396 | error!("{:?}", e); 397 | src.commit(); 398 | Err(io::Error::new(io::ErrorKind::InvalidData, e)) 399 | } 400 | } 401 | } 402 | } 403 | 404 | /// The P2P network layer 405 | pub struct P2P + Send + Sync + 'static> { 408 | // sender to the dispatcher of incoming messages 409 | dispatcher: PeerMessageSender, 410 | // network specific conf 411 | pub config: Config, 412 | // The collection of connected peers 413 | peers: Arc>>, 414 | // The poll object of the async IO layer (mio) 415 | // access to this is shared by P2P and Peer 416 | poll: Arc, 417 | // next peer id 418 | // atomic only for interior mutability 419 | next_peer_id: AtomicUsize, 420 | // waker 421 | waker: Arc>>, 422 | // server 423 | listener: Arc>>>, 424 | e: PhantomData 425 | } 426 | 427 | impl + Send + Sync> P2P { 430 | /// create a new P2P network controller 431 | pub fn new(config: Config, dispatcher: PeerMessageSender, back_pressure: usize) -> (Arc>, P2PControlSender) { 432 | let (control_sender, control_receiver) = mpsc::channel(); 433 | 434 | let peers = Arc::new(RwLock::new(PeerMap::new())); 435 | 436 | let p2p = Arc::new(P2P { 437 | dispatcher, 438 | config, 439 | peers: peers.clone(), 440 | poll: Arc::new(Poll::new().unwrap()), 441 | next_peer_id: AtomicUsize::new(0), 442 | waker: Arc::new(Mutex::new(HashMap::new())), 443 | listener: Arc::new(Mutex::new(HashMap::new())), 444 | e: PhantomData{} 445 | }); 446 | 447 | let p2p2 = p2p.clone(); 448 | 449 | thread::Builder::new().name("p2pcntrl".to_string()).spawn(move || p2p2.control_loop(control_receiver)).unwrap(); 450 | 451 | (p2p, P2PControlSender::new(control_sender, peers, back_pressure)) 452 | } 453 | 454 | pub fn connected_peers (&self) -> Vec { 455 | self.peers.read().unwrap().values() 456 | .filter_map(|peer| 457 | if let Ok(a) = peer.lock().unwrap().stream.peer_addr() { 458 | Some(a) 459 | } else {None}).collect() 460 | } 461 | 462 | pub fn n_connected_peers (&self) -> usize { 463 | self.peers.read().unwrap().len() 464 | } 465 | 466 | fn control_loop (&self, receiver: P2PControlReceiver) { 467 | while let Ok(control) = receiver.recv() { 468 | match control { 469 | P2PControl::Ban(peer_id, score) => { 470 | self.ban(peer_id, score); 471 | }, 472 | P2PControl::Disconnect(peer_id) => { 473 | self.disconnect(peer_id, false); 474 | }, 475 | P2PControl::Height(height) => { 476 | self.config.set_height(height); 477 | } 478 | P2PControl::Bind(addr) => { 479 | match self.add_listener(&addr) { 480 | Ok(()) => info!("listen to {}", addr), 481 | Err(err) => info!("failed to listen to {} with {}", addr, err) 482 | } 483 | }, 484 | P2PControl::Broadcast(message) => { 485 | for peer in self.peers.read().unwrap().values() { 486 | peer.lock().unwrap().send(message.clone()).expect("could not send to peer"); 487 | } 488 | } 489 | P2PControl::Send(peer_id, message) => { 490 | if let Some (peer) = self.peers.read().unwrap().get (&peer_id) { 491 | peer.lock().unwrap().send(message).expect("could not send to peer"); 492 | } 493 | } 494 | } 495 | } 496 | panic!("P2P Control loop failed"); 497 | } 498 | 499 | fn add_listener (&self, bind: &SocketAddr) -> Result<(), io::Error> { 500 | let listener = TcpListener::bind(bind)?; 501 | let token = Token(self.next_peer_id.fetch_add(1, Ordering::Relaxed)); 502 | self.poll.register(&listener, token, Ready::readable(), PollOpt::edge())?; 503 | self.listener.lock().unwrap().insert(token, Arc::new(listener)); 504 | Ok(()) 505 | } 506 | 507 | /// return a future that does not complete until the peer is connected 508 | pub fn add_peer (&self, network: &'static str, source: PeerSource) -> impl Future> + Send { 509 | // new token, never re-using previously connected peer's id 510 | // so log messages are easier to follow 511 | let token = Token(self.next_peer_id.fetch_add(1, Ordering::Relaxed)); 512 | let pid = PeerId{network, token}; 513 | 514 | let peers = self.peers.clone(); 515 | let peers2 = self.peers.clone(); 516 | let waker = self.waker.clone(); 517 | 518 | self.connecting(pid, source) 519 | .map_err(move |e| { 520 | let mut peers = peers2.write().unwrap(); 521 | if let Some(peer) = peers.remove(&pid) { 522 | peer.lock().unwrap().stream.shutdown(Shutdown::Both).unwrap_or(()); 523 | } 524 | e 525 | }) 526 | .and_then (move |addr| { 527 | future::poll_fn(move |ctx| { 528 | if peers.read().unwrap().get(&pid).is_some() { 529 | waker.lock().unwrap().insert(pid, ctx.waker().clone()); 530 | Async::Pending 531 | } else { 532 | debug!("finished orderly peer={}", pid); 533 | Async::Ready(Ok(addr)) 534 | } 535 | }) 536 | }) 537 | } 538 | 539 | fn connecting(&self, pid: PeerId, source: PeerSource) -> impl Future> + Send { 540 | 541 | 542 | let version = self.config.version( 543 | &SocketAddr::from_str("127.0.0.1:8333").unwrap(), // TODO wrong address 544 | self.config.max_protocol_version()); 545 | let peers = self.peers.clone(); 546 | let peers2 = self.peers.clone(); 547 | let poll = self.poll.clone(); 548 | let waker = self.waker.clone(); 549 | 550 | future::poll_fn(move |_| { 551 | match Self::connect(version.clone(), peers.clone(), poll.clone(), pid, source.clone()) { 552 | Ok(addr) => Async::Ready(Ok(addr)), 553 | Err(e) => { Async::Ready(Err(e)) } 554 | } 555 | }).and_then(move |addr| { 556 | use futures_timer::TryFutureExt; 557 | 558 | future::poll_fn(move |ctx| 559 | if let Some(peer) = peers2.read().unwrap().get(&pid) { 560 | // return pid if peer is connected (handshake perfect) 561 | if peer.lock().unwrap().connected { 562 | trace!("woke up to handshake"); 563 | Async::Ready(Ok(addr)) 564 | } else { 565 | waker.lock().unwrap().insert(pid, ctx.waker().clone()); 566 | Async::Pending 567 | } 568 | } else { 569 | // rejected or failed handshake 570 | Async::Ready(Err(Error::Handshake)) 571 | } 572 | ).timeout(Duration::from_secs(CONNECT_TIMEOUT_SECONDS)) 573 | }) 574 | } 575 | 576 | // initiate connection to peer 577 | fn connect(version: Message, peers: Arc>>, poll: Arc, pid: PeerId, source: PeerSource) -> Result { 578 | let outgoing; 579 | let addr; 580 | let stream; 581 | match source { 582 | PeerSource::Outgoing(a) => { 583 | if let PeerSource::Outgoing(a) = source { 584 | if peers.read().unwrap().values() 585 | .any(|peer| 586 | if let Ok(addr) = peer.lock().unwrap().stream.peer_addr() { 587 | a.ip() == addr.ip() 588 | } else { false }) { 589 | debug!("rejecting outgoing connect for a peer already connected"); 590 | return Err(Error::Handshake); 591 | } 592 | } 593 | 594 | addr = a; 595 | outgoing = true; 596 | info!("trying outgoing connect to {} peer={}", addr, pid); 597 | stream = TcpStream::connect(&addr)?; 598 | }, 599 | PeerSource::Incoming(listener) => { 600 | let (s, a) = listener.accept()?; 601 | if peers.read().unwrap().values() 602 | .any(|peer| 603 | if let Ok(addr) = peer.lock().unwrap().stream.peer_addr() { 604 | a.ip() == addr.ip() 605 | } else { false }) { 606 | debug!("rejecting incoming connect from a peer already connected"); 607 | s.shutdown(Shutdown::Both).unwrap_or(()); 608 | return Err(Error::Handshake); 609 | } 610 | addr = a; 611 | stream = s; 612 | info!("trying incoming connect to {} peer={}", addr, pid); 613 | outgoing = false; 614 | } 615 | }; 616 | 617 | // create lock protected peer object 618 | let peer = Mutex::new(Peer::new(pid, stream, poll.clone(), outgoing)?); 619 | 620 | let mut peers = peers.write().unwrap(); 621 | 622 | // add to peer map 623 | peers.insert(pid, peer); 624 | 625 | let stored_peer = peers.get(&pid).unwrap(); 626 | 627 | if outgoing { 628 | stored_peer.lock().unwrap().register_write()?; 629 | } else { 630 | stored_peer.lock().unwrap().register_read()?; 631 | } 632 | if outgoing { 633 | // send this node's version message to peer 634 | peers.get(&pid).unwrap().lock().unwrap().send(version)?; 635 | } 636 | 637 | Ok(addr) 638 | } 639 | 640 | fn disconnect (&self, pid: PeerId, banned: bool) { 641 | self.dispatcher.send(PeerMessage::Disconnected(pid, banned)); 642 | { 643 | // remove from peers before waking up, so disconnect is recognized 644 | let mut peers = self.peers.write().unwrap(); 645 | if let Some(peer) = peers.remove(&pid) { 646 | peer.lock().unwrap().stream.shutdown(Shutdown::Both).unwrap_or(()); 647 | } 648 | } 649 | { 650 | let mut wakers = self.waker.lock().unwrap(); 651 | if let Some(waker) = wakers.remove(&pid) { 652 | debug!("waking for disconnect peer={}", pid); 653 | waker.wake(); 654 | } 655 | } 656 | } 657 | 658 | fn connected(&self, pid: PeerId, address: Option) { 659 | self.dispatcher.send(PeerMessage::Connected(pid, address)); 660 | } 661 | 662 | fn ban (&self, pid: PeerId, increment: u32) { 663 | let mut disconnect = false; 664 | if let Some(peer) = self.peers.read().unwrap().get(&pid) { 665 | let mut locked_peer = peer.lock().unwrap(); 666 | locked_peer.ban += increment; 667 | trace!("ban score {} for peer={}", locked_peer.ban, pid); 668 | if locked_peer.ban >= BAN { 669 | disconnect = true; 670 | } 671 | } 672 | if disconnect { 673 | debug!("ban peer={}", pid); 674 | self.disconnect(pid, true); 675 | } 676 | } 677 | 678 | fn event_processor (&self, event: Event, pid: PeerId, needed_services: u64, iobuf: &mut [u8]) -> Result<(), Error> { 679 | let readiness = UnixReady::from(event.readiness()); 680 | // check for error first 681 | if readiness.is_hup() || readiness.is_error() { 682 | info!("left us peer={}", pid); 683 | self.disconnect(pid, false); 684 | } else { 685 | // check for ability to write before read, to get rid of data before buffering more read 686 | // token should only be registered for write if there is a need to write 687 | // to avoid superfluous wakeups from poll 688 | if readiness.contains(Ready::writable()) { 689 | trace!("writeable peer={}", pid); 690 | 691 | // figure peer's entry in the peer map, provided it is still connected, ignore event if not 692 | if let Some(peer) = self.peers.read().unwrap().get(&pid) { 693 | // get and lock the peer from the peer map entry 694 | let mut locked_peer = peer.lock().unwrap(); 695 | loop { 696 | let mut get_next = true; 697 | // if there is previously unfinished write 698 | if let Ok(len) = locked_peer.write_buffer.read_ahead(iobuf) { 699 | if len > 0 { 700 | trace!("try write {} bytes to peer={}", len, pid); 701 | // try writing it out now 702 | let mut wrote = 0; 703 | while let Ok(wlen) = locked_peer.stream.write(&iobuf[wrote..len]) { 704 | if wlen == 0 { 705 | trace!("would block on peer={}", pid); 706 | // do not fetch next message until there is an unfinished write 707 | get_next = false; 708 | break; 709 | } 710 | trace!("wrote {} bytes to peer={}", wlen, pid); 711 | // advance buffer and drop used store 712 | locked_peer.write_buffer.advance(wlen); 713 | locked_peer.write_buffer.commit(); 714 | wrote += wlen; 715 | if wrote == len { 716 | break; 717 | } 718 | } 719 | } 720 | } 721 | if get_next { 722 | // get an outgoing message from the channel (if any) 723 | if let Some(msg) = locked_peer.try_receive() { 724 | // serialize the message 725 | let raw = self.config.wrap(msg); 726 | trace!("next message {} to peer={}", raw.command(), pid); 727 | // refill write buffer 728 | self.config.encode(&raw, &mut locked_peer.write_buffer)?; 729 | } else { 730 | // no unfinished write and no outgoing message 731 | // keep registered only for read events 732 | trace!("done writing to peer={}", pid); 733 | locked_peer.reregister_read()?; 734 | break; 735 | } 736 | } 737 | } 738 | } 739 | } 740 | // is peer readable ? 741 | if readiness.contains(Ready::readable()) { 742 | trace!("readable peer={}", pid); 743 | // collect incoming messages here 744 | // incoming messages are collected here for processing after release 745 | // of the lock on the peer map. 746 | let mut incoming = Vec::new(); 747 | // disconnect if set 748 | let mut disconnect = false; 749 | // how to disconnect 750 | let mut ban = false; 751 | // new handshake if set 752 | let mut handshake = false; 753 | // peer address 754 | let mut address = None; 755 | // read lock peer map and retrieve peer 756 | if let Some(peer) = self.peers.read().unwrap().get(&pid) { 757 | // lock the peer from the peer 758 | let mut locked_peer = peer.lock().unwrap(); 759 | // read the peer's socket 760 | if let Ok(len) = locked_peer.stream.read(iobuf) { 761 | trace!("received {} bytes from peer={}", len, pid); 762 | if len == 0 { 763 | debug!("read zero length message, disconnecting peer={}", pid); 764 | disconnect = true; 765 | } 766 | // accumulate in a buffer 767 | locked_peer.read_buffer.write_all(&iobuf[0..len])?; 768 | // extract messages from the buffer 769 | while let Some(msg) = self.config.decode(&mut locked_peer.read_buffer)? { 770 | trace!("received {} peer={}", msg.command(), pid); 771 | if locked_peer.connected { 772 | // regular processing after handshake 773 | incoming.push(msg); 774 | } 775 | else { 776 | // have to get both version and verack to complete handhsake 777 | if !(locked_peer.version.is_some() && locked_peer.got_verack) { 778 | // before handshake complete 779 | if let Ok(msg) = self.config.unwrap(msg) { 780 | if let Some(version) = msg.is_version() { 781 | if locked_peer.version.is_some() { 782 | // repeated version 783 | disconnect = true; 784 | ban = true; 785 | debug!("misbehaving peer, repeated version peer={}", pid); 786 | break; 787 | } 788 | if version.nonce == self.config.nonce() { 789 | // connect to myself 790 | disconnect = true; 791 | ban = true; 792 | debug!("rejecting to connect to myself peer={}", pid); 793 | break; 794 | } else { 795 | if version.version < self.config.min_protocol_version() || (needed_services & version.services) != needed_services { 796 | debug!("rejecting peer of version {} and services {:b} peer={}", version.version, version.services, pid); 797 | disconnect = true; 798 | break; 799 | } else { 800 | if !locked_peer.outgoing { 801 | // send own version message to incoming peer 802 | let addr = locked_peer.stream.peer_addr()?; 803 | trace!("send version to incoming connection {}", addr); 804 | // do not show higher version than the peer speaks 805 | let version = self.config.version(&addr, version.version); 806 | locked_peer.send(version)?; 807 | } else { 808 | // outgoing connects should not be behind this 809 | if version.start_height < self.config.get_height() { 810 | debug!("rejecting to connect with height {} peer={}", version.start_height, pid); 811 | disconnect = true; 812 | break; 813 | } 814 | } 815 | debug!("accepting peer of version {} and services {:b} peer={}", version.version, version.services, pid); 816 | // acknowledge version message received 817 | locked_peer.send(self.config.verack())?; 818 | // all right, remember this peer 819 | info!("client {} height: {} peer={}", version.user_agent, version.start_height, pid); 820 | let mut vm = version.clone(); 821 | // reduce protocol version to our capabilities 822 | vm.version = min(vm.version, self.config.max_protocol_version()); 823 | locked_peer.version = Some(vm); 824 | } 825 | } 826 | } else if msg.is_verack() { 827 | if locked_peer.got_verack { 828 | // repeated verack 829 | disconnect = true; 830 | ban = true; 831 | debug!("misbehaving peer, repeated version peer={}", pid); 832 | break; 833 | } 834 | trace!("got verack peer={}", pid); 835 | locked_peer.got_verack = true; 836 | } else { 837 | debug!("misbehaving peer unexpected message before handshake peer={}", pid); 838 | // some other message before handshake 839 | disconnect = true; 840 | ban = true; 841 | break; 842 | } 843 | if locked_peer.version.is_some() && locked_peer.got_verack { 844 | locked_peer.connected = true; 845 | handshake = true; 846 | address = if let Ok(addr) = locked_peer.stream.peer_addr() { 847 | Some(addr) 848 | } else { 849 | None 850 | } 851 | } 852 | } 853 | else { 854 | debug!("Ban for malformed message peer={}", pid); 855 | disconnect = true; 856 | ban = true; 857 | break; 858 | } 859 | } 860 | } 861 | } 862 | } 863 | else { 864 | debug!("IO error reading peer={}", pid); 865 | disconnect = true; 866 | } 867 | } 868 | if disconnect { 869 | info!("disconnecting peer={}", pid); 870 | self.disconnect(pid, ban); 871 | } 872 | else { 873 | if handshake { 874 | info!("handshake peer={}", pid); 875 | self.connected (pid, address); 876 | if let Some(w) = self.waker.lock().unwrap().remove(&pid) { 877 | trace!("waking for handshake"); 878 | w.wake(); 879 | } 880 | } 881 | // process queued incoming messages outside lock 882 | // as process could call back to P2P 883 | for msg in incoming { 884 | trace!("processing {} for peer={}", msg.command(), pid); 885 | if let Ok(m) = self.config.unwrap(msg) { 886 | self.dispatcher.send(PeerMessage::Incoming(pid, m)); 887 | } 888 | else { 889 | debug!("Ban for malformed message peer={}", pid); 890 | self.disconnect(pid, true); 891 | } 892 | } 893 | } 894 | } 895 | } 896 | Ok(()) 897 | } 898 | 899 | /// run the message dispatcher loop 900 | /// this method does not return unless there is an error obtaining network events 901 | /// run in its own thread, which will process all network events 902 | pub fn poll_events(&self, network: &'static str, needed_services: u64, spawn: &mut dyn Spawn) { 903 | // events buffer 904 | let mut events = Events::with_capacity(EVENT_BUFFER_SIZE); 905 | // IO buffer 906 | let mut iobuf = vec!(0u8; IO_BUFFER_SIZE); 907 | 908 | loop { 909 | // get the next batch of events 910 | self.poll.poll(&mut events, None).expect("can not poll mio events"); 911 | 912 | // iterate over events 913 | for event in events.iter() { 914 | // check for listener 915 | if let Some(server) = self.is_listener(event.token()) { 916 | trace!("incoming connection request"); 917 | spawn.spawn(self.add_peer(network, PeerSource::Incoming(server)).map(|_| ())).expect("can not add peer for incoming connection"); 918 | } else { 919 | // construct the id of the peer the event concerns 920 | let pid = PeerId { network, token: event.token() }; 921 | if let Err(error) = self.event_processor(event, pid, needed_services, iobuf.as_mut_slice()) { 922 | use std::error::Error; 923 | 924 | debug!("error {:?} peer={}", error.source(), pid); 925 | self.ban(pid, 10); 926 | } 927 | } 928 | } 929 | } 930 | } 931 | 932 | fn is_listener(&self, token: Token) -> Option> { 933 | if let Some(server) = self.listener.lock().unwrap().get(&token) { 934 | return Some(server.clone()) 935 | } 936 | None 937 | } 938 | } 939 | 940 | /// a peer 941 | struct Peer { 942 | /// the peer's id for log messages 943 | pub pid: PeerId, 944 | // the event poller, shared with P2P, needed here to register for events 945 | poll: Arc, 946 | // the connection to remote peer 947 | stream: TcpStream, 948 | // temporary buffer for not yet completely read incoming messages 949 | read_buffer: Buffer, 950 | // temporary buffer for not yet completely written outgoing messages 951 | write_buffer: Buffer, 952 | // did the remote peer already sent a verack? 953 | got_verack: bool, 954 | /// the version message the peer sent to us at connect 955 | pub version: Option, 956 | // channel into the event processing loop for outgoing messages 957 | sender: mpsc::Sender, 958 | // channel into the event processing loop for outgoing messages 959 | receiver: mpsc::Receiver, 960 | // is registered for write? 961 | writeable: AtomicBool, 962 | // connected and handshake complete? 963 | connected: bool, 964 | // ban score 965 | ban: u32, 966 | // outgoing or incoming connection 967 | outgoing: bool 968 | } 969 | 970 | impl Peer { 971 | /// create a new peer 972 | pub fn new (pid: PeerId, stream: TcpStream, poll: Arc, outgoing: bool) -> Result, Error> { 973 | let (sender, receiver) = mpsc::channel(); 974 | let peer = Peer{pid, poll: poll.clone(), stream, read_buffer: Buffer::new(), write_buffer: Buffer::new(), 975 | got_verack: false, version: None, sender, receiver, writeable: AtomicBool::new(false), 976 | connected: false, ban: 0, outgoing }; 977 | Ok(peer) 978 | } 979 | 980 | // re-register for peer readable events 981 | fn reregister_read(&self) -> Result<(), Error> { 982 | if self.writeable.swap(false, Ordering::Acquire) { 983 | trace!("re-register for read peer={}", self.pid); 984 | self.poll.reregister(&self.stream, self.pid.token, Ready::readable() | UnixReady::error() | UnixReady::hup(), PollOpt::level())?; 985 | } 986 | Ok(()) 987 | } 988 | 989 | // register for peer readable events 990 | fn register_read(&self) -> Result<(), Error> { 991 | trace!("register for read peer={}", self.pid); 992 | self.poll.register(&self.stream, self.pid.token, Ready::readable() | UnixReady::error() | UnixReady::hup(), PollOpt::level())?; 993 | self.writeable.store(false, Ordering::Relaxed); 994 | Ok(()) 995 | } 996 | 997 | /// send a message to P2P network 998 | pub fn send (&self, msg: Message) -> Result<(), Error> { 999 | // send to outgoing message channel 1000 | self.sender.send(msg).map_err(| _ | Error::Downstream("can not send to peer queue".to_owned()))?; 1001 | // register for writable peer events since we have outgoing message 1002 | self.reregister_write()?; 1003 | Ok(()) 1004 | } 1005 | 1006 | // register for peer writable events 1007 | fn reregister_write(&self) -> Result<(), Error> { 1008 | if !self.writeable.swap(true, Ordering::Acquire) { 1009 | trace!("re-register for write peer={}", self.pid); 1010 | self.poll.reregister(&self.stream, self.pid.token, Ready::writable() | UnixReady::error() | UnixReady::hup(), PollOpt::level())?; 1011 | } 1012 | Ok(()) 1013 | } 1014 | 1015 | // register for peer writable events 1016 | fn register_write(&self) -> Result<(), Error> { 1017 | trace!("register for write peer={}", self.pid); 1018 | self.poll.register(&self.stream, self.pid.token, Ready::writable() | UnixReady::error() | UnixReady::hup(), PollOpt::level())?; 1019 | self.writeable.store(true, Ordering::Relaxed); 1020 | Ok(()) 1021 | } 1022 | 1023 | 1024 | // try to receive a message from the outgoing message channel 1025 | fn try_receive (&self) -> Option { 1026 | if let Ok (msg) = self.receiver.try_recv() { 1027 | Some (msg) 1028 | } else { 1029 | None 1030 | } 1031 | } 1032 | } 1033 | 1034 | // A buffer that can be: 1035 | // * rolled back and re-read from last commit 1036 | // * read ahead without moving read position 1037 | // * advance position 1038 | pub struct Buffer { 1039 | // a deque of chunks 1040 | chunks: VecDeque>, 1041 | // pos.0 - current chunk 1042 | // pos.1 - position to read next in the current chunk 1043 | pos: (usize, usize), 1044 | // a copy of pos at last checkpoint call 1045 | checkpoint: (usize, usize) 1046 | } 1047 | 1048 | impl Buffer { 1049 | // create new buffer 1050 | fn new () -> Buffer { 1051 | Buffer{ chunks: VecDeque::new(), pos: (0, 0), checkpoint: (0, 0) } 1052 | } 1053 | 1054 | /// not yet consumed length of the buffer 1055 | pub fn len(&self) -> usize { 1056 | self.chunks.iter().skip(self.pos.0).map(|c| c.len()).sum::() - self.pos.1 1057 | } 1058 | 1059 | /// rollback to last commit 1060 | pub fn rollback (&mut self) { 1061 | self.pos = self.checkpoint; 1062 | } 1063 | 1064 | /// checkpoint and drop already read content 1065 | pub fn commit (&mut self) { 1066 | // drop read chunks 1067 | self.chunks.drain(0 .. self.pos.0); 1068 | // current chunk is now the first 1069 | self.pos.0 = 0; 1070 | self.checkpoint = self.pos; 1071 | } 1072 | 1073 | // read without advancing position 1074 | // subsequent read would deliver the same data again 1075 | fn read_ahead (&mut self, buf: &mut [u8]) -> Result { 1076 | let mut pos = (self.pos.0, self.pos.1); 1077 | if self.chunks.len() == 0 { 1078 | // no chunks -> no content 1079 | Ok(0) 1080 | } 1081 | else { 1082 | // number of bytes already collected for the read 1083 | let mut have = 0; 1084 | // until read enough 1085 | while have < buf.len() { 1086 | // current chunk 1087 | let current = &self.chunks[pos.0]; 1088 | // number of bytes available to read from current chunk 1089 | let available = min(buf.len() - have, current.len() - pos.1); 1090 | // copy those 1091 | buf[have..have+available].copy_from_slice(¤t[pos.1..pos.1 + available]); 1092 | // move pointer 1093 | pos.1 += available; 1094 | // have more 1095 | have += available; 1096 | // if current chunk was wholly read 1097 | if pos.1 == current.len() { 1098 | // there are more chunks 1099 | if pos.0 < self.chunks.len() - 1 { 1100 | // move pointer to begin of next chunk 1101 | pos.0 += 1; 1102 | pos.1 = 0; 1103 | } 1104 | else { 1105 | // we can't have more now 1106 | break; 1107 | } 1108 | } 1109 | } 1110 | // return the number of bytes that could be read 1111 | Ok(have) 1112 | } 1113 | } 1114 | 1115 | // advance position by len 1116 | fn advance (&mut self, len: usize) -> usize { 1117 | let mut have = 0; 1118 | // until read enough 1119 | while have < len { 1120 | // current chunk 1121 | let current = &self.chunks[self.pos.0]; 1122 | // number of bytes available to read from current chunk 1123 | let available = min(len - have, current.len() - self.pos.1); 1124 | // move pointer 1125 | self.pos.1 += available; 1126 | // have more 1127 | have += available; 1128 | // if current chunk was wholly read 1129 | if self.pos.1 == current.len() { 1130 | // there are more chunks 1131 | if self.pos.0 < self.chunks.len() - 1 { 1132 | // move pointer to begin of next chunk 1133 | self.pos.0 += 1; 1134 | self.pos.1 = 0; 1135 | } else { 1136 | // we can't have more now 1137 | break; 1138 | } 1139 | } 1140 | } 1141 | // return the number of bytes that could be read 1142 | have 1143 | } 1144 | 1145 | // read and advance position in one step 1146 | fn read_advance (&mut self, buf: &mut [u8]) -> Result { 1147 | if self.chunks.len() == 0 { 1148 | // no chunks -> no content 1149 | Ok(0) 1150 | } 1151 | else { 1152 | // number of bytes already collected for the read 1153 | let mut have = 0; 1154 | // until read enough 1155 | while have < buf.len() { 1156 | // current chunk 1157 | let current = &self.chunks[self.pos.0]; 1158 | // number of bytes available to read from current chunk 1159 | let available = min(buf.len() - have, current.len() - self.pos.1); 1160 | // copy those 1161 | buf[have..have+available].copy_from_slice(¤t[self.pos.1..self.pos.1 + available]); 1162 | // move pointer 1163 | self.pos.1 += available; 1164 | // have more 1165 | have += available; 1166 | // if current chunk was wholly read 1167 | if self.pos.1 == current.len() { 1168 | // there are more chunks 1169 | if self.pos.0 < self.chunks.len() - 1 { 1170 | // move pointer to begin of next chunk 1171 | self.pos.0 += 1; 1172 | self.pos.1 = 0; 1173 | } 1174 | else { 1175 | // we can't have more now 1176 | break; 1177 | } 1178 | } 1179 | } 1180 | // return the number of bytes that could be read 1181 | Ok(have) 1182 | } 1183 | } 1184 | } 1185 | 1186 | // write adapter for above buffer 1187 | impl Write for Buffer { 1188 | fn write(&mut self, buf: &[u8]) -> Result { 1189 | if buf.len() > 0 { 1190 | // number of chunks in buffer 1191 | let mut nc = self.chunks.len(); 1192 | // if no chunks or append to last chunk would create a too big chunk 1193 | if nc == 0 || (buf.len() + self.chunks[nc - 1].len()) > IO_BUFFER_SIZE { 1194 | // allocate and append a new chunk sufficient to hold buf but not smaller than IO_BUFFER_SIZE 1195 | self.chunks.push_back(Vec::with_capacity(max(buf.len(), IO_BUFFER_SIZE))); 1196 | nc += 1; 1197 | } 1198 | // append buf to current chunk 1199 | self.chunks[nc - 1].extend_from_slice(buf); 1200 | } 1201 | Ok(buf.len()) 1202 | } 1203 | // nop 1204 | fn flush(&mut self) -> Result<(), io::Error> { 1205 | Ok(()) 1206 | } 1207 | } 1208 | 1209 | // read adapter for above buffer 1210 | impl Read for Buffer { 1211 | fn read(&mut self, buf: &mut [u8]) -> Result { 1212 | self.read_advance(buf) 1213 | } 1214 | } 1215 | 1216 | 1217 | -------------------------------------------------------------------------------- /src/ping.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # regularly ping peers 18 | //! 19 | 20 | use bitcoin::network::message::NetworkMessage; 21 | use crate::p2p::{ 22 | P2PControlSender, PeerId, PeerMessage, PeerMessageReceiver, PeerMessageSender 23 | }; 24 | use rand::{RngCore, thread_rng}; 25 | use std::{ 26 | collections::HashMap, 27 | sync::mpsc, 28 | thread, 29 | time::Duration 30 | }; 31 | use crate::timeout::{ExpectedReply, SharedTimeout}; 32 | 33 | // ping peers every SECS seconds if not asked anything else in the meanwhile 34 | const SECS: u64 = 60; 35 | 36 | pub struct Ping { 37 | p2p: P2PControlSender, 38 | timeout: SharedTimeout, 39 | asked: HashMap 40 | } 41 | 42 | 43 | impl Ping { 44 | pub fn new(p2p: P2PControlSender, timeout: SharedTimeout) -> PeerMessageSender { 45 | let (sender, receiver) = mpsc::sync_channel(p2p.back_pressure); 46 | let mut ping = Ping { p2p, timeout, asked: HashMap::new() }; 47 | 48 | thread::Builder::new().name("ping".to_string()).spawn(move || { ping.run(receiver) }).unwrap(); 49 | 50 | PeerMessageSender::new(sender) 51 | } 52 | 53 | fn run(&mut self, receiver: PeerMessageReceiver) { 54 | loop { 55 | while let Ok(msg) = receiver.recv_timeout(Duration::from_millis(SECS*1000)) { 56 | match msg { 57 | PeerMessage::Disconnected(pid,_) => { 58 | self.timeout.lock().unwrap().forget(pid); 59 | self.asked.remove(&pid); 60 | }, 61 | PeerMessage::Incoming(pid, msg) => { 62 | match msg { 63 | NetworkMessage::Pong(n) => { 64 | if self.asked.remove(&pid) == Some(n) { 65 | self.timeout.lock().unwrap().received(pid, 1, ExpectedReply::Pong); 66 | } 67 | } 68 | NetworkMessage::Ping(nonce) => { 69 | self.p2p.send_network(pid, NetworkMessage::Pong(nonce)); 70 | } 71 | _ => { } 72 | } 73 | } 74 | _ => {} 75 | } 76 | } 77 | self.timeout.lock().unwrap().check(vec!(ExpectedReply::Pong)); 78 | for peer in self.p2p.peers() { 79 | if !self.timeout.lock().unwrap().is_busy(peer) { 80 | let ask = thread_rng().next_u64(); 81 | self.asked.insert(peer, ask); 82 | self.timeout.lock().unwrap().expect(peer, 1, ExpectedReply::Pong); 83 | self.p2p.send_network(peer, NetworkMessage::Ping(ask)); 84 | } 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/timeout.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2018-2019 Tamas Blummer 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //! 17 | //! # Keep track of peer timeouts 18 | //! 19 | 20 | use crate::p2p::{P2PControl, P2PControlSender, PeerId}; 21 | use log::debug; 22 | use std::{ 23 | cmp::min, 24 | collections::HashMap, 25 | sync::{Arc, Mutex}, 26 | time::{SystemTime, UNIX_EPOCH} 27 | }; 28 | use std::hash::Hash; 29 | 30 | pub type SharedTimeout = Arc>>; 31 | 32 | const TIMEOUT:u64 = 60; 33 | 34 | #[derive(Eq, PartialEq, Hash, Debug)] 35 | pub enum ExpectedReply { 36 | Block, 37 | Headers, 38 | Pong, 39 | FilterHeader, 40 | FilterCheckpoints, 41 | Filter 42 | } 43 | 44 | pub struct Timeout { 45 | timeouts: HashMap, 46 | expected: HashMap>, 47 | p2p: P2PControlSender 48 | } 49 | 50 | impl Timeout { 51 | pub fn new (p2p: P2PControlSender) -> Timeout { 52 | Timeout{p2p, timeouts: HashMap::new(), expected: HashMap::new()} 53 | } 54 | 55 | pub fn forget (&mut self, peer: PeerId) { 56 | self.timeouts.remove(&peer); 57 | self.expected.remove(&peer); 58 | } 59 | 60 | pub fn expect (&mut self, peer: PeerId, n: usize, what: Reply) { 61 | self.timeouts.insert(peer, Self::now() + TIMEOUT); 62 | *self.expected.entry(peer).or_insert(HashMap::new()).entry(what).or_insert(0) += n; 63 | } 64 | 65 | pub fn received (&mut self, peer: PeerId, n: usize, what: Reply) { 66 | if let Some(expected) = self.expected.get(&peer) { 67 | if let Some(m) = expected.get(&what) { 68 | if *m > 0 { 69 | self.timeouts.insert(peer, Self::now() + TIMEOUT); 70 | } 71 | } 72 | } 73 | { 74 | let expected = self.expected.entry(peer).or_insert(HashMap::new()).entry(what).or_insert(n); 75 | *expected -= min(n, *expected); 76 | } 77 | if let Some(expected) = self.expected.get(&peer) { 78 | if expected.values().all(|v| *v == 0) { 79 | self.timeouts.remove(&peer); 80 | } 81 | } 82 | } 83 | 84 | pub fn is_busy (&self, peer: PeerId) -> bool { 85 | self.timeouts.contains_key(&peer) 86 | } 87 | 88 | pub fn is_busy_with (&self, peer: PeerId, what: Reply) -> bool { 89 | if self.timeouts.contains_key(&peer) { 90 | if let Some(expected) = self.expected.get(&peer) { 91 | if let Some(n) = expected.get(&what) { 92 | return *n > 0; 93 | } 94 | } 95 | } 96 | false 97 | } 98 | 99 | 100 | pub fn check (&mut self, expected: Vec) { 101 | let mut banned = Vec::new(); 102 | for (peer, timeout) in &self.timeouts { 103 | if *timeout < Self::now () { 104 | if expected.iter().any(|expected| if let Some(e) = self.expected.get(peer) { if let Some(n) = e.get(expected) { *n>0 } else { false } } else { false }) { 105 | debug!("too slow answering {:?} requests {:?}, disconnecting peer={}", expected, self.expected.get(peer), *peer); 106 | self.p2p.send(P2PControl::Disconnect(*peer)); 107 | banned.push(*peer); 108 | } 109 | } 110 | } 111 | for peer in &banned { 112 | self.timeouts.remove(peer); 113 | self.expected.remove(peer); 114 | } 115 | } 116 | 117 | fn now() -> u64 { 118 | SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() 119 | } 120 | } --------------------------------------------------------------------------------