Merge branch 'master' into sync_transactions

This commit is contained in:
Svyatoslav Nikolsky 2016-11-17 11:14:05 +03:00
commit 4e74bb7f68
41 changed files with 735 additions and 465 deletions

28
Cargo.lock generated
View File

@ -104,13 +104,13 @@ dependencies = [
"bitcrypto 0.1.0", "bitcrypto 0.1.0",
"heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
"serialization 0.1.0", "serialization 0.1.0",
] ]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.17.1" version = "2.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -135,7 +135,7 @@ version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -199,7 +199,7 @@ dependencies = [
"gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -270,15 +270,15 @@ dependencies = [
"base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitcrypto 0.1.0", "bitcrypto 0.1.0",
"eth-secp256k1 0.5.6 (git+https://github.com/ethcore/rust-secp256k1)", "eth-secp256k1 0.5.6 (git+https://github.com/ethcore/rust-secp256k1)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -476,7 +476,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chain 0.1.0", "chain 0.1.0",
"clap 2.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
"db 0.1.0", "db 0.1.0",
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"import 0.1.0", "import 0.1.0",
@ -495,7 +495,7 @@ name = "primitives"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -565,13 +565,13 @@ dependencies = [
"gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "rustc-serialize" name = "rustc-serialize"
version = "0.3.19" version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -781,7 +781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum clap 2.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27dac76762fb56019b04aed3ccb43a770a18f80f9c2eb62ee1a18d9fb4ea2430" "checksum clap 2.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "40046b8a004bf3ba43b9078bf4b9b6d1708406a234848f925dbd7160a374c8a8"
"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
"checksum csv 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "266c1815d7ca63a5bd86284043faf91e8c95e943e55ce05dc0ae08e952de18bc" "checksum csv 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "266c1815d7ca63a5bd86284043faf91e8c95e943e55ce05dc0ae08e952de18bc"
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
@ -794,7 +794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5" "checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"
"checksum heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8c80e194758495a9109566134dc06e42ea0423987d6ceca016edaa90381b3549" "checksum heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8c80e194758495a9109566134dc06e42ea0423987d6ceca016edaa90381b3549"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b"
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8" "checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
@ -820,7 +820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>" "checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>" "checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" "checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818"
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"

View File

@ -26,7 +26,7 @@ pub fn merkle_root(hashes: &[H256]) -> H256 {
// duplicate the last element if len is not even // duplicate the last element if len is not even
if hashes.len() % 2 == 1 { if hashes.len() % 2 == 1 {
let last = &hashes[hashes.len() - 1]; let last = &hashes[hashes.len() - 1];
row.push(dhash256(&*concat(&last, last))); row.push(dhash256(&*concat(last, last)));
} }
merkle_root(&row) merkle_root(&row)

View File

@ -377,7 +377,7 @@ impl Storage {
/// all transaction meta is removed /// all transaction meta is removed
/// DOES NOT update best block /// DOES NOT update best block
fn decanonize_block(&self, context: &mut UpdateContext, hash: &H256) -> Result<(), Error> { fn decanonize_block(&self, context: &mut UpdateContext, hash: &H256) -> Result<(), Error> {
trace!(target: "reorg", "Decanonizing block {}", hash); trace!(target: "reorg", "Decanonizing block {}", hash.to_reversed_str());
// ensure that block is of the main chain // ensure that block is of the main chain
try!(self.block_number(hash).ok_or(Error::not_main(hash))); try!(self.block_number(hash).ok_or(Error::not_main(hash)));
@ -645,19 +645,34 @@ impl Store for Storage {
Err(Error::Consistency(consistency_error)) => { Err(Error::Consistency(consistency_error)) => {
match consistency_error { match consistency_error {
ConsistencyError::DoubleSpend(hash) => { ConsistencyError::DoubleSpend(hash) => {
warn!(target: "reorg", "Failed to reorganize to {} due to double-spend at {}", &block_hash, &hash); warn!(
target: "reorg",
"Failed to reorganize to {} due to double-spend at {}",
block_hash.to_reversed_str(),
hash.to_reversed_str()
);
// return without any commit // return without any commit
return Err(Error::reorganize(&hash)); return Err(Error::reorganize(&hash));
}, },
ConsistencyError::UnknownSpending(hash) => { ConsistencyError::UnknownSpending(hash) => {
warn!(target: "reorg", "Failed to reorganize to {} due to spending unknown transaction {}", &block_hash, &hash); warn!(
target: "reorg",
"Failed to reorganize to {} due to spending unknown transaction {}",
block_hash.to_reversed_str(),
hash.to_reversed_str()
);
// return without any commit // return without any commit
return Err(Error::reorganize(&hash)); return Err(Error::reorganize(&hash));
}, },
ConsistencyError::Unknown(hash) => { ConsistencyError::Unknown(hash) => {
// this is orphan block inserted or disconnected chain head updated, we allow that (by now) // this is orphan block inserted or disconnected chain head updated, we allow that (by now)
// so it is no-op // so it is no-op
warn!(target: "reorg", "Disconnected chain head {} updated with {}", &hash, &block_hash); warn!(
target: "reorg",
"Disconnected chain head {} updated with {}",
hash.to_reversed_str(),
block_hash.to_reversed_str()
);
}, },
_ => { _ => {
// we don't allow other errors on side chain/orphans // we don't allow other errors on side chain/orphans

View File

@ -3,6 +3,9 @@ use super::Magic;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Parameters that influence chain consensus. /// Parameters that influence chain consensus.
pub struct ConsensusParams { pub struct ConsensusParams {
/// Time when BIP16 becomes active.
/// See https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
pub bip16_time: u32,
/// Block height at which BIP65 becomes active. /// Block height at which BIP65 becomes active.
/// See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki /// See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
pub bip65_height: u32, pub bip65_height: u32,
@ -12,12 +15,15 @@ impl ConsensusParams {
pub fn with_magic(magic: Magic) -> Self { pub fn with_magic(magic: Magic) -> Self {
match magic { match magic {
Magic::Mainnet => ConsensusParams { Magic::Mainnet => ConsensusParams {
bip16_time: 1333238400, // Apr 1 2012
bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
}, },
Magic::Testnet => ConsensusParams { Magic::Testnet => ConsensusParams {
bip16_time: 1333238400, // Apr 1 2012
bip65_height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 bip65_height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
}, },
Magic::Regtest => ConsensusParams { Magic::Regtest => ConsensusParams {
bip16_time: 1333238400, // Apr 1 2012
bip65_height: 1351, bip65_height: 1351,
}, },
} }

View File

@ -33,3 +33,9 @@ impl<T> AsRef<[u8]> for Message<T> {
self.bytes.as_ref() self.bytes.as_ref()
} }
} }
impl<T> From<Message<T>> for Bytes {
fn from(m: Message<T>) -> Self {
m.bytes.into_raw()
}
}

View File

@ -7,7 +7,7 @@ pub struct GetAddr;
impl Payload for GetAddr { impl Payload for GetAddr {
fn version() -> u32 { fn version() -> u32 {
60002 0
} }
fn command() -> &'static str { fn command() -> &'static str {

View File

@ -6,10 +6,6 @@ use net::Config as NetConfig;
pub struct Config { pub struct Config {
/// Number of threads used by p2p thread pool. /// Number of threads used by p2p thread pool.
pub threads: usize, pub threads: usize,
/// Lowest supported protocol version.
pub protocol_minimum: u32,
/// Highest supported protocol version.
pub protocol_maximum: u32,
/// Number of inbound connections. /// Number of inbound connections.
pub inbound_connections: u32, pub inbound_connections: u32,
/// Number of outbound connections. /// Number of outbound connections.
@ -20,6 +16,6 @@ pub struct Config {
pub peers: Vec<SocketAddr>, pub peers: Vec<SocketAddr>,
/// Connect to these nodes to retrieve peer addresses, and disconnect. /// Connect to these nodes to retrieve peer addresses, and disconnect.
pub seeds: Vec<String>, pub seeds: Vec<String>,
/// p2p module cache directory. /// p2p/nodes.csv file path
pub node_table_path: PathBuf, pub node_table_path: PathBuf,
} }

View File

@ -1,19 +1,20 @@
use std::{io, cmp}; use std::{io, cmp};
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use message::{Message, MessageResult}; use message::{Message, MessageResult, Error};
use message::types::{Version, Verack}; use message::types::{Version, Verack};
use message::common::Magic; use message::common::Magic;
use io::{write_message, WriteMessage, ReadMessage, read_message}; use io::{write_message, WriteMessage, ReadMessage, read_message};
pub fn handshake<A>(a: A, magic: Magic, version: Version) -> Handshake<A> where A: io::Write + io::Read { pub fn handshake<A>(a: A, magic: Magic, version: Version, min_version: u32) -> Handshake<A> where A: io::Write + io::Read {
Handshake { Handshake {
version: version.version(), version: version.version(),
state: HandshakeState::SendVersion(write_message(a, version_message(magic, version))), state: HandshakeState::SendVersion(write_message(a, version_message(magic, version))),
magic: magic, magic: magic,
min_version: min_version,
} }
} }
pub fn accept_handshake<A>(a: A, magic: Magic, version: Version) -> AcceptHandshake<A> where A: io::Write + io::Read { pub fn accept_handshake<A>(a: A, magic: Magic, version: Version, min_version: u32) -> AcceptHandshake<A> where A: io::Write + io::Read {
AcceptHandshake { AcceptHandshake {
version: version.version(), version: version.version(),
state: AcceptHandshakeState::ReceiveVersion { state: AcceptHandshakeState::ReceiveVersion {
@ -21,6 +22,7 @@ pub fn accept_handshake<A>(a: A, magic: Magic, version: Version) -> AcceptHandsh
future: read_message(a, magic, 0), future: read_message(a, magic, 0),
}, },
magic: magic, magic: magic,
min_version: min_version,
} }
} }
@ -72,12 +74,14 @@ pub struct Handshake<A> {
state: HandshakeState<A>, state: HandshakeState<A>,
magic: Magic, magic: Magic,
version: u32, version: u32,
min_version: u32,
} }
pub struct AcceptHandshake<A> { pub struct AcceptHandshake<A> {
state: AcceptHandshakeState<A>, state: AcceptHandshakeState<A>,
magic: Magic, magic: Magic,
version: u32, version: u32,
min_version: u32,
} }
impl<A> Future for Handshake<A> where A: io::Read + io::Write { impl<A> Future for Handshake<A> where A: io::Read + io::Write {
@ -97,6 +101,10 @@ impl<A> Future for Handshake<A> where A: io::Read + io::Write {
Err(err) => return Ok((stream, Err(err.into())).into()), Err(err) => return Ok((stream, Err(err.into())).into()),
}; };
if version.version() < self.min_version {
return Ok((stream, Err(Error::InvalidVersion)).into());
}
let next = HandshakeState::ReceiveVerack { let next = HandshakeState::ReceiveVerack {
version: Some(version), version: Some(version),
future: read_message(stream, self.magic, 0), future: read_message(stream, self.magic, 0),
@ -140,6 +148,10 @@ impl<A> Future for AcceptHandshake<A> where A: io::Read + io::Write {
Err(err) => return Ok((stream, Err(err.into())).into()), Err(err) => return Ok((stream, Err(err.into())).into()),
}; };
if version.version() < self.min_version {
return Ok((stream, Err(Error::InvalidVersion)).into());
}
let local_version = local_version.take().expect("local version must be set"); let local_version = local_version.take().expect("local version must be set");
let next = AcceptHandshakeState::SendVersion { let next = AcceptHandshakeState::SendVersion {
version: Some(version), version: Some(version),
@ -276,7 +288,7 @@ mod tests {
write: Bytes::default(), write: Bytes::default(),
}; };
let hs = handshake(test_io, magic, local_version).wait().unwrap(); let hs = handshake(test_io, magic, local_version, 0).wait().unwrap();
assert_eq!(hs.0.write, expected_stream.out()); assert_eq!(hs.0.write, expected_stream.out());
assert_eq!(hs.1.unwrap(), expected); assert_eq!(hs.1.unwrap(), expected);
} }
@ -305,7 +317,7 @@ mod tests {
expected_stream.append_slice(Message::new(magic, version, &local_version).unwrap().as_ref()); expected_stream.append_slice(Message::new(magic, version, &local_version).unwrap().as_ref());
expected_stream.append_slice(Message::new(magic, version, &Verack).unwrap().as_ref()); expected_stream.append_slice(Message::new(magic, version, &Verack).unwrap().as_ref());
let hs = accept_handshake(test_io, magic, local_version).wait().unwrap(); let hs = accept_handshake(test_io, magic, local_version, 0).wait().unwrap();
assert_eq!(hs.0.write, expected_stream.out()); assert_eq!(hs.0.write, expected_stream.out());
assert_eq!(hs.1.unwrap(), expected); assert_eq!(hs.1.unwrap(), expected);
} }

View File

@ -25,9 +25,6 @@ mod config;
mod event_loop; mod event_loop;
mod p2p; mod p2p;
pub const VERSION: u32 = 70_014;
pub const USER_AGENT: &'static str = "pbtc";
pub use primitives::{hash, bytes}; pub use primitives::{hash, bytes};
pub use config::Config; pub use config::Config;

View File

@ -9,7 +9,7 @@ use net::{Config, Connection};
pub fn accept_connection(stream: TcpStream, handle: &Handle, config: &Config, address: net::SocketAddr) -> Deadline<AcceptConnection> { pub fn accept_connection(stream: TcpStream, handle: &Handle, config: &Config, address: net::SocketAddr) -> Deadline<AcceptConnection> {
let accept = AcceptConnection { let accept = AcceptConnection {
handshake: accept_handshake(stream, config.magic, config.version(&address)), handshake: accept_handshake(stream, config.magic, config.version(&address), config.protocol_minimum),
magic: config.magic, magic: config.magic,
address: address, address: address,
}; };

View File

@ -1,6 +1,6 @@
use message::{Payload, Message}; use tokio_core::io::{write_all, WriteAll};
use session::Session; use session::Session;
use io::{SharedTcpStream, WriteMessage, write_message, read_any_message, ReadAnyMessage}; use io::{SharedTcpStream, read_any_message, ReadAnyMessage};
use util::PeerInfo; use util::PeerInfo;
pub struct Channel { pub struct Channel {
@ -18,10 +18,8 @@ impl Channel {
} }
} }
pub fn write_message<T>(&self, payload: &T) -> WriteMessage<T, SharedTcpStream> where T: Payload { pub fn write_message<T>(&self, message: T) -> WriteAll<SharedTcpStream, T> where T: AsRef<[u8]> {
// TODO: some tracing here write_all(self.stream.clone(), message)
let message = Message::new(self.peer_info.magic, self.peer_info.version, payload).expect("failed to create outgoing message");
write_message(self.stream.clone(), message)
} }
pub fn read_message(&self) -> ReadAnyMessage<SharedTcpStream> { pub fn read_message(&self) -> ReadAnyMessage<SharedTcpStream> {

View File

@ -3,10 +3,11 @@ use message::common::{Magic, Services, NetAddress};
use message::types::version::{Version, V0, V106, V70001}; use message::types::version::{Version, V0, V106, V70001};
use util::time::{Time, RealTime}; use util::time::{Time, RealTime};
use util::nonce::{NonceGenerator, RandomNonce}; use util::nonce::{NonceGenerator, RandomNonce};
use VERSION;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Config { pub struct Config {
pub protocol_version: u32,
pub protocol_minimum: u32,
pub magic: Magic, pub magic: Magic,
pub local_address: SocketAddr, pub local_address: SocketAddr,
pub services: Services, pub services: Services,
@ -18,7 +19,7 @@ pub struct Config {
impl Config { impl Config {
pub fn version(&self, to: &SocketAddr) -> Version { pub fn version(&self, to: &SocketAddr) -> Version {
Version::V70001(V0 { Version::V70001(V0 {
version: VERSION, version: self.protocol_version,
services: self.services, services: self.services,
timestamp: RealTime.get().sec, timestamp: RealTime.get().sec,
receiver: NetAddress { receiver: NetAddress {

View File

@ -18,6 +18,7 @@ pub fn connect(address: &SocketAddr, handle: &Handle, config: &Config) -> Deadli
}, },
magic: config.magic, magic: config.magic,
address: *address, address: *address,
protocol_minimum: config.protocol_minimum,
}; };
deadline(Duration::new(5, 0), handle, connect).expect("Failed to create timeout") deadline(Duration::new(5, 0), handle, connect).expect("Failed to create timeout")
@ -36,6 +37,7 @@ pub struct Connect {
state: ConnectState, state: ConnectState,
magic: Magic, magic: Magic,
address: SocketAddr, address: SocketAddr,
protocol_minimum: u32,
} }
impl Future for Connect { impl Future for Connect {
@ -47,7 +49,7 @@ impl Future for Connect {
ConnectState::TcpConnect { ref mut future, ref mut version } => { ConnectState::TcpConnect { ref mut future, ref mut version } => {
let stream = try_ready!(future.poll()); let stream = try_ready!(future.poll());
let version = version.take().expect("state TcpConnect must have version"); let version = version.take().expect("state TcpConnect must have version");
let handshake = handshake(stream, self.magic, version); let handshake = handshake(stream, self.magic, version, self.protocol_minimum);
(ConnectState::Handshake(handshake), Async::NotReady) (ConnectState::Handshake(handshake), Async::NotReady)
}, },
ConnectState::Handshake(ref mut future) => { ConnectState::Handshake(ref mut future) => {

View File

@ -9,6 +9,8 @@ use session::{SessionFactory};
use util::{Direction, PeerInfo}; use util::{Direction, PeerInfo};
use PeerId; use PeerId;
const SYNCHRONOUS_RESPONSES: bool = true;
#[derive(Default)] #[derive(Default)]
pub struct Connections { pub struct Connections {
/// Incremental peer counter. /// Incremental peer counter.
@ -51,7 +53,7 @@ impl Connections {
magic: connection.magic, magic: connection.magic,
}; };
let session = T::new_session(context, peer_info.clone()); let session = T::new_session(context, peer_info.clone(), SYNCHRONOUS_RESPONSES);
let channel = Arc::new(Channel::new(connection.stream, peer_info, session)); let channel = Arc::new(Channel::new(connection.stream, peer_info, session));
self.channels.write().insert(id, channel.clone()); self.channels.write().insert(id, channel.clone());
channel channel

View File

@ -5,6 +5,7 @@ mod connect;
mod connection; mod connection;
mod connection_counter; mod connection_counter;
mod connections; mod connections;
mod peer_context;
pub use self::accept_connection::{AcceptConnection, accept_connection}; pub use self::accept_connection::{AcceptConnection, accept_connection};
pub use self::channel::Channel; pub use self::channel::Channel;
@ -13,3 +14,4 @@ pub use self::connect::{Connect, connect};
pub use self::connection::Connection; pub use self::connection::Connection;
pub use self::connection_counter::ConnectionCounter; pub use self::connection_counter::ConnectionCounter;
pub use self::connections::Connections; pub use self::connections::Connections;
pub use self::peer_context::PeerContext;

114
p2p/src/net/peer_context.rs Normal file
View File

@ -0,0 +1,114 @@
use std::sync::Arc;
use parking_lot::Mutex;
use message::{Payload, Message};
use p2p::Context;
use util::{PeerInfo, ConfigurableSynchronizer, ResponseQueue, Synchronizer, Responses};
pub struct PeerContext {
context: Arc<Context>,
info: PeerInfo,
synchronizer: Mutex<ConfigurableSynchronizer>,
response_queue: Mutex<ResponseQueue>,
}
impl PeerContext {
pub fn new(context: Arc<Context>, info: PeerInfo, synchronous: bool) -> Self {
PeerContext {
context: context,
info: info,
synchronizer: Mutex::new(ConfigurableSynchronizer::new(synchronous)),
response_queue: Mutex::default(),
}
}
fn to_message<T>(&self, payload: &T) -> Message<T> where T: Payload {
Message::new(self.info.magic, self.info.version, payload).expect("failed to create outgoing message")
}
fn send_awaiting(&self, sync: &mut ConfigurableSynchronizer, queue: &mut ResponseQueue, start_id: u32) {
let mut next_id = start_id;
loop {
next_id = next_id.overflowing_add(1).0;
match queue.responses(next_id) {
Some(Responses::Finished(messages)) => {
assert!(sync.permission_for_response(next_id));
for message in messages {
let send = Context::send_message_to_peer(self.context.clone(), self.info.id, message);
self.context.spawn(send);
}
},
Some(Responses::Ignored) => {
assert!(sync.permission_for_response(next_id));
},
Some(Responses::Unfinished(messages)) => {
assert!(sync.is_permitted(next_id));
for message in messages {
let send = Context::send_message_to_peer(self.context.clone(), self.info.id, message);
self.context.spawn(send);
}
break;
},
None => {
break;
}
}
}
}
/// Request is always automatically send.
pub fn send_request<T>(&self, payload: &T) where T: Payload {
let send = Context::send_to_peer(self.context.clone(), self.info.id, payload);
self.context.spawn(send);
}
pub fn declare_response(&self) -> u32 {
let d = self.synchronizer.lock().declare_response();
trace!("declared response: {}", d);
d
}
pub fn send_response_inline<T>(&self, payload: &T) where T: Payload {
let id = self.declare_response();
self.send_response(payload, id, true);
}
/// Do not wait for response with given id.
pub fn ignore_response(&self, id: u32) {
let mut sync = self.synchronizer.lock();
let mut queue = self.response_queue.lock();
if sync.permission_for_response(id) {
self.send_awaiting(&mut sync, &mut queue, id);
} else {
queue.push_ignored_response(id);
}
}
/// Responses are sent in order defined by synchronizer.
pub fn send_response<T>(&self, payload: &T, id: u32, is_final: bool) where T: Payload {
trace!("response ready: {}, id: {}, final: {}", T::command(), id, is_final);
let mut sync = self.synchronizer.lock();
let mut queue = self.response_queue.lock();
if is_final {
if sync.permission_for_response(id) {
let send = Context::send_to_peer(self.context.clone(), self.info.id, payload);
self.context.spawn(send);
self.send_awaiting(&mut sync, &mut queue, id);
} else {
queue.push_finished_response(id, self.to_message(payload).into());
}
} else if sync.is_permitted(id) {
let send = Context::send_to_peer(self.context.clone(), self.info.id, payload);
self.context.spawn(send);
} else {
queue.push_unfinished_response(id, self.to_message(payload).into());
}
}
pub fn info(&self) -> &PeerInfo {
&self.info
}
pub fn global(&self) -> &Arc<Context> {
&self.context
}
}

View File

@ -9,7 +9,7 @@ use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::{Handle, Remote, Timeout, Interval}; use tokio_core::reactor::{Handle, Remote, Timeout, Interval};
use abstract_ns::Resolver; use abstract_ns::Resolver;
use ns_dns_tokio::DnsResolver; use ns_dns_tokio::DnsResolver;
use message::{Payload, MessageResult}; use message::{Payload, MessageResult, Message};
use message::common::Services; use message::common::Services;
use net::{connect, Connections, Channel, Config as NetConfig, accept_connection, ConnectionCounter}; use net::{connect, Connections, Channel, Config as NetConfig, accept_connection, ConnectionCounter};
use util::{NodeTable, Node, Direction}; use util::{NodeTable, Node, Direction};
@ -293,7 +293,22 @@ impl Context {
/// Send message to a channel with given peer id. /// Send message to a channel with given peer id.
pub fn send_to_peer<T>(context: Arc<Context>, peer: PeerId, payload: &T) -> IoFuture<()> where T: Payload { pub fn send_to_peer<T>(context: Arc<Context>, peer: PeerId, payload: &T) -> IoFuture<()> where T: Payload {
match context.connections.channel(peer) { match context.connections.channel(peer) {
Some(channel) => Context::send(context, channel, payload), Some(channel) => {
let info = channel.peer_info();
let message = Message::new(info.magic, info.version, payload).expect("failed to create outgoing message");
Context::send(context, channel, message)
},
None => {
// peer no longer exists.
// TODO: should we return error here?
finished(()).boxed()
}
}
}
pub fn send_message_to_peer<T>(context: Arc<Context>, peer: PeerId, message: T) -> IoFuture<()> where T: AsRef<[u8]> + Send + 'static {
match context.connections.channel(peer) {
Some(channel) => Context::send(context, channel, message),
None => { None => {
// peer no longer exists. // peer no longer exists.
// TODO: should we return error here? // TODO: should we return error here?
@ -303,13 +318,13 @@ impl Context {
} }
/// Send message using given channel. /// Send message using given channel.
pub fn send<T>(_context: Arc<Context>, channel: Arc<Channel>, payload: &T) -> IoFuture<()> where T: Payload { pub fn send<T>(_context: Arc<Context>, channel: Arc<Channel>, message: T) -> IoFuture<()> where T: AsRef<[u8]> + Send + 'static {
trace!("Sending {} message to {}", T::command(), channel.peer_info().address); //trace!("Sending {} message to {}", T::command(), channel.peer_info().address);
channel.write_message(payload).then(move |result| { channel.write_message(message).then(move |result| {
match result { match result {
Ok(_) => { Ok(_) => {
// successful send // successful send
trace!("Sent {} message to {}", T::command(), channel.peer_info().address); //trace!("Sent {} message to {}", T::command(), channel.peer_info().address);
finished(()).boxed() finished(()).boxed()
}, },
Err(err) => { Err(err) => {

View File

@ -4,30 +4,26 @@ use bytes::Bytes;
use message::{Error, Command, deserialize_payload, Payload}; use message::{Error, Command, deserialize_payload, Payload};
use message::types::{GetAddr, Addr}; use message::types::{GetAddr, Addr};
use protocol::Protocol; use protocol::Protocol;
use p2p::Context; use net::PeerContext;
use util::{Direction, PeerInfo}; use util::Direction;
pub struct AddrProtocol { pub struct AddrProtocol {
/// Context /// Context
context: Arc<Context>, context: Arc<PeerContext>,
/// Connected peer info.
info: PeerInfo,
} }
impl AddrProtocol { impl AddrProtocol {
pub fn new(context: Arc<Context>, info: PeerInfo) -> Self { pub fn new(context: Arc<PeerContext>) -> Self {
AddrProtocol { AddrProtocol {
context: context, context: context,
info: info,
} }
} }
} }
impl Protocol for AddrProtocol { impl Protocol for AddrProtocol {
fn initialize(&mut self) { fn initialize(&mut self) {
if let Direction::Outbound = self.info.direction { if let Direction::Outbound = self.context.info().direction {
let send = Context::send_to_peer(self.context.clone(), self.info.id, &GetAddr); self.context.send_request(&GetAddr);
self.context.spawn(send);
} }
} }
@ -35,20 +31,19 @@ impl Protocol for AddrProtocol {
// normal nodes send addr message only after they receive getaddr message // normal nodes send addr message only after they receive getaddr message
// meanwhile seednodes, surprisingly, send addr message even before they are asked for it // meanwhile seednodes, surprisingly, send addr message even before they are asked for it
if command == &GetAddr::command() { if command == &GetAddr::command() {
let _: GetAddr = try!(deserialize_payload(payload, self.info.version)); let _: GetAddr = try!(deserialize_payload(payload, self.context.info().version));
let entries = self.context.node_table_entries().into_iter().map(Into::into).collect(); let entries = self.context.global().node_table_entries().into_iter().map(Into::into).collect();
let addr = Addr::new(entries); let addr = Addr::new(entries);
let send = Context::send_to_peer(self.context.clone(), self.info.id, &addr); self.context.send_response_inline(&addr);
self.context.spawn(send);
} else if command == &Addr::command() { } else if command == &Addr::command() {
let addr: Addr = try!(deserialize_payload(payload, self.info.version)); let addr: Addr = try!(deserialize_payload(payload, self.context.info().version));
match addr { match addr {
Addr::V0(_) => { Addr::V0(_) => {
unreachable!("This version of protocol is not supported!"); unreachable!("This version of protocol is not supported!");
}, },
Addr::V31402(addr) => { Addr::V31402(addr) => {
let nodes = addr.addresses.into_iter().map(Into::into).collect(); let nodes = addr.addresses.into_iter().map(Into::into).collect();
self.context.update_node_table(nodes); self.context.global().update_node_table(nodes);
}, },
} }
} }
@ -58,18 +53,15 @@ impl Protocol for AddrProtocol {
pub struct SeednodeProtocol { pub struct SeednodeProtocol {
/// Context /// Context
context: Arc<Context>, context: Arc<PeerContext>,
/// Connected peer info,
info: PeerInfo,
/// Indicates if disconnecting has been scheduled. /// Indicates if disconnecting has been scheduled.
disconnecting: bool, disconnecting: bool,
} }
impl SeednodeProtocol { impl SeednodeProtocol {
pub fn new(context: Arc<Context>, info: PeerInfo) -> Self { pub fn new(context: Arc<PeerContext>) -> Self {
SeednodeProtocol { SeednodeProtocol {
context: context, context: context,
info: info,
disconnecting: false, disconnecting: false,
} }
} }
@ -81,9 +73,9 @@ impl Protocol for SeednodeProtocol {
// We can't disconenct after first read. Let's delay it by 60 seconds. // We can't disconenct after first read. Let's delay it by 60 seconds.
if !self.disconnecting && command == &Addr::command() { if !self.disconnecting && command == &Addr::command() {
self.disconnecting = true; self.disconnecting = true;
let context = self.context.clone(); let context = self.context.global().clone();
let peer = self.info.id; let peer = self.context.info().id;
self.context.execute_after(Duration::new(60, 0), move || { self.context.global().execute_after(Duration::new(60, 0), move || {
context.close_channel(peer); context.close_channel(peer);
}); });
} }

View File

@ -4,26 +4,12 @@ use message::{Error, Payload, deserialize_payload};
use message::types::{Ping, Pong}; use message::types::{Ping, Pong};
use message::common::Command; use message::common::Command;
use protocol::Protocol; use protocol::Protocol;
use util::{PeerId, PeerInfo}; use net::PeerContext;
use util::nonce::{NonceGenerator, RandomNonce}; use util::nonce::{NonceGenerator, RandomNonce};
use p2p::Context;
pub trait PingContext: Send + Sync { pub struct PingProtocol<T = RandomNonce, C = PeerContext> {
fn send_to_peer<T>(context: Arc<Self>, peer: PeerId, payload: &T) where Self: Sized, T: Payload;
}
impl PingContext for Context {
fn send_to_peer<T>(context: Arc<Self>, peer: PeerId, payload: &T) where T: Payload {
let send = Context::send_to_peer(context.clone(), peer, payload);
context.spawn(send);
}
}
pub struct PingProtocol<T = RandomNonce, C = Context> {
/// Context /// Context
context: Arc<C>, context: Arc<C>,
/// Connected peer info.
info: PeerInfo,
/// Nonce generator. /// Nonce generator.
nonce_generator: T, nonce_generator: T,
/// Last nonce sent in the ping message. /// Last nonce sent in the ping message.
@ -31,32 +17,31 @@ pub struct PingProtocol<T = RandomNonce, C = Context> {
} }
impl PingProtocol { impl PingProtocol {
pub fn new(context: Arc<Context>, info: PeerInfo) -> Self { pub fn new(context: Arc<PeerContext>) -> Self {
PingProtocol { PingProtocol {
context: context, context: context,
info: info,
nonce_generator: RandomNonce::default(), nonce_generator: RandomNonce::default(),
last_ping_nonce: None, last_ping_nonce: None,
} }
} }
} }
impl<T, C> Protocol for PingProtocol<T, C> where T: NonceGenerator + Send, C: PingContext { impl Protocol for PingProtocol {
fn initialize(&mut self) { fn initialize(&mut self) {
// bitcoind always sends ping, let's do the same // bitcoind always sends ping, let's do the same
let nonce = self.nonce_generator.get(); let nonce = self.nonce_generator.get();
self.last_ping_nonce = Some(nonce); self.last_ping_nonce = Some(nonce);
let ping = Ping::new(nonce); let ping = Ping::new(nonce);
PingContext::send_to_peer(self.context.clone(), self.info.id, &ping); self.context.send_request(&ping);
} }
fn on_message(&mut self, command: &Command, payload: &Bytes) -> Result<(), Error> { fn on_message(&mut self, command: &Command, payload: &Bytes) -> Result<(), Error> {
if command == &Ping::command() { if command == &Ping::command() {
let ping: Ping = try!(deserialize_payload(payload, self.info.version)); let ping: Ping = try!(deserialize_payload(payload, self.context.info().version));
let pong = Pong::new(ping.nonce); let pong = Pong::new(ping.nonce);
PingContext::send_to_peer(self.context.clone(), self.info.id, &pong); self.context.send_response_inline(&pong);
} else if command == &Pong::command() { } else if command == &Pong::command() {
let pong: Pong = try!(deserialize_payload(payload, self.info.version)); let pong: Pong = try!(deserialize_payload(payload, self.context.info().version));
if Some(pong.nonce) != self.last_ping_nonce.take() { if Some(pong.nonce) != self.last_ping_nonce.take() {
return Err(Error::InvalidCommand) return Err(Error::InvalidCommand)
} }
@ -65,111 +50,3 @@ impl<T, C> Protocol for PingProtocol<T, C> where T: NonceGenerator + Send, C: Pi
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod tests {
use std::sync::Arc;
use parking_lot::Mutex;
use bytes::Bytes;
use message::{Payload, serialize_payload, Magic};
use message::types::{Ping, Pong};
use util::{PeerId, PeerInfo, Direction};
use util::nonce::StaticNonce;
use protocol::Protocol;
use super::{PingProtocol, PingContext};
#[derive(Default)]
struct TestPingContext {
version: u32,
messages: Mutex<Vec<(PeerId, Bytes)>>,
}
impl PingContext for TestPingContext {
fn send_to_peer<T>(context: Arc<Self>, peer: PeerId, payload: &T) where T: Payload {
let value = (peer, serialize_payload(payload, context.version).unwrap());
context.messages.lock().push(value);
}
}
#[test]
fn test_ping_init() {
let ping_context = Arc::new(TestPingContext::default());
let peer = 99;
let nonce = 1000;
let expected_message = serialize_payload(&Ping::new(nonce), 0).unwrap();
let mut ping_protocol = PingProtocol {
context: ping_context.clone(),
info: PeerInfo {
id: peer,
address: "0.0.0.0:8080".parse().unwrap(),
direction: Direction::Inbound,
version: 0,
magic: Magic::Testnet,
},
nonce_generator: StaticNonce::new(nonce),
last_ping_nonce: None,
};
ping_protocol.initialize();
let messages: Vec<(PeerId, Bytes)> = ping_context.messages.lock().clone();
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].0, peer);
assert_eq!(messages[0].1, expected_message);
assert_eq!(ping_protocol.last_ping_nonce, Some(nonce));
}
#[test]
fn test_ping_on_message_ping() {
let ping_context = Arc::new(TestPingContext::default());
let peer = 99;
let nonce = 1000;
let command = "ping".into();
let message = serialize_payload(&Ping::new(nonce), 0).unwrap();
let expected_message = serialize_payload(&Pong::new(nonce), 0).unwrap();
let mut ping_protocol = PingProtocol {
context: ping_context.clone(),
info: PeerInfo {
id: peer,
address: "0.0.0.0:8080".parse().unwrap(),
direction: Direction::Inbound,
version: 0,
magic: Magic::Testnet,
},
nonce_generator: StaticNonce::new(nonce),
last_ping_nonce: None,
};
assert!(ping_protocol.on_message(&command, &message).is_ok());
let messages: Vec<(PeerId, Bytes)> = ping_context.messages.lock().clone();
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].0, peer);
assert_eq!(messages[0].1, expected_message);
assert_eq!(ping_protocol.last_ping_nonce, None);
}
#[test]
fn test_ping_on_message_pong() {
let ping_context = Arc::new(TestPingContext::default());
let peer = 99;
let nonce = 1000;
let command = "pong".into();
let message = serialize_payload(&Pong::new(nonce), 0).unwrap();
let mut ping_protocol = PingProtocol {
context: ping_context.clone(),
info: PeerInfo {
id: peer,
address: "0.0.0.0:8080".parse().unwrap(),
direction: Direction::Inbound,
version: 0,
magic: Magic::Testnet,
},
nonce_generator: StaticNonce::new(nonce),
last_ping_nonce: Some(nonce),
};
assert!(ping_protocol.on_message(&command, &message).is_ok());
let messages: Vec<(PeerId, Bytes)> = ping_context.messages.lock().clone();
assert_eq!(messages.len(), 0);
assert_eq!(ping_protocol.last_ping_nonce, None);
}
}

View File

@ -2,8 +2,7 @@ use std::sync::Arc;
use bytes::Bytes; use bytes::Bytes;
use message::{Command, Error, Payload, types, deserialize_payload}; use message::{Command, Error, Payload, types, deserialize_payload};
use protocol::Protocol; use protocol::Protocol;
use util::{PeerInfo, PeerId}; use net::PeerContext;
use p2p::Context;
pub type InboundSyncConnectionRef = Box<InboundSyncConnection>; pub type InboundSyncConnectionRef = Box<InboundSyncConnection>;
pub type OutboundSyncConnectionRef = Box<OutboundSyncConnection>; pub type OutboundSyncConnectionRef = Box<OutboundSyncConnection>;
@ -19,13 +18,13 @@ pub trait InboundSyncConnection : Send + Sync {
fn start_sync_session(&self, version: u32); fn start_sync_session(&self, version: u32);
fn close_session(&self); fn close_session(&self);
fn on_inventory(&self, message: types::Inv); fn on_inventory(&self, message: types::Inv);
fn on_getdata(&self, message: types::GetData); fn on_getdata(&self, message: types::GetData, id: u32);
fn on_getblocks(&self, message: types::GetBlocks); fn on_getblocks(&self, message: types::GetBlocks, id: u32);
fn on_getheaders(&self, message: types::GetHeaders); fn on_getheaders(&self, message: types::GetHeaders, id: u32);
fn on_transaction(&self, message: types::Tx); fn on_transaction(&self, message: types::Tx);
fn on_block(&self, message: types::Block); fn on_block(&self, message: types::Block);
fn on_headers(&self, message: types::Headers); fn on_headers(&self, message: types::Headers);
fn on_mempool(&self, message: types::MemPool); fn on_mempool(&self, message: types::MemPool, id: u32);
fn on_filterload(&self, message: types::FilterLoad); fn on_filterload(&self, message: types::FilterLoad);
fn on_filteradd(&self, message: types::FilterAdd); fn on_filteradd(&self, message: types::FilterAdd);
fn on_filterclear(&self, message: types::FilterClear); fn on_filterclear(&self, message: types::FilterClear);
@ -40,14 +39,14 @@ pub trait InboundSyncConnection : Send + Sync {
} }
pub trait OutboundSyncConnection : Send + Sync { pub trait OutboundSyncConnection : Send + Sync {
fn send_inventory(&self, message: &types::Inv); fn send_inventory(&self, message: &types::Inv, id: u32, is_final: bool);
fn send_getdata(&self, message: &types::GetData); fn send_getdata(&self, message: &types::GetData);
fn send_getblocks(&self, message: &types::GetBlocks); fn send_getblocks(&self, message: &types::GetBlocks);
fn send_getheaders(&self, message: &types::GetHeaders); fn send_getheaders(&self, message: &types::GetHeaders);
fn send_transaction(&self, message: &types::Tx); fn send_transaction(&self, message: &types::Tx);
fn send_block(&self, message: &types::Block); fn send_block(&self, message: &types::Block, id: u32, is_final: bool);
fn send_headers(&self, message: &types::Headers); fn send_headers(&self, message: &types::Headers, id: u32, is_final: bool);
fn send_mempool(&self, message: &types::MemPool); fn send_mempool(&self, message: &types::MemPool, id: u32, is_final: bool);
fn send_filterload(&self, message: &types::FilterLoad); fn send_filterload(&self, message: &types::FilterLoad);
fn send_filteradd(&self, message: &types::FilterAdd); fn send_filteradd(&self, message: &types::FilterAdd);
fn send_filterclear(&self, message: &types::FilterClear); fn send_filterclear(&self, message: &types::FilterClear);
@ -58,206 +57,213 @@ pub trait OutboundSyncConnection : Send + Sync {
fn send_compact_block(&self, message: &types::CompactBlock); fn send_compact_block(&self, message: &types::CompactBlock);
fn send_get_block_txn(&self, message: &types::GetBlockTxn); fn send_get_block_txn(&self, message: &types::GetBlockTxn);
fn send_block_txn(&self, message: &types::BlockTxn); fn send_block_txn(&self, message: &types::BlockTxn);
fn send_notfound(&self, message: &types::NotFound); fn send_notfound(&self, message: &types::NotFound, id: u32, is_final: bool);
fn ignored(&self, id: u32);
} }
struct OutboundSync { struct OutboundSync {
context: Arc<Context>, context: Arc<PeerContext>,
peer: PeerId,
} }
impl OutboundSync { impl OutboundSync {
pub fn new(context: Arc<Context>, peer: PeerId) -> OutboundSync { pub fn new(context: Arc<PeerContext>) -> OutboundSync {
OutboundSync { OutboundSync {
context: context, context: context,
peer: peer,
} }
} }
pub fn send_message<T>(&self, message: &T) where T: Payload {
let send = Context::send_to_peer(self.context.clone(), self.peer, message);
self.context.spawn(send);
}
pub fn boxed(self) -> Box<OutboundSyncConnection> { pub fn boxed(self) -> Box<OutboundSyncConnection> {
Box::new(self) Box::new(self)
} }
} }
impl OutboundSyncConnection for OutboundSync { impl OutboundSyncConnection for OutboundSync {
fn send_inventory(&self, message: &types::Inv) { fn send_inventory(&self, message: &types::Inv, id: u32, is_final: bool) {
self.send_message(message); self.context.send_response(message, id, is_final);
} }
fn send_getdata(&self, message: &types::GetData) { fn send_getdata(&self, message: &types::GetData) {
self.send_message(message); self.context.send_request(message);
} }
fn send_getblocks(&self, message: &types::GetBlocks) { fn send_getblocks(&self, message: &types::GetBlocks) {
self.send_message(message); self.context.send_request(message);
} }
fn send_getheaders(&self, message: &types::GetHeaders) { fn send_getheaders(&self, message: &types::GetHeaders) {
self.send_message(message); self.context.send_request(message);
} }
fn send_transaction(&self, message: &types::Tx) { fn send_transaction(&self, message: &types::Tx) {
self.send_message(message); self.context.send_request(message);
} }
fn send_block(&self, message: &types::Block) { fn send_block(&self, message: &types::Block, id: u32, is_final: bool) {
self.send_message(message); self.context.send_response(message, id, is_final);
} }
fn send_headers(&self, message: &types::Headers) { fn send_headers(&self, message: &types::Headers, id: u32, is_final: bool) {
self.send_message(message); self.context.send_response(message, id, is_final);
} }
fn send_mempool(&self, message: &types::MemPool) { fn send_mempool(&self, message: &types::MemPool, id: u32, is_final: bool) {
self.send_message(message); self.context.send_response(message, id, is_final);
} }
fn send_filterload(&self, message: &types::FilterLoad) { fn send_filterload(&self, message: &types::FilterLoad) {
self.send_message(message); self.context.send_request(message);
} }
fn send_filteradd(&self, message: &types::FilterAdd) { fn send_filteradd(&self, message: &types::FilterAdd) {
self.send_message(message); self.context.send_request(message);
} }
fn send_filterclear(&self, message: &types::FilterClear) { fn send_filterclear(&self, message: &types::FilterClear) {
self.send_message(message); self.context.send_request(message);
} }
fn send_merkleblock(&self, message: &types::MerkleBlock) { fn send_merkleblock(&self, message: &types::MerkleBlock) {
self.send_message(message); self.context.send_request(message);
} }
fn send_sendheaders(&self, message: &types::SendHeaders) { fn send_sendheaders(&self, message: &types::SendHeaders) {
self.send_message(message); self.context.send_request(message);
} }
fn send_feefilter(&self, message: &types::FeeFilter) { fn send_feefilter(&self, message: &types::FeeFilter) {
self.send_message(message); self.context.send_request(message);
} }
fn send_send_compact(&self, message: &types::SendCompact) { fn send_send_compact(&self, message: &types::SendCompact) {
self.send_message(message); self.context.send_request(message);
} }
fn send_compact_block(&self, message: &types::CompactBlock) { fn send_compact_block(&self, message: &types::CompactBlock) {
self.send_message(message); self.context.send_request(message);
} }
fn send_get_block_txn(&self, message: &types::GetBlockTxn) { fn send_get_block_txn(&self, message: &types::GetBlockTxn) {
self.send_message(message); self.context.send_request(message);
} }
fn send_block_txn(&self, message: &types::BlockTxn) { fn send_block_txn(&self, message: &types::BlockTxn) {
self.send_message(message); self.context.send_request(message);
} }
fn send_notfound(&self, message: &types::NotFound) { fn send_notfound(&self, message: &types::NotFound, id: u32, is_final: bool) {
self.send_message(message); self.context.send_response(message, id, is_final);
}
fn ignored(&self, id: u32) {
self.context.ignore_response(id);
} }
} }
pub struct SyncProtocol { pub struct SyncProtocol {
inbound_connection: InboundSyncConnectionRef, inbound_connection: InboundSyncConnectionRef,
info: PeerInfo, context: Arc<PeerContext>,
} }
impl SyncProtocol { impl SyncProtocol {
pub fn new(context: Arc<Context>, info: PeerInfo) -> Self { pub fn new(context: Arc<PeerContext>) -> Self {
let outbound_connection = OutboundSync::new(context.clone(), info.id).boxed(); let outbound_connection = OutboundSync::new(context.clone()).boxed();
let inbound_connection = context.create_sync_session(0, outbound_connection); let inbound_connection = context.global().create_sync_session(0, outbound_connection);
SyncProtocol { SyncProtocol {
inbound_connection: inbound_connection, inbound_connection: inbound_connection,
info: info, context: context,
} }
} }
} }
impl Protocol for SyncProtocol { impl Protocol for SyncProtocol {
fn initialize(&mut self) { fn initialize(&mut self) {
self.inbound_connection.start_sync_session(self.info.version); self.inbound_connection.start_sync_session(self.context.info().version);
} }
fn on_message(&mut self, command: &Command, payload: &Bytes) -> Result<(), Error> { fn on_message(&mut self, command: &Command, payload: &Bytes) -> Result<(), Error> {
let version = self.context.info().version;
if command == &types::Inv::command() { if command == &types::Inv::command() {
let message: types::Inv = try!(deserialize_payload(payload, self.info.version)); let message: types::Inv = try!(deserialize_payload(payload, version));
self.inbound_connection.on_inventory(message); self.inbound_connection.on_inventory(message);
} }
else if command == &types::GetData::command() { else if command == &types::GetData::command() {
let message: types::GetData = try!(deserialize_payload(payload, self.info.version)); let message: types::GetData = try!(deserialize_payload(payload, version));
self.inbound_connection.on_getdata(message); let id = self.context.declare_response();
trace!("declared response {} for request: {}", id, types::GetData::command());
self.inbound_connection.on_getdata(message, id);
} }
else if command == &types::GetBlocks::command() { else if command == &types::GetBlocks::command() {
let message: types::GetBlocks = try!(deserialize_payload(payload, self.info.version)); let message: types::GetBlocks = try!(deserialize_payload(payload, version));
self.inbound_connection.on_getblocks(message); let id = self.context.declare_response();
trace!("declared response {} for request: {}", id, types::GetBlocks::command());
self.inbound_connection.on_getblocks(message, id);
} }
else if command == &types::GetHeaders::command() { else if command == &types::GetHeaders::command() {
let message: types::GetHeaders = try!(deserialize_payload(payload, self.info.version)); let message: types::GetHeaders = try!(deserialize_payload(payload, version));
self.inbound_connection.on_getheaders(message); let id = self.context.declare_response();
trace!("declared response {} for request: {}", id, types::GetHeaders::command());
self.inbound_connection.on_getheaders(message, id);
} }
else if command == &types::Tx::command() { else if command == &types::Tx::command() {
let message: types::Tx = try!(deserialize_payload(payload, self.info.version)); let message: types::Tx = try!(deserialize_payload(payload, version));
self.inbound_connection.on_transaction(message); self.inbound_connection.on_transaction(message);
} }
else if command == &types::Block::command() { else if command == &types::Block::command() {
let message: types::Block = try!(deserialize_payload(payload, self.info.version)); let message: types::Block = try!(deserialize_payload(payload, version));
self.inbound_connection.on_block(message); self.inbound_connection.on_block(message);
} }
else if command == &types::MemPool::command() { else if command == &types::MemPool::command() {
let message: types::MemPool = try!(deserialize_payload(payload, self.info.version)); let message: types::MemPool = try!(deserialize_payload(payload, version));
self.inbound_connection.on_mempool(message); let id = self.context.declare_response();
trace!("declared response {} for request: {}", id, types::MemPool::command());
self.inbound_connection.on_mempool(message, id);
} }
else if command == &types::Headers::command() { else if command == &types::Headers::command() {
let message: types::Headers = try!(deserialize_payload(payload, self.info.version)); let message: types::Headers = try!(deserialize_payload(payload, version));
self.inbound_connection.on_headers(message); self.inbound_connection.on_headers(message);
} }
else if command == &types::FilterLoad::command() { else if command == &types::FilterLoad::command() {
let message: types::FilterLoad = try!(deserialize_payload(payload, self.info.version)); let message: types::FilterLoad = try!(deserialize_payload(payload, version));
self.inbound_connection.on_filterload(message); self.inbound_connection.on_filterload(message);
} }
else if command == &types::FilterAdd::command() { else if command == &types::FilterAdd::command() {
let message: types::FilterAdd = try!(deserialize_payload(payload, self.info.version)); let message: types::FilterAdd = try!(deserialize_payload(payload, version));
self.inbound_connection.on_filteradd(message); self.inbound_connection.on_filteradd(message);
} }
else if command == &types::FilterClear::command() { else if command == &types::FilterClear::command() {
let message: types::FilterClear = try!(deserialize_payload(payload, self.info.version)); let message: types::FilterClear = try!(deserialize_payload(payload, version));
self.inbound_connection.on_filterclear(message); self.inbound_connection.on_filterclear(message);
} }
else if command == &types::MerkleBlock::command() { else if command == &types::MerkleBlock::command() {
let message: types::MerkleBlock = try!(deserialize_payload(payload, self.info.version)); let message: types::MerkleBlock = try!(deserialize_payload(payload, version));
self.inbound_connection.on_merkleblock(message); self.inbound_connection.on_merkleblock(message);
} }
else if command == &types::SendHeaders::command() { else if command == &types::SendHeaders::command() {
let message: types::SendHeaders = try!(deserialize_payload(payload, self.info.version)); let message: types::SendHeaders = try!(deserialize_payload(payload, version));
self.inbound_connection.on_sendheaders(message); self.inbound_connection.on_sendheaders(message);
} }
else if command == &types::FeeFilter::command() { else if command == &types::FeeFilter::command() {
let message: types::FeeFilter = try!(deserialize_payload(payload, self.info.version)); let message: types::FeeFilter = try!(deserialize_payload(payload, version));
self.inbound_connection.on_feefilter(message); self.inbound_connection.on_feefilter(message);
} }
else if command == &types::SendCompact::command() { else if command == &types::SendCompact::command() {
let message: types::SendCompact = try!(deserialize_payload(payload, self.info.version)); let message: types::SendCompact = try!(deserialize_payload(payload, version));
self.inbound_connection.on_send_compact(message); self.inbound_connection.on_send_compact(message);
} }
else if command == &types::CompactBlock::command() { else if command == &types::CompactBlock::command() {
let message: types::CompactBlock = try!(deserialize_payload(payload, self.info.version)); let message: types::CompactBlock = try!(deserialize_payload(payload, version));
self.inbound_connection.on_compact_block(message); self.inbound_connection.on_compact_block(message);
} }
else if command == &types::GetBlockTxn::command() { else if command == &types::GetBlockTxn::command() {
let message: types::GetBlockTxn = try!(deserialize_payload(payload, self.info.version)); let message: types::GetBlockTxn = try!(deserialize_payload(payload, version));
self.inbound_connection.on_get_block_txn(message); self.inbound_connection.on_get_block_txn(message);
} }
else if command == &types::BlockTxn::command() { else if command == &types::BlockTxn::command() {
let message: types::BlockTxn = try!(deserialize_payload(payload, self.info.version)); let message: types::BlockTxn = try!(deserialize_payload(payload, version));
self.inbound_connection.on_block_txn(message); self.inbound_connection.on_block_txn(message);
} }
else if command == &types::NotFound::command() { else if command == &types::NotFound::command() {
let message: types::NotFound = try!(deserialize_payload(payload, self.info.version)); let message: types::NotFound = try!(deserialize_payload(payload, version));
self.inbound_connection.on_notfound(message); self.inbound_connection.on_notfound(message);
} }
Ok(()) Ok(())

View File

@ -3,42 +3,47 @@ use parking_lot::Mutex;
use bytes::Bytes; use bytes::Bytes;
use message::{Command, Error}; use message::{Command, Error};
use p2p::Context; use p2p::Context;
use net::PeerContext;
use protocol::{Protocol, PingProtocol, SyncProtocol, AddrProtocol, SeednodeProtocol}; use protocol::{Protocol, PingProtocol, SyncProtocol, AddrProtocol, SeednodeProtocol};
use util::{PeerInfo}; use util::PeerInfo;
pub trait SessionFactory { pub trait SessionFactory {
fn new_session(context: Arc<Context>, info: PeerInfo) -> Session; fn new_session(context: Arc<Context>, info: PeerInfo, synchronous: bool) -> Session;
} }
pub struct SeednodeSessionFactory; pub struct SeednodeSessionFactory;
impl SessionFactory for SeednodeSessionFactory { impl SessionFactory for SeednodeSessionFactory {
fn new_session(context: Arc<Context>, info: PeerInfo) -> Session { fn new_session(context: Arc<Context>, info: PeerInfo, synchronous: bool) -> Session {
let ping = PingProtocol::new(context.clone(), info.clone()).boxed(); let peer_context = Arc::new(PeerContext::new(context, info, synchronous));
let addr = AddrProtocol::new(context.clone(), info.clone()).boxed(); let ping = PingProtocol::new(peer_context.clone()).boxed();
let seed = SeednodeProtocol::new(context.clone(), info).boxed(); let addr = AddrProtocol::new(peer_context.clone()).boxed();
Session::new(vec![ping, addr, seed]) let seed = SeednodeProtocol::new(peer_context.clone()).boxed();
Session::new(peer_context, vec![ping, addr, seed])
} }
} }
pub struct NormalSessionFactory; pub struct NormalSessionFactory;
impl SessionFactory for NormalSessionFactory { impl SessionFactory for NormalSessionFactory {
fn new_session(context: Arc<Context>, info: PeerInfo) -> Session { fn new_session(context: Arc<Context>, info: PeerInfo, synchronous: bool) -> Session {
let ping = PingProtocol::new(context.clone(), info.clone()).boxed(); let peer_context = Arc::new(PeerContext::new(context, info, synchronous));
let addr = AddrProtocol::new(context.clone(), info.clone()).boxed(); let ping = PingProtocol::new(peer_context.clone()).boxed();
let sync = SyncProtocol::new(context, info).boxed(); let addr = AddrProtocol::new(peer_context.clone()).boxed();
Session::new(vec![ping, addr, sync]) let sync = SyncProtocol::new(peer_context.clone()).boxed();
Session::new(peer_context, vec![ping, addr, sync])
} }
} }
pub struct Session { pub struct Session {
_peer_context: Arc<PeerContext>,
protocols: Mutex<Vec<Box<Protocol>>>, protocols: Mutex<Vec<Box<Protocol>>>,
} }
impl Session { impl Session {
pub fn new(protocols: Vec<Box<Protocol>>) -> Self { pub fn new(peer_context: Arc<PeerContext>, protocols: Vec<Box<Protocol>>) -> Self {
Session { Session {
_peer_context: peer_context,
protocols: Mutex::new(protocols), protocols: Mutex::new(protocols),
} }
} }

View File

@ -2,8 +2,10 @@ pub mod nonce;
pub mod time; pub mod time;
mod node_table; mod node_table;
mod peer; mod peer;
mod response_queue;
mod synchronizer; mod synchronizer;
pub use self::node_table::{NodeTable, Node}; pub use self::node_table::{NodeTable, Node};
pub use self::peer::{PeerId, PeerInfo, Direction}; pub use self::peer::{PeerId, PeerInfo, Direction};
pub use self::response_queue::{ResponseQueue, Responses};
pub use self::synchronizer::{Synchronizer, ConfigurableSynchronizer}; pub use self::synchronizer::{Synchronizer, ConfigurableSynchronizer};

View File

@ -0,0 +1,45 @@
use std::collections::{HashMap, HashSet};
use bytes::Bytes;
/// Queue of out-of-order responses. Each peer has it's own queue.
#[derive(Debug, Default)]
pub struct ResponseQueue {
unfinished: HashMap<u32, Vec<Bytes>>,
finished: HashMap<u32, Vec<Bytes>>,
ignored: HashSet<u32>,
}
pub enum Responses {
Unfinished(Vec<Bytes>),
Finished(Vec<Bytes>),
Ignored,
}
impl ResponseQueue {
pub fn push_unfinished_response(&mut self, id: u32, response: Bytes) {
self.unfinished.entry(id).or_insert_with(Vec::new).push(response)
}
pub fn push_finished_response(&mut self, id: u32, response: Bytes) {
let mut responses = self.unfinished.remove(&id).unwrap_or_default();
responses.push(response);
let previous = self.finished.insert(id, responses);
assert!(previous.is_none(), "logic error; same finished response should never be pushed twice");
}
pub fn push_ignored_response(&mut self, id: u32) {
assert!(self.ignored.insert(id), "logic error; same response should never be ignored twice");
}
pub fn responses(&mut self, id: u32) -> Option<Responses> {
self.unfinished.remove(&id).map(Responses::Unfinished)
.or_else(|| self.finished.remove(&id).map(Responses::Finished))
.or_else(|| {
if self.ignored.remove(&id) {
Some(Responses::Ignored)
} else {
None
}
})
}
}

View File

@ -11,6 +11,9 @@ pub trait Synchronizer: Send {
/// Declare sending response in future. /// Declare sending response in future.
fn declare_response(&mut self) -> u32; fn declare_response(&mut self) -> u32;
/// Returns true if permission for response is granted, but without marking response as sent.
fn is_permitted(&self, id: u32) -> bool;
/// Returns true if permission for sending response is granted. /// Returns true if permission for sending response is granted.
fn permission_for_response(&mut self, id: u32) -> bool; fn permission_for_response(&mut self, id: u32) -> bool;
} }
@ -29,6 +32,10 @@ impl Synchronizer for FifoSynchronizer {
result result
} }
fn is_permitted(&self, id: u32) -> bool {
id == self.next_to_grant
}
fn permission_for_response(&mut self, id: u32) -> bool { fn permission_for_response(&mut self, id: u32) -> bool {
// there should be an assertion here, assert!(id < self.declared_responses), // there should be an assertion here, assert!(id < self.declared_responses),
// but it's impossible to write an assertion if the value may overflow // but it's impossible to write an assertion if the value may overflow
@ -54,6 +61,10 @@ impl Synchronizer for NoopSynchronizer {
result result
} }
fn is_permitted(&self, _id: u32) -> bool {
true
}
fn permission_for_response(&mut self, _id: u32) -> bool { fn permission_for_response(&mut self, _id: u32) -> bool {
true true
} }
@ -81,6 +92,16 @@ impl ThresholdSynchronizer {
to_grant_max: declared, to_grant_max: declared,
} }
} }
fn within_threshold(&self, id: u32) -> bool {
if self.to_grant_min <= self.to_grant_max {
// if max is bigger then min, id must be in range [min, max)
self.to_grant_min <= id && id < self.to_grant_max
} else {
// otherwise if is in range [min, u32::max_value()] || [0, max)
self.to_grant_min <= id || id < self.to_grant_max
}
}
} }
impl Synchronizer for ThresholdSynchronizer { impl Synchronizer for ThresholdSynchronizer {
@ -88,19 +109,12 @@ impl Synchronizer for ThresholdSynchronizer {
self.inner.declare_response() self.inner.declare_response()
} }
fn permission_for_response(&mut self, id: u32) -> bool { fn is_permitted(&self, id: u32) -> bool {
if self.inner.permission_for_response(id) { self.inner.is_permitted(id) || self.within_threshold(id)
return true;
} }
if self.to_grant_min <= self.to_grant_max { fn permission_for_response(&mut self, id: u32) -> bool {
// if max is bigger then min, id must be in range [min, max) self.inner.permission_for_response(id) || self.within_threshold(id)
self.to_grant_min <= id && id < self.to_grant_max
} else {
// otherwise if is in range [min, u32::max_value()] || [0, max)
(self.to_grant_min <= id && id <= u32::max_value()) ||
id < self.to_grant_max
}
} }
} }
@ -143,12 +157,12 @@ impl ConfigurableSynchronizer {
/// from last_processed response will still be granted permissions. /// from last_processed response will still be granted permissions.
pub fn change_sync_policy(&mut self, sync: bool) { pub fn change_sync_policy(&mut self, sync: bool) {
let new_inner = match self.inner { let new_inner = match self.inner {
InnerSynchronizer::Threshold(ref s) if sync == false => { InnerSynchronizer::Threshold(ref s) if !sync => {
InnerSynchronizer::Noop(NoopSynchronizer { InnerSynchronizer::Noop(NoopSynchronizer {
declared_responses: s.inner.declared_responses, declared_responses: s.inner.declared_responses,
}) })
}, },
InnerSynchronizer::Noop(ref s) if sync == true => { InnerSynchronizer::Noop(ref s) if sync => {
let threshold = ThresholdSynchronizer::new( let threshold = ThresholdSynchronizer::new(
s.declared_responses, s.declared_responses,
CONFIGURABLE_SYNCHRONIZER_THRESHOLD, CONFIGURABLE_SYNCHRONIZER_THRESHOLD,
@ -170,6 +184,13 @@ impl Synchronizer for ConfigurableSynchronizer {
} }
} }
fn is_permitted(&self, id: u32) -> bool {
match self.inner {
InnerSynchronizer::Noop(ref s) => s.is_permitted(id),
InnerSynchronizer::Threshold(ref s) => s.is_permitted(id),
}
}
fn permission_for_response(&mut self, id: u32) -> bool { fn permission_for_response(&mut self, id: u32) -> bool {
match self.inner { match self.inner {
InnerSynchronizer::Threshold(ref mut s) => s.permission_for_response(id), InnerSynchronizer::Threshold(ref mut s) => s.permission_for_response(id),

View File

@ -2,7 +2,7 @@ use std::net::SocketAddr;
use sync::create_sync_connection_factory; use sync::create_sync_connection_factory;
use message::Services; use message::Services;
use util::{open_db, init_db, node_table_path}; use util::{open_db, init_db, node_table_path};
use {config, p2p}; use {config, p2p, PROTOCOL_VERSION, PROTOCOL_MINIMUM, USER_AGENT};
pub fn start(cfg: config::Config) -> Result<(), String> { pub fn start(cfg: config::Config) -> Result<(), String> {
let mut el = p2p::event_loop(); let mut el = p2p::event_loop();
@ -11,16 +11,16 @@ pub fn start(cfg: config::Config) -> Result<(), String> {
try!(init_db(&cfg, &db)); try!(init_db(&cfg, &db));
let p2p_cfg = p2p::Config { let p2p_cfg = p2p::Config {
threads: 4, threads: cfg.p2p_threads,
protocol_minimum: 70001, inbound_connections: cfg.inbound_connections,
protocol_maximum: 70017, outbound_connections: cfg.outbound_connections,
inbound_connections: 10,
outbound_connections: 10,
connection: p2p::NetConfig { connection: p2p::NetConfig {
protocol_version: PROTOCOL_VERSION,
protocol_minimum: PROTOCOL_MINIMUM,
magic: cfg.magic, magic: cfg.magic,
local_address: SocketAddr::new("127.0.0.1".parse().unwrap(), cfg.port), local_address: SocketAddr::new("127.0.0.1".parse().unwrap(), cfg.port),
services: Services::default().with_network(true), services: Services::default().with_network(true),
user_agent: "pbtc".into(), user_agent: USER_AGENT.into(),
start_height: 0, start_height: 0,
relay: false, relay: false,
}, },

View File

@ -8,6 +8,9 @@ pub struct Config {
pub connect: Option<net::SocketAddr>, pub connect: Option<net::SocketAddr>,
pub seednode: Option<String>, pub seednode: Option<String>,
pub print_to_console: bool, pub print_to_console: bool,
pub inbound_connections: u32,
pub outbound_connections: u32,
pub p2p_threads: usize,
} }
pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> { pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
@ -19,6 +22,16 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
(true, true) => return Err("Only one testnet option can be used".into()), (true, true) => return Err("Only one testnet option can be used".into()),
}; };
let (in_connections, out_connections) = match magic {
Magic::Testnet | Magic::Mainnet => (10, 10),
Magic::Regtest => (1, 0),
};
let p2p_threads = match magic {
Magic::Testnet | Magic::Mainnet => 4,
Magic::Regtest => 1,
};
let port = match matches.value_of("port") { let port = match matches.value_of("port") {
Some(port) => try!(port.parse().map_err(|_| "Invalid port".to_owned())), Some(port) => try!(port.parse().map_err(|_| "Invalid port".to_owned())),
None => magic.port(), None => magic.port(),
@ -45,6 +58,9 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
port: port, port: port,
connect: connect, connect: connect,
seednode: seednode, seednode: seednode,
inbound_connections: in_connections,
outbound_connections: out_connections,
p2p_threads: p2p_threads,
}; };
Ok(config) Ok(config)

View File

@ -23,6 +23,9 @@ mod util;
use app_dirs::AppInfo; use app_dirs::AppInfo;
pub const APP_INFO: AppInfo = AppInfo { name: "pbtc", author: "Parity" }; pub const APP_INFO: AppInfo = AppInfo { name: "pbtc", author: "Parity" };
pub const PROTOCOL_VERSION: u32 = 70_014;
pub const PROTOCOL_MINIMUM: u32 = 70_001;
pub const USER_AGENT: &'static str = "pbtc";
fn main() { fn main() {
env_logger::init().unwrap(); env_logger::init().unwrap();
@ -31,7 +34,6 @@ fn main() {
} }
} }
fn run() -> Result<(), String> { fn run() -> Result<(), String> {
let yaml = load_yaml!("cli.yml"); let yaml = load_yaml!("cli.yml");
let matches = clap::App::from_yaml(yaml).get_matches(); let matches = clap::App::from_yaml(yaml).get_matches();

View File

@ -56,11 +56,11 @@ impl BestHeadersChain {
self.best.position(hash) self.best.position(hash)
.and_then(|pos| self.best.at(pos + 1)) .and_then(|pos| self.best.at(pos + 1))
.and_then(|child| Some(vec![child])) .and_then(|child| Some(vec![child]))
.unwrap_or(Vec::new()) .unwrap_or_default()
} }
pub fn best_block_hash(&self) -> H256 { pub fn best_block_hash(&self) -> H256 {
self.best.back().or(Some(self.storage_best_hash.clone())).expect("storage_best_hash is always known") self.best.back().or_else(|| Some(self.storage_best_hash.clone())).expect("storage_best_hash is always known")
} }
pub fn insert(&mut self, header: BlockHeader) { pub fn insert(&mut self, header: BlockHeader) {
@ -80,7 +80,7 @@ impl BestHeadersChain {
} }
pub fn remove(&mut self, hash: &H256) { pub fn remove(&mut self, hash: &H256) {
if let Some(_) = self.headers.remove(hash) { if self.headers.remove(hash).is_some() {
match self.best.remove(hash) { match self.best.remove(hash) {
HashPosition::Front => self.clear(), HashPosition::Front => self.clear(),
HashPosition::Inside(position) => self.clear_after(position), HashPosition::Inside(position) => self.clear_after(position),
@ -89,8 +89,8 @@ impl BestHeadersChain {
} }
} }
pub fn remove_n<'a, I: IntoIterator<Item=H256>> (&mut self, hashes: I) { pub fn remove_n<I: IntoIterator<Item=H256>> (&mut self, hashes: I) {
for hash in hashes.into_iter() { for hash in hashes {
self.remove(&hash); self.remove(&hash);
} }
} }

View File

@ -191,7 +191,7 @@ impl HashQueueChain {
/// Returns element at the given position /// Returns element at the given position
pub fn at(&self, mut index: u32) -> Option<H256> { pub fn at(&self, mut index: u32) -> Option<H256> {
for queue in self.chain.iter() { for queue in &self.chain {
let queue_len = queue.len(); let queue_len = queue.len();
if index < queue_len { if index < queue_len {
return queue.at(index); return queue.at(index);

View File

@ -29,16 +29,16 @@ impl InboundSyncConnection for InboundConnection {
self.local_node.on_peer_inventory(self.peer_index, message); self.local_node.on_peer_inventory(self.peer_index, message);
} }
fn on_getdata(&self, message: types::GetData) { fn on_getdata(&self, message: types::GetData, id: u32) {
self.local_node.on_peer_getdata(self.peer_index, message); self.local_node.on_peer_getdata(self.peer_index, message, id);
} }
fn on_getblocks(&self, message: types::GetBlocks) { fn on_getblocks(&self, message: types::GetBlocks, id: u32) {
self.local_node.on_peer_getblocks(self.peer_index, message); self.local_node.on_peer_getblocks(self.peer_index, message, id);
} }
fn on_getheaders(&self, message: types::GetHeaders) { fn on_getheaders(&self, message: types::GetHeaders, id: u32) {
self.local_node.on_peer_getheaders(self.peer_index, message); self.local_node.on_peer_getheaders(self.peer_index, message, id);
} }
fn on_transaction(&self, message: types::Tx) { fn on_transaction(&self, message: types::Tx) {
@ -53,8 +53,8 @@ impl InboundSyncConnection for InboundConnection {
self.local_node.on_peer_headers(self.peer_index, message); self.local_node.on_peer_headers(self.peer_index, message);
} }
fn on_mempool(&self, message: types::MemPool) { fn on_mempool(&self, message: types::MemPool, id: u32) {
self.local_node.on_peer_mempool(self.peer_index, message); self.local_node.on_peer_mempool(self.peer_index, message, id);
} }
fn on_filterload(&self, message: types::FilterLoad) { fn on_filterload(&self, message: types::FilterLoad) {

View File

@ -94,23 +94,24 @@ impl<T, U, V> LocalNode<T, U, V> where T: SynchronizationTaskExecutor + PeersCon
// TODO: process other inventory types // TODO: process other inventory types
} }
pub fn on_peer_getdata(&self, peer_index: usize, message: types::GetData) { pub fn on_peer_getdata(&self, peer_index: usize, message: types::GetData, id: u32) {
trace!(target: "sync", "Got `getdata` message from peer#{}", peer_index); trace!(target: "sync", "Got `getdata` message from peer#{}", peer_index);
self.server.serve_getdata(peer_index, message); self.server.serve_getdata(peer_index, message, id);
} }
pub fn on_peer_getblocks(&self, peer_index: usize, message: types::GetBlocks) { pub fn on_peer_getblocks(&self, peer_index: usize, message: types::GetBlocks, id: u32) {
trace!(target: "sync", "Got `getblocks` message from peer#{}", peer_index); trace!(target: "sync", "Got `getblocks` message from peer#{}", peer_index);
self.server.serve_getblocks(peer_index, message); self.server.serve_getblocks(peer_index, message, id);
} }
pub fn on_peer_getheaders(&self, peer_index: usize, message: types::GetHeaders) { pub fn on_peer_getheaders(&self, peer_index: usize, message: types::GetHeaders, id: u32) {
trace!(target: "sync", "Got `getheaders` message from peer#{}", peer_index); trace!(target: "sync", "Got `getheaders` message from peer#{}", peer_index);
// do not serve getheaders requests until we are synchronized // do not serve getheaders requests until we are synchronized
if self.client.lock().state().is_synchronizing() { if self.client.lock().state().is_synchronizing() {
self.executor.lock().execute(SynchronizationTask::Ignore(peer_index, id));
return; return;
} }
@ -125,21 +126,21 @@ impl<T, U, V> LocalNode<T, U, V> where T: SynchronizationTaskExecutor + PeersCon
need_wait need_wait
}; };
self.server.serve_getheaders(peer_index, message); self.server.serve_getheaders(peer_index, message, id);
if need_wait { if need_wait {
self.server.wait_peer_requests_completed(peer_index); self.server.wait_peer_requests_completed(peer_index);
} }
} }
pub fn on_peer_transaction(&self, peer_index: usize, message: types::Tx) { pub fn on_peer_transaction(&self, peer_index: usize, message: types::Tx) {
trace!(target: "sync", "Got `transaction` message from peer#{}. Transaction hash: {}", peer_index, message.transaction.hash()); trace!(target: "sync", "Got `transaction` message from peer#{}. Transaction hash: {}", peer_index, message.transaction.hash().to_reversed_str());
// try to process new transaction // try to process new transaction
self.client.lock().on_peer_transaction(peer_index, message.transaction); self.client.lock().on_peer_transaction(peer_index, message.transaction);
} }
pub fn on_peer_block(&self, peer_index: usize, message: types::Block) { pub fn on_peer_block(&self, peer_index: usize, message: types::Block) {
trace!(target: "sync", "Got `block` message from peer#{}. Block hash: {}", peer_index, message.block.hash()); trace!(target: "sync", "Got `block` message from peer#{}. Block hash: {}", peer_index, message.block.hash().to_reversed_str());
// try to process new block // try to process new block
self.client.lock().on_peer_block(peer_index, message.block); self.client.lock().on_peer_block(peer_index, message.block);
@ -153,10 +154,10 @@ impl<T, U, V> LocalNode<T, U, V> where T: SynchronizationTaskExecutor + PeersCon
} }
} }
pub fn on_peer_mempool(&self, peer_index: usize, _message: types::MemPool) { pub fn on_peer_mempool(&self, peer_index: usize, _message: types::MemPool, id: u32) {
trace!(target: "sync", "Got `mempool` message from peer#{}", peer_index); trace!(target: "sync", "Got `mempool` message from peer#{}", peer_index);
self.server.serve_mempool(peer_index); self.server.serve_mempool(peer_index, id);
} }
pub fn on_peer_filterload(&self, peer_index: usize, _message: types::FilterLoad) { pub fn on_peer_filterload(&self, peer_index: usize, _message: types::FilterLoad) {
@ -206,14 +207,14 @@ impl<T, U, V> LocalNode<T, U, V> where T: SynchronizationTaskExecutor + PeersCon
self.client.lock().on_peer_blocks_notfound(peer_index, blocks_inventory); self.client.lock().on_peer_blocks_notfound(peer_index, blocks_inventory);
} }
fn transactions_inventory(&self, inventory: &Vec<InventoryVector>) -> Vec<H256> { fn transactions_inventory(&self, inventory: &[InventoryVector]) -> Vec<H256> {
inventory.iter() inventory.iter()
.filter(|item| item.inv_type == InventoryType::MessageTx) .filter(|item| item.inv_type == InventoryType::MessageTx)
.map(|item| item.hash.clone()) .map(|item| item.hash.clone())
.collect() .collect()
} }
fn blocks_inventory(&self, inventory: &Vec<InventoryVector>) -> Vec<H256> { fn blocks_inventory(&self, inventory: &[InventoryVector]) -> Vec<H256> {
inventory.iter() inventory.iter()
.filter(|item| item.inv_type == InventoryType::MessageBlock) .filter(|item| item.inv_type == InventoryType::MessageBlock)
.map(|item| item.hash.clone()) .map(|item| item.hash.clone())
@ -249,14 +250,14 @@ mod tests {
} }
impl OutboundSyncConnection for DummyOutboundSyncConnection { impl OutboundSyncConnection for DummyOutboundSyncConnection {
fn send_inventory(&self, _message: &types::Inv) {} fn send_inventory(&self, _message: &types::Inv, _id: u32, _is_final: bool) {}
fn send_getdata(&self, _message: &types::GetData) {} fn send_getdata(&self, _message: &types::GetData) {}
fn send_getblocks(&self, _message: &types::GetBlocks) {} fn send_getblocks(&self, _message: &types::GetBlocks) {}
fn send_getheaders(&self, _message: &types::GetHeaders) {} fn send_getheaders(&self, _message: &types::GetHeaders) {}
fn send_transaction(&self, _message: &types::Tx) {} fn send_transaction(&self, _message: &types::Tx) {}
fn send_block(&self, _message: &types::Block) {} fn send_block(&self, _message: &types::Block, _id: u32, _is_final: bool) {}
fn send_headers(&self, _message: &types::Headers) {} fn send_headers(&self, _message: &types::Headers, _id: u32, _is_final: bool) {}
fn send_mempool(&self, _message: &types::MemPool) {} fn send_mempool(&self, _message: &types::MemPool, _id: u32, _is_final: bool) {}
fn send_filterload(&self, _message: &types::FilterLoad) {} fn send_filterload(&self, _message: &types::FilterLoad) {}
fn send_filteradd(&self, _message: &types::FilterAdd) {} fn send_filteradd(&self, _message: &types::FilterAdd) {}
fn send_filterclear(&self, _message: &types::FilterClear) {} fn send_filterclear(&self, _message: &types::FilterClear) {}
@ -267,7 +268,8 @@ mod tests {
fn send_compact_block(&self, _message: &types::CompactBlock) {} fn send_compact_block(&self, _message: &types::CompactBlock) {}
fn send_get_block_txn(&self, _message: &types::GetBlockTxn) {} fn send_get_block_txn(&self, _message: &types::GetBlockTxn) {}
fn send_block_txn(&self, _message: &types::BlockTxn) {} fn send_block_txn(&self, _message: &types::BlockTxn) {}
fn send_notfound(&self, _message: &types::NotFound) {} fn send_notfound(&self, _message: &types::NotFound, _id: u32, _is_final: bool) {}
fn ignored(&self, _id: u32) {}
} }
fn create_local_node() -> (Core, Handle, Arc<Mutex<DummyTaskExecutor>>, Arc<DummyServer>, LocalNode<DummyTaskExecutor, DummyServer, SynchronizationClient<DummyTaskExecutor>>) { fn create_local_node() -> (Core, Handle, Arc<Mutex<DummyTaskExecutor>>, Arc<DummyServer>, LocalNode<DummyTaskExecutor, DummyServer, SynchronizationClient<DummyTaskExecutor>>) {
@ -305,9 +307,10 @@ mod tests {
hash: genesis_block_hash.clone(), hash: genesis_block_hash.clone(),
} }
]; ];
let dummy_id = 0;
local_node.on_peer_getdata(peer_index, types::GetData { local_node.on_peer_getdata(peer_index, types::GetData {
inventory: inventory.clone() inventory: inventory.clone()
}); }, dummy_id);
// => `getdata` is served // => `getdata` is served
let tasks = server.take_tasks(); let tasks = server.take_tasks();
assert_eq!(tasks, vec![(peer_index, ServerTask::ServeGetData(inventory))]); assert_eq!(tasks, vec![(peer_index, ServerTask::ServeGetData(inventory))]);

View File

@ -367,9 +367,9 @@ impl Chain {
match self.hash_chain.remove_at(VERIFYING_QUEUE, hash) { match self.hash_chain.remove_at(VERIFYING_QUEUE, hash) {
HashPosition::Missing => match self.hash_chain.remove_at(REQUESTED_QUEUE, hash) { HashPosition::Missing => match self.hash_chain.remove_at(REQUESTED_QUEUE, hash) {
HashPosition::Missing => self.hash_chain.remove_at(SCHEDULED_QUEUE, hash), HashPosition::Missing => self.hash_chain.remove_at(SCHEDULED_QUEUE, hash),
position @ _ => position, position => position,
}, },
position @ _ => position, position => position,
} }
} }
@ -416,14 +416,14 @@ impl Chain {
} }
/// Intersect chain with inventory /// Intersect chain with inventory
pub fn intersect_with_blocks_headers(&self, hashes: &Vec<H256>, headers: &Vec<BlockHeader>) -> HeadersIntersection { pub fn intersect_with_blocks_headers(&self, hashes: &[H256], headers: &[BlockHeader]) -> HeadersIntersection {
let hashes_len = hashes.len(); let hashes_len = hashes.len();
assert!(hashes_len != 0 && hashes.len() == headers.len()); assert!(hashes_len != 0 && hashes.len() == headers.len());
// giving that headers are ordered // giving that headers are ordered
let (is_first_known, first_state) = match self.block_state(&hashes[0]) { let (is_first_known, first_state) = match self.block_state(&hashes[0]) {
BlockState::Unknown => (false, self.block_state(&headers[0].previous_header_hash)), BlockState::Unknown => (false, self.block_state(&headers[0].previous_header_hash)),
state @ _ => (true, state), state => (true, state),
}; };
match first_state { match first_state {
// if first block of inventory is unknown && its parent is unknonw => all other blocks are also unknown // if first block of inventory is unknown && its parent is unknonw => all other blocks are also unknown
@ -431,7 +431,7 @@ impl Chain {
HeadersIntersection::NoKnownBlocks(0) HeadersIntersection::NoKnownBlocks(0)
}, },
// else if first block is known // else if first block is known
first_block_state @ _ => match self.block_state(&hashes[hashes_len - 1]) { first_block_state => match self.block_state(&hashes[hashes_len - 1]) {
// if last block is known to be in db => all inventory blocks are also in db // if last block is known to be in db => all inventory blocks are also in db
BlockState::Stored => { BlockState::Stored => {
HeadersIntersection::DbAllBlocksKnown HeadersIntersection::DbAllBlocksKnown
@ -440,23 +440,23 @@ impl Chain {
BlockState::Unknown if !is_first_known => { BlockState::Unknown if !is_first_known => {
// previous block is stored => fork from stored block // previous block is stored => fork from stored block
if first_state == BlockState::Stored { if first_state == BlockState::Stored {
return HeadersIntersection::DbForkNewBlocks(0); HeadersIntersection::DbForkNewBlocks(0)
} }
// previous block is best block => no fork // previous block is best block => no fork
else if &self.best_block().hash == &headers[0].previous_header_hash { else if &self.best_block().hash == &headers[0].previous_header_hash {
return HeadersIntersection::InMemoryMainNewBlocks(0); HeadersIntersection::InMemoryMainNewBlocks(0)
} }
// previous block is not a best block => fork // previous block is not a best block => fork
else { else {
return HeadersIntersection::InMemoryForkNewBlocks(0); HeadersIntersection::InMemoryForkNewBlocks(0)
} }
}, },
// if first block is known && last block is unknown => intersection with queue or with db // if first block is known && last block is unknown => intersection with queue or with db
BlockState::Unknown if is_first_known => { BlockState::Unknown if is_first_known => {
// find last known block // find last known block
let mut previous_state = first_block_state; let mut previous_state = first_block_state;
for index in 1..hashes_len { for (index, hash) in hashes.iter().enumerate().take(hashes_len).skip(1) {
let state = self.block_state(&hashes[index]); let state = self.block_state(hash);
if state == BlockState::Unknown { if state == BlockState::Unknown {
// previous block is stored => fork from stored block // previous block is stored => fork from stored block
if previous_state == BlockState::Stored { if previous_state == BlockState::Stored {

View File

@ -268,8 +268,8 @@ impl Config {
impl State { impl State {
pub fn is_saturated(&self) -> bool { pub fn is_saturated(&self) -> bool {
match self { match *self {
&State::Saturated => true, State::Saturated => true,
_ => false, _ => false,
} }
} }
@ -282,8 +282,8 @@ impl State {
} }
pub fn is_nearly_saturated(&self) -> bool { pub fn is_nearly_saturated(&self) -> bool {
match self { match *self {
&State::NearlySaturated => true, State::NearlySaturated => true,
_ => false, _ => false,
} }
} }
@ -310,7 +310,7 @@ impl<T> Client for SynchronizationClient<T> where T: TaskExecutor {
/// Get synchronization state /// Get synchronization state
fn state(&self) -> State { fn state(&self) -> State {
self.state.clone() self.state
} }
/// Try to queue synchronization of unknown blocks when new inventory is received. /// Try to queue synchronization of unknown blocks when new inventory is received.
@ -328,7 +328,7 @@ impl<T> Client for SynchronizationClient<T> where T: TaskExecutor {
let unknown_blocks_hashes: Vec<_> = { let unknown_blocks_hashes: Vec<_> = {
let chain = self.chain.read(); let chain = self.chain.read();
blocks_hashes.into_iter() blocks_hashes.into_iter()
.filter(|h| chain.block_state(&h) == BlockState::Unknown) .filter(|h| chain.block_state(h) == BlockState::Unknown)
.filter(|h| !self.orphaned_blocks_pool.contains_unknown_block(h)) .filter(|h| !self.orphaned_blocks_pool.contains_unknown_block(h))
.collect() .collect()
}; };
@ -364,11 +364,16 @@ impl<T> Client for SynchronizationClient<T> where T: TaskExecutor {
fn on_new_blocks_headers(&mut self, peer_index: usize, blocks_headers: Vec<BlockHeader>) { fn on_new_blocks_headers(&mut self, peer_index: usize, blocks_headers: Vec<BlockHeader>) {
let blocks_hashes = { let blocks_hashes = {
// we can't process headers message if it has no link to our headers // we can't process headers message if it has no link to our headers
let ref header0 = blocks_headers[0]; let header0 = &blocks_headers[0];
if { let unknown_state = self.chain.read().block_state(&header0.previous_header_hash) == BlockState::Unknown;
self.chain.read().block_state(&header0.previous_header_hash) == BlockState::Unknown if unknown_state {
} { warn!(
warn!(target: "sync", "Previous header of the first header from peer#{} `headers` message is unknown. First: {:?}. Previous: {:?}", peer_index, header0.hash(), header0.previous_header_hash); target: "sync",
"Previous header of the first header from peer#{} `headers` message is unknown. First: {:?}. Previous: {:?}",
peer_index,
header0.hash().to_reversed_str(),
header0.previous_header_hash.to_reversed_str()
);
return; return;
} }
@ -376,7 +381,7 @@ impl<T> Client for SynchronizationClient<T> where T: TaskExecutor {
// validate blocks headers before scheduling // validate blocks headers before scheduling
let mut blocks_hashes: Vec<H256> = Vec::with_capacity(blocks_headers.len()); let mut blocks_hashes: Vec<H256> = Vec::with_capacity(blocks_headers.len());
let mut prev_block_hash = header0.previous_header_hash.clone(); let mut prev_block_hash = header0.previous_header_hash.clone();
for block_header in blocks_headers.iter() { for block_header in &blocks_headers {
let block_header_hash = block_header.hash(); let block_header_hash = block_header.hash();
if block_header.previous_header_hash != prev_block_hash { if block_header.previous_header_hash != prev_block_hash {
warn!(target: "sync", "Neighbour headers in peer#{} `headers` message are unlinked: Prev: {:?}, PrevLink: {:?}, Curr: {:?}", peer_index, prev_block_hash, block_header.previous_header_hash, block_header_hash); warn!(target: "sync", "Neighbour headers in peer#{} `headers` message are unlinked: Prev: {:?}, PrevLink: {:?}, Curr: {:?}", peer_index, prev_block_hash, block_header.previous_header_hash, block_header_hash);
@ -515,18 +520,18 @@ impl<T> Client for SynchronizationClient<T> where T: TaskExecutor {
/// Process failed block verification /// Process failed block verification
fn on_block_verification_error(&mut self, err: &str, hash: &H256) { fn on_block_verification_error(&mut self, err: &str, hash: &H256) {
warn!(target: "sync", "Block {:?} verification failed with error {:?}", hash, err); warn!(target: "sync", "Block {:?} verification failed with error {:?}", hash.to_reversed_str(), err);
{ {
let mut chain = self.chain.write(); let mut chain = self.chain.write();
// forget for this block and all its children // forget for this block and all its children
// headers are also removed as they all are invalid // headers are also removed as they all are invalid
chain.forget_block_with_children(&hash); chain.forget_block_with_children(hash);
} }
// awake threads, waiting for this block insertion // awake threads, waiting for this block insertion
self.awake_waiting_threads(&hash); self.awake_waiting_threads(hash);
// start new tasks // start new tasks
self.execute_synchronization_tasks(None); self.execute_synchronization_tasks(None);
@ -652,7 +657,7 @@ impl<T> SynchronizationClient<T> where T: TaskExecutor {
} }
/// Get configuration parameters. /// Get configuration parameters.
pub fn config<'a>(&'a self) -> &'a Config { pub fn config(&self) -> &Config {
&self.config &self.config
} }
@ -685,7 +690,13 @@ impl<T> SynchronizationClient<T> where T: TaskExecutor {
let new_blocks_hashes = hashes.split_off(new_block_index); let new_blocks_hashes = hashes.split_off(new_block_index);
let new_blocks_headers = headers.split_off(new_block_index); let new_blocks_headers = headers.split_off(new_block_index);
let new_blocks_hashes_len = new_blocks_hashes.len(); let new_blocks_hashes_len = new_blocks_hashes.len();
trace!(target: "sync", "Sch. {} headers from peer#{}. First {:?}, last: {:?}", new_blocks_hashes_len, peer_index, new_blocks_hashes[0], new_blocks_hashes[new_blocks_hashes_len - 1]); trace!(
target: "sync", "Sch. {} headers from peer#{}. First {:?}, last: {:?}",
new_blocks_hashes_len,
peer_index,
new_blocks_hashes[0].to_reversed_str(),
new_blocks_hashes[new_blocks_hashes_len - 1].to_reversed_str()
);
chain.schedule_blocks_headers(new_blocks_hashes, new_blocks_headers); chain.schedule_blocks_headers(new_blocks_hashes, new_blocks_headers);
// remember peer as useful // remember peer as useful
self.peers.useful_peer(peer_index); self.peers.useful_peer(peer_index);
@ -718,7 +729,12 @@ impl<T> SynchronizationClient<T> where T: TaskExecutor {
BlockState::Unknown => { BlockState::Unknown => {
if self.state.is_synchronizing() { if self.state.is_synchronizing() {
// when synchronizing, we tend to receive all blocks in-order // when synchronizing, we tend to receive all blocks in-order
trace!(target: "sync", "Ignoring block {} from peer#{}, because its parent is unknown and we are synchronizing", block_hash, peer_index); trace!(
target: "sync",
"Ignoring block {} from peer#{}, because its parent is unknown and we are synchronizing",
block_hash.to_reversed_str(),
peer_index
);
// remove block from current queue // remove block from current queue
chain.forget_block(&block_hash); chain.forget_block(&block_hash);
// remove orphaned blocks // remove orphaned blocks
@ -863,11 +879,11 @@ impl<T> SynchronizationClient<T> where T: TaskExecutor {
if !inventory_idle_peers.is_empty() { if !inventory_idle_peers.is_empty() {
let scheduled_hashes_len = { self.chain.read().length_of_blocks_state(BlockState::Scheduled) }; let scheduled_hashes_len = { self.chain.read().length_of_blocks_state(BlockState::Scheduled) };
if scheduled_hashes_len < MAX_SCHEDULED_HASHES { if scheduled_hashes_len < MAX_SCHEDULED_HASHES {
for inventory_peer in inventory_idle_peers.iter() { for inventory_peer in &inventory_idle_peers {
self.peers.on_inventory_requested(*inventory_peer); self.peers.on_inventory_requested(*inventory_peer);
} }
let inventory_tasks = inventory_idle_peers.into_iter().map(|p| Task::RequestBlocksHeaders(p)); let inventory_tasks = inventory_idle_peers.into_iter().map(Task::RequestBlocksHeaders);
tasks.extend(inventory_tasks); tasks.extend(inventory_tasks);
} }
} }
@ -1009,10 +1025,20 @@ impl<T> SynchronizationClient<T> where T: TaskExecutor {
/// Thread procedure for handling verification tasks /// Thread procedure for handling verification tasks
fn verification_worker_proc(sync: Arc<Mutex<Self>>, mut verifier: ChainVerifier, work_receiver: Receiver<VerificationTask>) { fn verification_worker_proc(sync: Arc<Mutex<Self>>, mut verifier: ChainVerifier, work_receiver: Receiver<VerificationTask>) {
let bip16_time_border = { sync.lock().config().consensus_params.bip16_time };
let mut is_bip16_active = false;
let mut parameters_change_steps = Some(0); let mut parameters_change_steps = Some(0);
while let Ok(task) = work_receiver.recv() { while let Ok(task) = work_receiver.recv() {
match task { match task {
VerificationTask::VerifyBlock(block) => { VerificationTask::VerifyBlock(block) => {
// for changes that are not relying on block#
let is_bip16_active_on_block = block.block_header.time >= bip16_time_border;
let force_parameters_change = is_bip16_active_on_block != is_bip16_active;
if force_parameters_change {
parameters_change_steps = Some(0);
}
// change verifier parameters, if needed // change verifier parameters, if needed
if let Some(steps_left) = parameters_change_steps { if let Some(steps_left) = parameters_change_steps {
if steps_left == 0 { if steps_left == 0 {
@ -1020,6 +1046,9 @@ impl<T> SynchronizationClient<T> where T: TaskExecutor {
let config = sync.config(); let config = sync.config();
let best_storage_block = sync.chain.read().best_storage_block(); let best_storage_block = sync.chain.read().best_storage_block();
is_bip16_active = is_bip16_active_on_block;
verifier = verifier.verify_p2sh(is_bip16_active);
let is_bip65_active = best_storage_block.number >= config.consensus_params.bip65_height; let is_bip65_active = best_storage_block.number >= config.consensus_params.bip65_height;
verifier = verifier.verify_clocktimeverify(is_bip65_active); verifier = verifier.verify_clocktimeverify(is_bip65_active);

View File

@ -7,6 +7,7 @@ use message::types;
use primitives::hash::H256; use primitives::hash::H256;
use p2p::OutboundSyncConnectionRef; use p2p::OutboundSyncConnectionRef;
use synchronization_chain::ChainRef; use synchronization_chain::ChainRef;
use synchronization_server::ServerTaskIndex;
use local_node::PeersConnections; use local_node::PeersConnections;
pub type LocalSynchronizationTaskExecutorRef = Arc<Mutex<LocalSynchronizationTaskExecutor>>; pub type LocalSynchronizationTaskExecutorRef = Arc<Mutex<LocalSynchronizationTaskExecutor>>;
@ -28,13 +29,15 @@ pub enum Task {
/// Request memory pool contents /// Request memory pool contents
RequestMemoryPool(usize), RequestMemoryPool(usize),
/// Send block. /// Send block.
SendBlock(usize, Block), SendBlock(usize, Block, ServerTaskIndex),
/// Send notfound /// Send notfound
SendNotFound(usize, Vec<InventoryVector>), SendNotFound(usize, Vec<InventoryVector>, ServerTaskIndex),
/// Send inventory /// Send inventory
SendInventory(usize, Vec<InventoryVector>), SendInventory(usize, Vec<InventoryVector>, ServerTaskIndex),
/// Send headers /// Send headers
SendHeaders(usize, Vec<BlockHeader>), SendHeaders(usize, Vec<BlockHeader>, ServerTaskIndex),
/// Notify io about ignored request
Ignore(usize, u32),
} }
/// Synchronization tasks executor /// Synchronization tasks executor
@ -79,7 +82,6 @@ impl TaskExecutor for LocalSynchronizationTaskExecutor {
}; };
if let Some(connection) = self.peers.get_mut(&peer_index) { if let Some(connection) = self.peers.get_mut(&peer_index) {
let connection = &mut *connection;
trace!(target: "sync", "Querying {} unknown blocks from peer#{}", getdata.inventory.len(), peer_index); trace!(target: "sync", "Querying {} unknown blocks from peer#{}", getdata.inventory.len(), peer_index);
connection.send_getdata(&getdata); connection.send_getdata(&getdata);
} }
@ -93,7 +95,6 @@ impl TaskExecutor for LocalSynchronizationTaskExecutor {
}; };
if let Some(connection) = self.peers.get_mut(&peer_index) { if let Some(connection) = self.peers.get_mut(&peer_index) {
let connection = &mut *connection;
trace!(target: "sync", "Request blocks hashes from peer#{} using getheaders", peer_index); trace!(target: "sync", "Request blocks hashes from peer#{} using getheaders", peer_index);
connection.send_getheaders(&getheaders); connection.send_getheaders(&getheaders);
} }
@ -122,48 +123,50 @@ impl TaskExecutor for LocalSynchronizationTaskExecutor {
connection.send_getdata(&getdata); connection.send_getdata(&getdata);
} }
}, },
Task::SendBlock(peer_index, block) => { Task::SendBlock(peer_index, block, id) => {
let block_message = types::Block { let block_message = types::Block {
block: block, block: block,
}; };
if let Some(connection) = self.peers.get_mut(&peer_index) { if let Some(connection) = self.peers.get_mut(&peer_index) {
let connection = &mut *connection;
trace!(target: "sync", "Sending block {:?} to peer#{}", block_message.block.hash(), peer_index); trace!(target: "sync", "Sending block {:?} to peer#{}", block_message.block.hash(), peer_index);
connection.send_block(&block_message); connection.send_block(&block_message, id.raw(), id.is_final());
} }
}, },
Task::SendNotFound(peer_index, unknown_inventory) => { Task::SendNotFound(peer_index, unknown_inventory, id) => {
let notfound = types::NotFound { let notfound = types::NotFound {
inventory: unknown_inventory, inventory: unknown_inventory,
}; };
if let Some(connection) = self.peers.get_mut(&peer_index) { if let Some(connection) = self.peers.get_mut(&peer_index) {
let connection = &mut *connection;
trace!(target: "sync", "Sending notfound to peer#{} with {} items", peer_index, notfound.inventory.len()); trace!(target: "sync", "Sending notfound to peer#{} with {} items", peer_index, notfound.inventory.len());
connection.send_notfound(&notfound); connection.send_notfound(&notfound, id.raw(), id.is_final());
} }
}, },
Task::SendInventory(peer_index, inventory) => { Task::SendInventory(peer_index, inventory, id) => {
let inventory = types::Inv { let inventory = types::Inv {
inventory: inventory, inventory: inventory,
}; };
if let Some(connection) = self.peers.get_mut(&peer_index) { if let Some(connection) = self.peers.get_mut(&peer_index) {
let connection = &mut *connection;
trace!(target: "sync", "Sending inventory to peer#{} with {} items", peer_index, inventory.inventory.len()); trace!(target: "sync", "Sending inventory to peer#{} with {} items", peer_index, inventory.inventory.len());
connection.send_inventory(&inventory); connection.send_inventory(&inventory, id.raw(), id.is_final());
} }
}, },
Task::SendHeaders(peer_index, headers) => { Task::SendHeaders(peer_index, headers, id) => {
let headers = types::Headers { let headers = types::Headers {
headers: headers, headers: headers,
}; };
if let Some(connection) = self.peers.get_mut(&peer_index) { if let Some(connection) = self.peers.get_mut(&peer_index) {
let connection = &mut *connection;
trace!(target: "sync", "Sending headers to peer#{} with {} items", peer_index, headers.headers.len()); trace!(target: "sync", "Sending headers to peer#{} with {} items", peer_index, headers.headers.len());
connection.send_headers(&headers); connection.send_headers(&headers, id.raw(), id.is_final());
}
},
Task::Ignore(peer_index, id) => {
if let Some(connection) = self.peers.get_mut(&peer_index) {
trace!(target: "sync", "Ignoring request from peer#{} with id {}", peer_index, id);
connection.ignored(id);
} }
}, },
} }

View File

@ -14,10 +14,10 @@ use message::types;
/// Synchronization requests server trait /// Synchronization requests server trait
pub trait Server : Send + 'static { pub trait Server : Send + 'static {
fn serve_getdata(&self, peer_index: usize, message: types::GetData); fn serve_getdata(&self, peer_index: usize, message: types::GetData, id: u32);
fn serve_getblocks(&self, peer_index: usize, message: types::GetBlocks); fn serve_getblocks(&self, peer_index: usize, message: types::GetBlocks, id: u32);
fn serve_getheaders(&self, peer_index: usize, message: types::GetHeaders); fn serve_getheaders(&self, peer_index: usize, message: types::GetHeaders, id: u32);
fn serve_mempool(&self, peer_index: usize); fn serve_mempool(&self, peer_index: usize, id: u32);
fn wait_peer_requests_completed(&self, peer_index: usize); fn wait_peer_requests_completed(&self, peer_index: usize);
} }
@ -42,10 +42,58 @@ struct ServerQueue {
is_stopping: AtomicBool, is_stopping: AtomicBool,
queue_ready: Arc<Condvar>, queue_ready: Arc<Condvar>,
peers_queue: VecDeque<usize>, peers_queue: VecDeque<usize>,
tasks_queue: HashMap<usize, VecDeque<ServerTask>>, tasks_queue: HashMap<usize, VecDeque<IndexedServerTask>>,
peer_waiters: HashMap<usize, Arc<PeerRequestsWaiter>>, peer_waiters: HashMap<usize, Arc<PeerRequestsWaiter>>,
} }
/// `ServerTask` index.
#[derive(Debug, PartialEq)]
pub enum ServerTaskIndex {
/// `Partial` is used when server needs to send more than one response for request.
Partial(u32),
/// `Final` task task can be preceded by many `Partial` tasks with the same id.
Final(u32),
}
impl ServerTaskIndex {
pub fn raw(&self) -> u32 {
match *self {
ServerTaskIndex::Partial(id) | ServerTaskIndex::Final(id) => id,
}
}
pub fn is_final(&self) -> bool {
match *self {
ServerTaskIndex::Partial(_) => false,
ServerTaskIndex::Final(_) => true,
}
}
}
/// Server tests together with unique id assigned to it
#[derive(Debug, PartialEq)]
pub struct IndexedServerTask {
/// Task itself.
task: ServerTask,
/// Task id.
id: ServerTaskIndex,
}
impl IndexedServerTask {
fn new(task: ServerTask, id: ServerTaskIndex) -> Self {
IndexedServerTask {
task: task,
id: id,
}
}
}
impl IndexedServerTask {
fn ignore(id: u32) -> Self {
IndexedServerTask::new(ServerTask::Ignore, ServerTaskIndex::Final(id))
}
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ServerTask { pub enum ServerTask {
ServeGetData(Vec<InventoryVector>), ServeGetData(Vec<InventoryVector>),
@ -54,6 +102,7 @@ pub enum ServerTask {
ServeMempool, ServeMempool,
ReturnNotFound(Vec<InventoryVector>), ReturnNotFound(Vec<InventoryVector>),
ReturnBlock(H256), ReturnBlock(H256),
Ignore,
} }
impl SynchronizationServer { impl SynchronizationServer {
@ -97,11 +146,18 @@ impl SynchronizationServer {
}) })
}; };
match server_task { let (peer_index, indexed_task) = match server_task {
Some((peer_index, indexed_task)) => (peer_index, indexed_task),
// no tasks after wake-up => stopping or pausing
_ => continue,
};
match indexed_task.task {
// `getdata` => `notfound` + `block` + ... // `getdata` => `notfound` + `block` + ...
Some((peer_index, ServerTask::ServeGetData(inventory))) => { ServerTask::ServeGetData(inventory) => {
let mut unknown_items: Vec<InventoryVector> = Vec::new(); let mut unknown_items: Vec<InventoryVector> = Vec::new();
let mut new_tasks: Vec<ServerTask> = Vec::new(); let mut new_tasks: Vec<IndexedServerTask> = Vec::new();
let task_id = indexed_task.id.raw();
{ {
let chain = chain.read(); let chain = chain.read();
let storage = chain.storage(); let storage = chain.storage();
@ -109,7 +165,10 @@ impl SynchronizationServer {
match item.inv_type { match item.inv_type {
InventoryType::MessageBlock => { InventoryType::MessageBlock => {
match storage.block_number(&item.hash) { match storage.block_number(&item.hash) {
Some(_) => new_tasks.push(ServerTask::ReturnBlock(item.hash.clone())), Some(_) => {
let task = IndexedServerTask::new(ServerTask::ReturnBlock(item.hash.clone()), ServerTaskIndex::Partial(task_id));
new_tasks.push(task);
},
None => unknown_items.push(item), None => unknown_items.push(item),
} }
}, },
@ -120,18 +179,25 @@ impl SynchronizationServer {
// respond with `notfound` message for unknown data // respond with `notfound` message for unknown data
if !unknown_items.is_empty() { if !unknown_items.is_empty() {
trace!(target: "sync", "Going to respond with notfound with {} items to peer#{}", unknown_items.len(), peer_index); trace!(target: "sync", "Going to respond with notfound with {} items to peer#{}", unknown_items.len(), peer_index);
new_tasks.push(ServerTask::ReturnNotFound(unknown_items)); let task = IndexedServerTask::new(ServerTask::ReturnNotFound(unknown_items), ServerTaskIndex::Partial(task_id));
new_tasks.push(task);
} }
// schedule data responses // schedule data responses
if !new_tasks.is_empty() { if !new_tasks.is_empty() {
trace!(target: "sync", "Going to respond with data with {} items to peer#{}", new_tasks.len(), peer_index); trace!(target: "sync", "Going to respond with data with {} items to peer#{}", new_tasks.len(), peer_index);
// mark last task as the final one
if let Some(task) = new_tasks.last_mut() {
task.id = ServerTaskIndex::Final(task_id);
}
queue.lock().add_tasks(peer_index, new_tasks); queue.lock().add_tasks(peer_index, new_tasks);
} else {
executor.lock().execute(Task::Ignore(peer_index, task_id));
} }
// inform that we have processed task for peer // inform that we have processed task for peer
queue.lock().task_processed(peer_index); queue.lock().task_processed(peer_index);
}, },
// `getblocks` => `inventory` // `getblocks` => `inventory`
Some((peer_index, ServerTask::ServeGetBlocks(best_block, hash_stop))) => { ServerTask::ServeGetBlocks(best_block, hash_stop) => {
let blocks_hashes = SynchronizationServer::blocks_hashes_after(&chain, &best_block, &hash_stop, 500); let blocks_hashes = SynchronizationServer::blocks_hashes_after(&chain, &best_block, &hash_stop, 500);
if !blocks_hashes.is_empty() { if !blocks_hashes.is_empty() {
trace!(target: "sync", "Going to respond with inventory with {} items to peer#{}", blocks_hashes.len(), peer_index); trace!(target: "sync", "Going to respond with inventory with {} items to peer#{}", blocks_hashes.len(), peer_index);
@ -139,25 +205,29 @@ impl SynchronizationServer {
inv_type: InventoryType::MessageBlock, inv_type: InventoryType::MessageBlock,
hash: hash, hash: hash,
}).collect(); }).collect();
executor.lock().execute(Task::SendInventory(peer_index, inventory)); executor.lock().execute(Task::SendInventory(peer_index, inventory, indexed_task.id));
} else {
executor.lock().execute(Task::Ignore(peer_index, indexed_task.id.raw()));
} }
// inform that we have processed task for peer // inform that we have processed task for peer
queue.lock().task_processed(peer_index); queue.lock().task_processed(peer_index);
}, },
// `getheaders` => `headers` // `getheaders` => `headers`
Some((peer_index, ServerTask::ServeGetHeaders(best_block, hash_stop))) => { ServerTask::ServeGetHeaders(best_block, hash_stop) => {
// What if we have no common blocks with peer at all? Maybe drop connection or penalize peer? // What if we have no common blocks with peer at all? Maybe drop connection or penalize peer?
// https://github.com/ethcore/parity-bitcoin/pull/91#discussion_r86734568 // https://github.com/ethcore/parity-bitcoin/pull/91#discussion_r86734568
let blocks_headers = SynchronizationServer::blocks_headers_after(&chain, &best_block, &hash_stop, 2000); let blocks_headers = SynchronizationServer::blocks_headers_after(&chain, &best_block, &hash_stop, 2000);
if !blocks_headers.is_empty() { if !blocks_headers.is_empty() {
trace!(target: "sync", "Going to respond with blocks headers with {} items to peer#{}", blocks_headers.len(), peer_index); trace!(target: "sync", "Going to respond with blocks headers with {} items to peer#{}", blocks_headers.len(), peer_index);
executor.lock().execute(Task::SendHeaders(peer_index, blocks_headers)); executor.lock().execute(Task::SendHeaders(peer_index, blocks_headers, indexed_task.id));
} else {
executor.lock().execute(Task::Ignore(peer_index, indexed_task.id.raw()));
} }
// inform that we have processed task for peer // inform that we have processed task for peer
queue.lock().task_processed(peer_index); queue.lock().task_processed(peer_index);
}, },
// `mempool` => `inventory` // `mempool` => `inventory`
Some((peer_index, ServerTask::ServeMempool)) => { ServerTask::ServeMempool => {
let inventory: Vec<_> = chain.read() let inventory: Vec<_> = chain.read()
.transactions_hashes_with_state(TransactionState::InMemory) .transactions_hashes_with_state(TransactionState::InMemory)
.into_iter() .into_iter()
@ -168,27 +238,32 @@ impl SynchronizationServer {
.collect(); .collect();
if !inventory.is_empty() { if !inventory.is_empty() {
trace!(target: "sync", "Going to respond with {} memory-pool transactions ids to peer#{}", inventory.len(), peer_index); trace!(target: "sync", "Going to respond with {} memory-pool transactions ids to peer#{}", inventory.len(), peer_index);
executor.lock().execute(Task::SendInventory(peer_index, inventory)); executor.lock().execute(Task::SendInventory(peer_index, inventory, indexed_task.id));
} else {
executor.lock().execute(Task::Ignore(peer_index, indexed_task.id.raw()));
} }
// inform that we have processed task for peer // inform that we have processed task for peer
queue.lock().task_processed(peer_index); queue.lock().task_processed(peer_index);
}, },
// `notfound` // `notfound`
Some((peer_index, ServerTask::ReturnNotFound(inventory))) => { ServerTask::ReturnNotFound(inventory) => {
executor.lock().execute(Task::SendNotFound(peer_index, inventory)); executor.lock().execute(Task::SendNotFound(peer_index, inventory, indexed_task.id));
// inform that we have processed task for peer // inform that we have processed task for peer
queue.lock().task_processed(peer_index); queue.lock().task_processed(peer_index);
}, },
// `block` // `block`
Some((peer_index, ServerTask::ReturnBlock(block_hash))) => { ServerTask::ReturnBlock(block_hash) => {
let block = chain.read().storage().block(db::BlockRef::Hash(block_hash)) let block = chain.read().storage().block(db::BlockRef::Hash(block_hash))
.expect("we have checked that block exists in ServeGetData; db is append-only; qed"); .expect("we have checked that block exists in ServeGetData; db is append-only; qed");
executor.lock().execute(Task::SendBlock(peer_index, block)); executor.lock().execute(Task::SendBlock(peer_index, block, indexed_task.id));
// inform that we have processed task for peer // inform that we have processed task for peer
queue.lock().task_processed(peer_index); queue.lock().task_processed(peer_index);
}, },
// no tasks after wake-up => stopping or pausing // ignore
None => (), ServerTask::Ignore => {
executor.lock().execute(Task::Ignore(peer_index, indexed_task.id.raw()));
queue.lock().task_processed(peer_index);
},
} }
} }
} }
@ -205,9 +280,9 @@ impl SynchronizationServer {
// `max_hashes` hashes after best_block.number OR hash_stop OR blockchain end // `max_hashes` hashes after best_block.number OR hash_stop OR blockchain end
(first_block_number..last_block_number).into_iter() (first_block_number..last_block_number).into_iter()
.map(|number| chain.block_hash(number)) .map(|number| chain.block_hash(number))
.take_while(|ref hash| hash.is_some()) .take_while(|hash| hash.is_some())
.map(|hash| hash.unwrap()) .map(|hash| hash.unwrap())
.take_while(|ref hash| *hash != hash_stop) .take_while(|hash| hash != hash_stop)
.collect() .collect()
} }
@ -223,16 +298,16 @@ impl SynchronizationServer {
// `max_hashes` hashes after best_block.number OR hash_stop OR blockchain end // `max_hashes` hashes after best_block.number OR hash_stop OR blockchain end
(first_block_number..last_block_number).into_iter() (first_block_number..last_block_number).into_iter()
.map(|number| chain.block_header_by_number(number)) .map(|number| chain.block_header_by_number(number))
.take_while(|ref header| header.is_some()) .take_while(|header| header.is_some())
.map(|header| header.unwrap()) .map(|header| header.unwrap())
.take_while(|ref header| &header.hash() != hash_stop) .take_while(|header| &header.hash() != hash_stop)
.collect() .collect()
} }
fn locate_best_known_block_hash(chain: &ChainRef, hash: &H256) -> Option<db::BestBlock> { fn locate_best_known_block_hash(chain: &ChainRef, hash: &H256) -> Option<db::BestBlock> {
let chain = chain.read(); let chain = chain.read();
match chain.block_number(&hash) { match chain.block_number(hash) {
Some(number) => Some(db::BestBlock { Some(number) => Some(db::BestBlock {
number: number, number: number,
hash: hash.clone(), hash: hash.clone(),
@ -240,7 +315,7 @@ impl SynchronizationServer {
// block with hash is not in the main chain (block_number has returned None) // block with hash is not in the main chain (block_number has returned None)
// but maybe it is in some fork? if so => we should find intersection with main chain // but maybe it is in some fork? if so => we should find intersection with main chain
// and this would be our best common block // and this would be our best common block
None => chain.block_header_by_hash(&hash) None => chain.block_header_by_hash(hash)
.and_then(|block| { .and_then(|block| {
let mut current_block_hash = block.previous_header_hash; let mut current_block_hash = block.previous_header_hash;
loop { loop {
@ -272,32 +347,38 @@ impl Drop for SynchronizationServer {
} }
impl Server for SynchronizationServer { impl Server for SynchronizationServer {
fn serve_getdata(&self, peer_index: usize, message: types::GetData) { fn serve_getdata(&self, peer_index: usize, message: types::GetData, id: u32) {
self.queue.lock().add_task(peer_index, ServerTask::ServeGetData(message.inventory)); let task = IndexedServerTask::new(ServerTask::ServeGetData(message.inventory), ServerTaskIndex::Final(id));
self.queue.lock().add_task(peer_index, task);
} }
fn serve_getblocks(&self, peer_index: usize, message: types::GetBlocks) { fn serve_getblocks(&self, peer_index: usize, message: types::GetBlocks, id: u32) {
if let Some(best_common_block) = self.locate_known_block_hash(message.block_locator_hashes) { if let Some(best_common_block) = self.locate_known_block_hash(message.block_locator_hashes) {
trace!(target: "sync", "Best common block with peer#{} is block#{}: {:?}", peer_index, best_common_block.number, best_common_block.hash); trace!(target: "sync", "Best common block with peer#{} is block#{}: {:?}", peer_index, best_common_block.number, best_common_block.hash);
self.queue.lock().add_task(peer_index, ServerTask::ServeGetBlocks(best_common_block, message.hash_stop)); let task = IndexedServerTask::new(ServerTask::ServeGetBlocks(best_common_block, message.hash_stop), ServerTaskIndex::Final(id));
self.queue.lock().add_task(peer_index, task);
} }
else { else {
trace!(target: "sync", "No common blocks with peer#{}", peer_index); trace!(target: "sync", "No common blocks with peer#{}", peer_index);
self.queue.lock().add_task(peer_index, IndexedServerTask::ignore(id));
} }
} }
fn serve_getheaders(&self, peer_index: usize, message: types::GetHeaders) { fn serve_getheaders(&self, peer_index: usize, message: types::GetHeaders, id: u32) {
if let Some(best_common_block) = self.locate_known_block_header(message.block_locator_hashes) { if let Some(best_common_block) = self.locate_known_block_header(message.block_locator_hashes) {
trace!(target: "sync", "Best common block header with peer#{} is block#{}: {:?}", peer_index, best_common_block.number, best_common_block.hash); trace!(target: "sync", "Best common block header with peer#{} is block#{}: {:?}", peer_index, best_common_block.number, best_common_block.hash.to_reversed_str());
self.queue.lock().add_task(peer_index, ServerTask::ServeGetHeaders(best_common_block, message.hash_stop)); let task = IndexedServerTask::new(ServerTask::ServeGetHeaders(best_common_block, message.hash_stop), ServerTaskIndex::Final(id));
self.queue.lock().add_task(peer_index, task);
} }
else { else {
trace!(target: "sync", "No common blocks headers with peer#{}", peer_index); trace!(target: "sync", "No common blocks headers with peer#{}", peer_index);
self.queue.lock().add_task(peer_index, IndexedServerTask::ignore(id));
} }
} }
fn serve_mempool(&self, peer_index: usize) { fn serve_mempool(&self, peer_index: usize, id: u32) {
self.queue.lock().add_task(peer_index, ServerTask::ServeMempool); let task = IndexedServerTask::new(ServerTask::ServeMempool, ServerTaskIndex::Final(id));
self.queue.lock().add_task(peer_index, task);
} }
fn wait_peer_requests_completed(&self, peer_index: usize) { fn wait_peer_requests_completed(&self, peer_index: usize) {
@ -321,7 +402,7 @@ impl ServerQueue {
} }
} }
pub fn next_task(&mut self) -> Option<(usize, ServerTask)> { pub fn next_task(&mut self) -> Option<(usize, IndexedServerTask)> {
self.peers_queue.pop_front() self.peers_queue.pop_front()
.map(|peer| { .map(|peer| {
let (peer_task, no_tasks_left) = { let (peer_task, no_tasks_left) = {
@ -352,7 +433,7 @@ impl ServerQueue {
} }
} }
pub fn add_task(&mut self, peer_index: usize, task: ServerTask) { pub fn add_task(&mut self, peer_index: usize, task: IndexedServerTask) {
match self.tasks_queue.entry(peer_index) { match self.tasks_queue.entry(peer_index) {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
let add_to_peers_queue = entry.get().is_empty(); let add_to_peers_queue = entry.get().is_empty();
@ -371,7 +452,7 @@ impl ServerQueue {
self.queue_ready.notify_one(); self.queue_ready.notify_one();
} }
pub fn add_tasks(&mut self, peer_index: usize, tasks: Vec<ServerTask>) { pub fn add_tasks(&mut self, peer_index: usize, tasks: Vec<IndexedServerTask>) {
match self.tasks_queue.entry(peer_index) { match self.tasks_queue.entry(peer_index) {
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
let add_to_peers_queue = entry.get().is_empty(); let add_to_peers_queue = entry.get().is_empty();
@ -441,7 +522,7 @@ pub mod tests {
use synchronization_executor::Task; use synchronization_executor::Task;
use synchronization_executor::tests::DummyTaskExecutor; use synchronization_executor::tests::DummyTaskExecutor;
use synchronization_chain::Chain; use synchronization_chain::Chain;
use super::{Server, ServerTask, SynchronizationServer}; use super::{Server, ServerTask, SynchronizationServer, ServerTaskIndex};
pub struct DummyServer { pub struct DummyServer {
tasks: Mutex<Vec<(usize, ServerTask)>>, tasks: Mutex<Vec<(usize, ServerTask)>>,
@ -460,25 +541,25 @@ pub mod tests {
} }
impl Server for DummyServer { impl Server for DummyServer {
fn serve_getdata(&self, peer_index: usize, message: types::GetData) { fn serve_getdata(&self, peer_index: usize, message: types::GetData, _id: u32) {
self.tasks.lock().push((peer_index, ServerTask::ServeGetData(message.inventory))); self.tasks.lock().push((peer_index, ServerTask::ServeGetData(message.inventory)));
} }
fn serve_getblocks(&self, peer_index: usize, message: types::GetBlocks) { fn serve_getblocks(&self, peer_index: usize, message: types::GetBlocks, _id: u32) {
self.tasks.lock().push((peer_index, ServerTask::ServeGetBlocks(db::BestBlock { self.tasks.lock().push((peer_index, ServerTask::ServeGetBlocks(db::BestBlock {
number: 0, number: 0,
hash: message.block_locator_hashes[0].clone(), hash: message.block_locator_hashes[0].clone(),
}, message.hash_stop))); }, message.hash_stop)));
} }
fn serve_getheaders(&self, peer_index: usize, message: types::GetHeaders) { fn serve_getheaders(&self, peer_index: usize, message: types::GetHeaders, _id: u32) {
self.tasks.lock().push((peer_index, ServerTask::ServeGetHeaders(db::BestBlock { self.tasks.lock().push((peer_index, ServerTask::ServeGetHeaders(db::BestBlock {
number: 0, number: 0,
hash: message.block_locator_hashes[0].clone(), hash: message.block_locator_hashes[0].clone(),
}, message.hash_stop))); }, message.hash_stop)));
} }
fn serve_mempool(&self, peer_index: usize) { fn serve_mempool(&self, peer_index: usize, _id: u32) {
self.tasks.lock().push((peer_index, ServerTask::ServeMempool)); self.tasks.lock().push((peer_index, ServerTask::ServeMempool));
} }
@ -503,12 +584,13 @@ pub mod tests {
hash: H256::default(), hash: H256::default(),
} }
]; ];
let dummy_id = 0;
server.serve_getdata(0, types::GetData { server.serve_getdata(0, types::GetData {
inventory: inventory.clone(), inventory: inventory.clone(),
}); }, dummy_id);
// => respond with notfound // => respond with notfound
let tasks = DummyTaskExecutor::wait_tasks(executor); let tasks = DummyTaskExecutor::wait_tasks(executor);
assert_eq!(tasks, vec![Task::SendNotFound(0, inventory)]); assert_eq!(tasks, vec![Task::SendNotFound(0, inventory, ServerTaskIndex::Final(dummy_id))]);
} }
#[test] #[test]
@ -521,12 +603,13 @@ pub mod tests {
hash: test_data::genesis().hash(), hash: test_data::genesis().hash(),
} }
]; ];
let dummy_id = 0;
server.serve_getdata(0, types::GetData { server.serve_getdata(0, types::GetData {
inventory: inventory.clone(), inventory: inventory.clone(),
}); }, dummy_id);
// => respond with block // => respond with block
let tasks = DummyTaskExecutor::wait_tasks(executor); let tasks = DummyTaskExecutor::wait_tasks(executor);
assert_eq!(tasks, vec![Task::SendBlock(0, test_data::genesis())]); assert_eq!(tasks, vec![Task::SendBlock(0, test_data::genesis(), ServerTaskIndex::Final(dummy_id))]);
} }
#[test] #[test]
@ -534,14 +617,15 @@ pub mod tests {
let (_, executor, server) = create_synchronization_server(); let (_, executor, server) = create_synchronization_server();
// when asking for blocks hashes // when asking for blocks hashes
let genesis_block_hash = test_data::genesis().hash(); let genesis_block_hash = test_data::genesis().hash();
let dummy_id = 5;
server.serve_getblocks(0, types::GetBlocks { server.serve_getblocks(0, types::GetBlocks {
version: 0, version: 0,
block_locator_hashes: vec![genesis_block_hash.clone()], block_locator_hashes: vec![genesis_block_hash.clone()],
hash_stop: H256::default(), hash_stop: H256::default(),
}); }, dummy_id);
// => no response // => no response
let tasks = DummyTaskExecutor::wait_tasks_for(executor, 100); // TODO: get rid of explicit timeout let tasks = DummyTaskExecutor::wait_tasks_for(executor, 100); // TODO: get rid of explicit timeout
assert_eq!(tasks, vec![]); assert_eq!(tasks, vec![Task::Ignore(0, dummy_id)]);
} }
#[test] #[test]
@ -549,18 +633,19 @@ pub mod tests {
let (chain, executor, server) = create_synchronization_server(); let (chain, executor, server) = create_synchronization_server();
chain.write().insert_best_block(test_data::block_h1().hash(), &test_data::block_h1()).expect("Db write error"); chain.write().insert_best_block(test_data::block_h1().hash(), &test_data::block_h1()).expect("Db write error");
// when asking for blocks hashes // when asking for blocks hashes
let dummy_id = 0;
server.serve_getblocks(0, types::GetBlocks { server.serve_getblocks(0, types::GetBlocks {
version: 0, version: 0,
block_locator_hashes: vec![test_data::genesis().hash()], block_locator_hashes: vec![test_data::genesis().hash()],
hash_stop: H256::default(), hash_stop: H256::default(),
}); }, dummy_id);
// => responds with inventory // => responds with inventory
let inventory = vec![InventoryVector { let inventory = vec![InventoryVector {
inv_type: InventoryType::MessageBlock, inv_type: InventoryType::MessageBlock,
hash: test_data::block_h1().hash(), hash: test_data::block_h1().hash(),
}]; }];
let tasks = DummyTaskExecutor::wait_tasks(executor); let tasks = DummyTaskExecutor::wait_tasks(executor);
assert_eq!(tasks, vec![Task::SendInventory(0, inventory)]); assert_eq!(tasks, vec![Task::SendInventory(0, inventory, ServerTaskIndex::Final(dummy_id))]);
} }
#[test] #[test]
@ -568,14 +653,15 @@ pub mod tests {
let (_, executor, server) = create_synchronization_server(); let (_, executor, server) = create_synchronization_server();
// when asking for blocks hashes // when asking for blocks hashes
let genesis_block_hash = test_data::genesis().hash(); let genesis_block_hash = test_data::genesis().hash();
let dummy_id = 6;
server.serve_getheaders(0, types::GetHeaders { server.serve_getheaders(0, types::GetHeaders {
version: 0, version: 0,
block_locator_hashes: vec![genesis_block_hash.clone()], block_locator_hashes: vec![genesis_block_hash.clone()],
hash_stop: H256::default(), hash_stop: H256::default(),
}); }, dummy_id);
// => no response // => no response
let tasks = DummyTaskExecutor::wait_tasks_for(executor, 100); // TODO: get rid of explicit timeout let tasks = DummyTaskExecutor::wait_tasks_for(executor, 100); // TODO: get rid of explicit timeout
assert_eq!(tasks, vec![]); assert_eq!(tasks, vec![Task::Ignore(0, dummy_id)]);
} }
#[test] #[test]
@ -583,27 +669,29 @@ pub mod tests {
let (chain, executor, server) = create_synchronization_server(); let (chain, executor, server) = create_synchronization_server();
chain.write().insert_best_block(test_data::block_h1().hash(), &test_data::block_h1()).expect("Db write error"); chain.write().insert_best_block(test_data::block_h1().hash(), &test_data::block_h1()).expect("Db write error");
// when asking for blocks hashes // when asking for blocks hashes
let dummy_id = 0;
server.serve_getheaders(0, types::GetHeaders { server.serve_getheaders(0, types::GetHeaders {
version: 0, version: 0,
block_locator_hashes: vec![test_data::genesis().hash()], block_locator_hashes: vec![test_data::genesis().hash()],
hash_stop: H256::default(), hash_stop: H256::default(),
}); }, dummy_id);
// => responds with headers // => responds with headers
let headers = vec![ let headers = vec![
test_data::block_h1().block_header, test_data::block_h1().block_header,
]; ];
let tasks = DummyTaskExecutor::wait_tasks(executor); let tasks = DummyTaskExecutor::wait_tasks(executor);
assert_eq!(tasks, vec![Task::SendHeaders(0, headers)]); assert_eq!(tasks, vec![Task::SendHeaders(0, headers, ServerTaskIndex::Final(dummy_id))]);
} }
#[test] #[test]
fn server_mempool_do_not_responds_inventory_when_empty_memory_pool() { fn server_mempool_do_not_responds_inventory_when_empty_memory_pool() {
let (_, executor, server) = create_synchronization_server(); let (_, executor, server) = create_synchronization_server();
// when asking for memory pool transactions ids // when asking for memory pool transactions ids
server.serve_mempool(0); let dummy_id = 9;
server.serve_mempool(0, dummy_id);
// => no response // => no response
let tasks = DummyTaskExecutor::wait_tasks_for(executor, 100); // TODO: get rid of explicit timeout let tasks = DummyTaskExecutor::wait_tasks_for(executor, 100); // TODO: get rid of explicit timeout
assert_eq!(tasks, vec![]); assert_eq!(tasks, vec![Task::Ignore(0, dummy_id)]);
} }
#[test] #[test]
@ -614,13 +702,14 @@ pub mod tests {
let transaction_hash = transaction.hash(); let transaction_hash = transaction.hash();
chain.write().insert_verified_transaction(transaction); chain.write().insert_verified_transaction(transaction);
// when asking for memory pool transactions ids // when asking for memory pool transactions ids
server.serve_mempool(0); let dummy_id = 0;
server.serve_mempool(0, dummy_id);
// => respond with inventory // => respond with inventory
let inventory = vec![InventoryVector { let inventory = vec![InventoryVector {
inv_type: InventoryType::MessageTx, inv_type: InventoryType::MessageTx,
hash: transaction_hash, hash: transaction_hash,
}]; }];
let tasks = DummyTaskExecutor::wait_tasks(executor); let tasks = DummyTaskExecutor::wait_tasks(executor);
assert_eq!(tasks, vec![Task::SendInventory(0, inventory)]); assert_eq!(tasks, vec![Task::SendInventory(0, inventory, ServerTaskIndex::Final(dummy_id))]);
} }
} }

View File

@ -14,3 +14,6 @@ dot -Tpng > tools/graph.png tools/graph.dot
# Finally let's bring back old Cargo.toml file # Finally let's bring back old Cargo.toml file
patch Cargo.toml tools/workspace.diff patch Cargo.toml tools/workspace.diff
# Now let's revert Cargo.lock to previous state
cargo update -p pbtc

View File

@ -121,6 +121,7 @@ digraph dependencies {
N3 -> N46[label="",style=dashed]; N3 -> N46[label="",style=dashed];
N3 -> N47[label="",style=dashed]; N3 -> N47[label="",style=dashed];
N4 -> N2[label="",style=dashed]; N4 -> N2[label="",style=dashed];
N4 -> N8[label="",style=dashed];
N4 -> N30[label="",style=dashed]; N4 -> N30[label="",style=dashed];
N4 -> N32[label="",style=dashed]; N4 -> N32[label="",style=dashed];
N4 -> N36[label="",style=dashed]; N4 -> N36[label="",style=dashed];
@ -181,6 +182,7 @@ digraph dependencies {
N13 -> N14[label="",style=dashed]; N13 -> N14[label="",style=dashed];
N13 -> N16[label="",style=dashed]; N13 -> N16[label="",style=dashed];
N13 -> N32[label="",style=dashed]; N13 -> N32[label="",style=dashed];
N13 -> N51[label="",style=dashed];
N13 -> N52[label="",style=dashed]; N13 -> N52[label="",style=dashed];
N13 -> N54[label="",style=dashed]; N13 -> N54[label="",style=dashed];
N13 -> N57[label="",style=dashed]; N13 -> N57[label="",style=dashed];
@ -189,6 +191,7 @@ digraph dependencies {
N13 -> N80[label="",style=dashed]; N13 -> N80[label="",style=dashed];
N14 -> N2[label="",style=dashed]; N14 -> N2[label="",style=dashed];
N14 -> N4[label="",style=dashed]; N14 -> N4[label="",style=dashed];
N14 -> N8[label="",style=dashed];
N14 -> N12[label="",style=dashed]; N14 -> N12[label="",style=dashed];
N14 -> N32[label="",style=dashed]; N14 -> N32[label="",style=dashed];
N14 -> N36[label="",style=dashed]; N14 -> N36[label="",style=dashed];
@ -229,6 +232,7 @@ digraph dependencies {
N43 -> N65[label="",style=dashed]; N43 -> N65[label="",style=dashed];
N49 -> N36[label="",style=dashed]; N49 -> N36[label="",style=dashed];
N49 -> N39[label="",style=dashed]; N49 -> N39[label="",style=dashed];
N51 -> N8[label="",style=dashed];
N51 -> N18[label="",style=dashed]; N51 -> N18[label="",style=dashed];
N52 -> N79[label="",style=dashed]; N52 -> N79[label="",style=dashed];
N52 -> N81[label="",style=dashed]; N52 -> N81[label="",style=dashed];

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 KiB

After

Width:  |  Height:  |  Size: 858 KiB

View File

@ -14,6 +14,7 @@ const MAX_BLOCK_SIZE: usize = 1000000;
pub struct ChainVerifier { pub struct ChainVerifier {
store: Arc<db::Store>, store: Arc<db::Store>,
verify_p2sh: bool,
verify_clocktimeverify: bool, verify_clocktimeverify: bool,
skip_pow: bool, skip_pow: bool,
skip_sig: bool, skip_sig: bool,
@ -23,6 +24,7 @@ impl ChainVerifier {
pub fn new(store: Arc<db::Store>) -> Self { pub fn new(store: Arc<db::Store>) -> Self {
ChainVerifier { ChainVerifier {
store: store, store: store,
verify_p2sh: false,
verify_clocktimeverify: false, verify_clocktimeverify: false,
skip_pow: false, skip_pow: false,
skip_sig: false skip_sig: false
@ -41,6 +43,11 @@ impl ChainVerifier {
self self
} }
pub fn verify_p2sh(mut self, verify: bool) -> Self {
self.verify_p2sh = verify;
self
}
pub fn verify_clocktimeverify(mut self, verify: bool) -> Self { pub fn verify_clocktimeverify(mut self, verify: bool) -> Self {
self.verify_clocktimeverify = verify; self.verify_clocktimeverify = verify;
self self
@ -141,7 +148,7 @@ impl ChainVerifier {
let output: Script = paired_output.script_pubkey.to_vec().into(); let output: Script = paired_output.script_pubkey.to_vec().into();
let flags = VerificationFlags::default() let flags = VerificationFlags::default()
.verify_p2sh(true) .verify_p2sh(self.verify_p2sh)
.verify_clocktimeverify(self.verify_clocktimeverify); .verify_clocktimeverify(self.verify_clocktimeverify);
// for tests only, skips as late as possible // for tests only, skips as late as possible

View File

@ -157,7 +157,7 @@ impl Queue {
items.push_front(hash, ScheduleItem::Continued(item.block(), num)); items.push_front(hash, ScheduleItem::Continued(item.block(), num));
}, },
Err(e) => { Err(e) => {
trace!(target: "verification", "Verification of block {} failed: {:?}", &hash, e); trace!(target: "verification", "Verification of block {} failed: {:?}", hash.to_reversed_str(), e);
let mut invalid = self.invalid.write(); let mut invalid = self.invalid.write();
let mut processing = self.processing.write(); let mut processing = self.processing.write();

View File

@ -50,14 +50,14 @@ pub fn age(protocol_time: u32) -> i64 {
pub fn block_reward_satoshi(block_height: u32) -> u64 { pub fn block_reward_satoshi(block_height: u32) -> u64 {
let mut res = 50 * 100 * 1000 * 1000; let mut res = 50 * 100 * 1000 * 1000;
for _ in 0..block_height / 210000 { res = res / 2 } for _ in 0..block_height / 210000 { res /= 2 }
res res
} }
pub fn transaction_sigops(transaction: &chain::Transaction) -> Result<usize, script::Error> { pub fn transaction_sigops(transaction: &chain::Transaction) -> Result<usize, script::Error> {
let mut result = 0usize; let mut result = 0usize;
for output in transaction.outputs.iter() { for output in &transaction.outputs {
let output_script: Script = output.script_pubkey.to_vec().into(); let output_script: Script = output.script_pubkey.to_vec().into();
// todo: not always allow malformed output? // todo: not always allow malformed output?
result += output_script.sigop_count(false).unwrap_or(0); result += output_script.sigop_count(false).unwrap_or(0);
@ -65,7 +65,7 @@ pub fn transaction_sigops(transaction: &chain::Transaction) -> Result<usize, scr
if transaction.is_coinbase() { return Ok(result); } if transaction.is_coinbase() { return Ok(result); }
for input in transaction.inputs.iter() { for input in &transaction.inputs {
let input_script: Script = input.script_sig().to_vec().into(); let input_script: Script = input.script_sig().to_vec().into();
result += try!(input_script.sigop_count(false)); result += try!(input_script.sigop_count(false));
} }