Merge branch 'master' into sync_cmpctblk

This commit is contained in:
Svyatoslav Nikolsky 2016-11-27 17:53:34 +03:00
commit d331dfde32
61 changed files with 846 additions and 261 deletions

15
Cargo.lock generated
View File

@ -8,6 +8,7 @@ dependencies = [
"ethcore-devtools 1.3.0", "ethcore-devtools 1.3.0",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"network 0.1.0",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
"script 0.1.0", "script 0.1.0",
@ -329,6 +330,7 @@ dependencies = [
"bitcrypto 0.1.0", "bitcrypto 0.1.0",
"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)",
"chain 0.1.0", "chain 0.1.0",
"network 0.1.0",
"primitives 0.1.0", "primitives 0.1.0",
"serialization 0.1.0", "serialization 0.1.0",
] ]
@ -338,6 +340,7 @@ name = "miner"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chain 0.1.0", "chain 0.1.0",
"db 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",
"serialization 0.1.0", "serialization 0.1.0",
@ -391,6 +394,14 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "network"
version = "0.1.0"
dependencies = [
"chain 0.1.0",
"serialization 0.1.0",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.7.0" version = "0.7.0"
@ -461,6 +472,7 @@ dependencies = [
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"message 0.1.0", "message 0.1.0",
"network 0.1.0",
"ns-dns-tokio 0.1.0 (git+https://github.com/debris/abstract-ns)", "ns-dns-tokio 0.1.0 (git+https://github.com/debris/abstract-ns)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
@ -506,6 +518,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"message 0.1.0", "message 0.1.0",
"miner 0.1.0", "miner 0.1.0",
"network 0.1.0",
"p2p 0.1.0", "p2p 0.1.0",
"script 0.1.0", "script 0.1.0",
"sync 0.1.0", "sync 0.1.0",
@ -681,6 +694,7 @@ dependencies = [
"message 0.1.0", "message 0.1.0",
"miner 0.1.0", "miner 0.1.0",
"murmur3 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "murmur3 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"network 0.1.0",
"p2p 0.1.0", "p2p 0.1.0",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"primitives 0.1.0", "primitives 0.1.0",
@ -710,6 +724,7 @@ dependencies = [
"chain 0.1.0", "chain 0.1.0",
"primitives 0.1.0", "primitives 0.1.0",
"serialization 0.1.0", "serialization 0.1.0",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]

View File

@ -13,6 +13,7 @@ clap = { version = "2", features = ["yaml"] }
chain = { path = "chain" } chain = { path = "chain" }
keys = { path = "keys" } keys = { path = "keys" }
message = { path = "message" } message = { path = "message" }
network = { path = "network" }
miner = { path = "miner" } miner = { path = "miner" }
p2p = { path = "p2p" } p2p = { path = "p2p" }
script = { path = "script" } script = { path = "script" }

View File

@ -14,7 +14,7 @@ pub trait RepresentH256 {
} }
pub use rustc_serialize::hex; pub use rustc_serialize::hex;
pub use primitives::{hash, bytes}; pub use primitives::{hash, bytes, uint};
pub use self::block::Block; pub use self::block::Block;
pub use self::block_header::BlockHeader; pub use self::block_header::BlockHeader;

View File

@ -14,6 +14,9 @@ pub trait BlockProvider {
/// resolves header bytes by block reference (number/hash) /// resolves header bytes by block reference (number/hash)
fn block_header_bytes(&self, block_ref: BlockRef) -> Option<Bytes>; fn block_header_bytes(&self, block_ref: BlockRef) -> Option<Bytes>;
/// resolves header bytes by block reference (number/hash)
fn block_header(&self, block_ref: BlockRef) -> Option<chain::BlockHeader>;
/// resolves deserialized block body by block reference (number/hash) /// resolves deserialized block body by block reference (number/hash)
fn block(&self, block_ref: BlockRef) -> Option<chain::Block>; fn block(&self, block_ref: BlockRef) -> Option<chain::Block>;

View File

@ -34,6 +34,18 @@ pub enum BlockRef {
Hash(primitives::hash::H256), Hash(primitives::hash::H256),
} }
impl From<u32> for BlockRef {
fn from(u: u32) -> Self {
BlockRef::Number(u)
}
}
impl From<primitives::hash::H256> for BlockRef {
fn from(hash: primitives::hash::H256) -> Self {
BlockRef::Hash(hash)
}
}
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub enum BlockLocation { pub enum BlockLocation {
Main(u32), Main(u32),
@ -46,7 +58,7 @@ pub use best_block::BestBlock;
pub use storage::{Storage, Store}; pub use storage::{Storage, Store};
pub use error::Error; pub use error::Error;
pub use kvdb::Database; pub use kvdb::Database;
pub use transaction_provider::TransactionProvider; pub use transaction_provider::{TransactionProvider, AsTransactionProvider};
pub use transaction_meta_provider::TransactionMetaProvider; pub use transaction_meta_provider::TransactionMetaProvider;
pub use block_stapler::{BlockStapler, BlockInsertedChain}; pub use block_stapler::{BlockStapler, BlockInsertedChain};
pub use block_provider::BlockProvider; pub use block_provider::BlockProvider;

View File

@ -40,6 +40,9 @@ const MAX_FORK_ROUTE_PRESET: usize = 128;
pub trait Store : BlockProvider + BlockStapler + TransactionProvider + TransactionMetaProvider { pub trait Store : BlockProvider + BlockStapler + TransactionProvider + TransactionMetaProvider {
/// get best block /// get best block
fn best_block(&self) -> Option<BestBlock>; fn best_block(&self) -> Option<BestBlock>;
/// get best header
fn best_header(&self) -> Option<chain::BlockHeader>;
} }
/// Blockchain storage with rocksdb database /// Blockchain storage with rocksdb database
@ -165,7 +168,6 @@ impl Storage {
}) })
} }
/// update transactions metadata in the specified database transaction /// update transactions metadata in the specified database transaction
fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[chain::Transaction]) fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[chain::Transaction])
-> Result<(), Error> -> Result<(), Error>
@ -190,7 +192,7 @@ impl Storage {
return Err(Error::double_spend(&input.previous_output.hash)); return Err(Error::double_spend(&input.previous_output.hash));
} }
meta.note_used(input.previous_output.index as usize); meta.denote_used(input.previous_output.index as usize);
true true
}, },
None => false, None => false,
@ -202,7 +204,7 @@ impl Storage {
return Err(Error::double_spend(&input.previous_output.hash)); return Err(Error::double_spend(&input.previous_output.hash));
} }
meta.note_used(input.previous_output.index as usize); meta.denote_used(input.previous_output.index as usize);
context.meta.insert( context.meta.insert(
input.previous_output.hash.clone(), input.previous_output.hash.clone(),
@ -241,7 +243,7 @@ impl Storage {
for input in &tx.inputs { for input in &tx.inputs {
if !match context.meta.get_mut(&input.previous_output.hash) { if !match context.meta.get_mut(&input.previous_output.hash) {
Some(ref mut meta) => { Some(ref mut meta) => {
meta.denote_used(input.previous_output.index as usize); meta.denote_unused(input.previous_output.index as usize);
true true
}, },
None => false, None => false,
@ -255,7 +257,7 @@ impl Storage {
&input.previous_output.hash &input.previous_output.hash
)); ));
meta.denote_used(input.previous_output.index as usize); meta.denote_unused(input.previous_output.index as usize);
context.meta.insert( context.meta.insert(
input.previous_output.hash.clone(), input.previous_output.hash.clone(),
@ -385,6 +387,12 @@ impl BlockProvider for Storage {
self.resolve_hash(block_ref).and_then(|h| self.get(COL_BLOCK_HEADERS, &*h)) self.resolve_hash(block_ref).and_then(|h| self.get(COL_BLOCK_HEADERS, &*h))
} }
fn block_header(&self, block_ref: BlockRef) -> Option<chain::BlockHeader> {
self.block_header_bytes(block_ref).map(
|bytes| deserialize::<_, chain::BlockHeader>(bytes.as_ref())
.expect("Error deserializing header, possible db corruption"))
}
fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> { fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> {
self.resolve_hash(block_ref) self.resolve_hash(block_ref)
.map(|h| self.block_transaction_hashes_by_hash(&h)) .map(|h| self.block_transaction_hashes_by_hash(&h))
@ -596,6 +604,12 @@ impl Store for Storage {
fn best_block(&self) -> Option<BestBlock> { fn best_block(&self) -> Option<BestBlock> {
self.best_block.read().clone() self.best_block.read().clone()
} }
fn best_header(&self) -> Option<chain::BlockHeader> {
self.best_block.read().as_ref().and_then(
|bb| Some(self.block_header_by_hash(&bb.hash).expect("Best block exists but no such header. Race condition?")),
)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,7 +2,7 @@
use super::{ use super::{
BlockRef, Store, Error, BestBlock, BlockLocation, BlockInsertedChain, BlockProvider, BlockRef, Store, Error, BestBlock, BlockLocation, BlockInsertedChain, BlockProvider,
BlockStapler, TransactionMetaProvider, TransactionProvider, BlockStapler, TransactionMetaProvider, TransactionProvider, AsTransactionProvider
}; };
use chain::{self, Block}; use chain::{self, Block};
use primitives::hash::H256; use primitives::hash::H256;
@ -81,6 +81,13 @@ impl BlockProvider for TestStorage {
.map(|ref block| serialization::serialize(block.header())) .map(|ref block| serialization::serialize(block.header()))
} }
fn block_header(&self, block_ref: BlockRef) -> Option<chain::BlockHeader> {
let data = self.data.read();
self.resolve_hash(block_ref)
.and_then(|ref h| data.blocks.get(h))
.map(|ref block| block.header().clone())
}
fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> { fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> {
let data = self.data.read(); let data = self.data.read();
self.resolve_hash(block_ref) self.resolve_hash(block_ref)
@ -163,6 +170,12 @@ impl TransactionProvider for TestStorage {
} }
} }
impl AsTransactionProvider for TestStorage {
fn as_transaction_provider(&self) -> &TransactionProvider {
&*self
}
}
impl TransactionMetaProvider for TestStorage { impl TransactionMetaProvider for TestStorage {
// just spawns new meta so far, use real store for proper tests // just spawns new meta so far, use real store for proper tests
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta> { fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta> {
@ -174,5 +187,11 @@ impl Store for TestStorage {
fn best_block(&self) -> Option<BestBlock> { fn best_block(&self) -> Option<BestBlock> {
self.data.read().best_block.clone() self.data.read().best_block.clone()
} }
fn best_header(&self) -> Option<chain::BlockHeader> {
self.data.read().best_block.as_ref().and_then(
|bb| Some(self.block_header(BlockRef::Hash(bb.hash.clone())).expect("Best block exists but no such header. Race condition?"))
)
}
} }

View File

@ -25,11 +25,6 @@ impl TransactionMeta {
} }
} }
/// note that particular output has been used
pub fn note_used(&mut self, index: usize) {
self.bits.set(index + 1 , true);
}
pub fn coinbase(mut self) -> Self { pub fn coinbase(mut self) -> Self {
self.bits.set(0, true); self.bits.set(0, true);
self self
@ -40,8 +35,13 @@ impl TransactionMeta {
.expect("One bit should always exists, since it is created as usize + 1; minimum value of usize is 0; 0 + 1 = 1; qed") .expect("One bit should always exists, since it is created as usize + 1; minimum value of usize is 0; 0 + 1 = 1; qed")
} }
/// note that particular output has been used /// denote particular output as used
pub fn denote_used(&mut self, index: usize) { pub fn denote_used(&mut self, index: usize) {
self.bits.set(index + 1 , true);
}
/// denote particular output as not used
pub fn denote_unused(&mut self, index: usize) {
self.bits.set(index + 1, false); self.bits.set(index + 1, false);
} }

View File

@ -16,3 +16,8 @@ pub trait TransactionProvider {
fn transaction(&self, hash: &H256) -> Option<chain::Transaction>; fn transaction(&self, hash: &H256) -> Option<chain::Transaction>;
} }
pub trait AsTransactionProvider {
/// returns `TransactionProvider`
fn as_transaction_provider(&self) -> &TransactionProvider;
}

View File

@ -10,3 +10,4 @@ bitcrypto = { path = "../crypto" }
chain = { path = "../chain" } chain = { path = "../chain" }
primitives = { path = "../primitives" } primitives = { path = "../primitives" }
serialization = { path = "../serialization" } serialization = { path = "../serialization" }
network = { path = "../network" }

View File

@ -3,10 +3,8 @@ mod block_header_and_ids;
mod block_transactions; mod block_transactions;
mod block_transactions_request; mod block_transactions_request;
mod command; mod command;
mod consensus;
mod inventory; mod inventory;
mod ip; mod ip;
mod magic;
mod port; mod port;
mod prefilled_transaction; mod prefilled_transaction;
mod service; mod service;
@ -16,10 +14,8 @@ pub use self::block_header_and_ids::BlockHeaderAndIDs;
pub use self::block_transactions::BlockTransactions; pub use self::block_transactions::BlockTransactions;
pub use self::block_transactions_request::BlockTransactionsRequest; pub use self::block_transactions_request::BlockTransactionsRequest;
pub use self::command::Command; pub use self::command::Command;
pub use self::consensus::ConsensusParams;
pub use self::inventory::{InventoryVector, InventoryType}; pub use self::inventory::{InventoryVector, InventoryType};
pub use self::ip::IpAddress; pub use self::ip::IpAddress;
pub use self::magic::Magic;
pub use self::port::Port; pub use self::port::Port;
pub use self::prefilled_transaction::PrefilledTransaction; pub use self::prefilled_transaction::PrefilledTransaction;
pub use self::service::Services; pub use self::service::Services;

View File

@ -9,10 +9,8 @@ pub enum Error {
Deserialize, Deserialize,
/// Command has wrong format or is unsupported. /// Command has wrong format or is unsupported.
InvalidCommand, InvalidCommand,
/// Network magic is not supported.
InvalidMagic,
/// Network magic comes from different network. /// Network magic comes from different network.
WrongMagic, InvalidMagic,
/// Invalid checksum. /// Invalid checksum.
InvalidChecksum, InvalidChecksum,
/// Invalid version. /// Invalid version.
@ -37,7 +35,6 @@ impl error::Error for Error {
Error::Deserialize => "Message Deserialization Error", Error::Deserialize => "Message Deserialization Error",
Error::InvalidCommand => "Invalid Message Command", Error::InvalidCommand => "Invalid Message Command",
Error::InvalidMagic => "Invalid Network Magic", Error::InvalidMagic => "Invalid Network Magic",
Error::WrongMagic => "Wrong Network Magic",
Error::InvalidChecksum => "Invalid message chacksum", Error::InvalidChecksum => "Invalid message chacksum",
Error::InvalidVersion => "Unsupported protocol version", Error::InvalidVersion => "Unsupported protocol version",
} }

View File

@ -3,6 +3,7 @@ extern crate bitcrypto as crypto;
extern crate chain; extern crate chain;
extern crate primitives; extern crate primitives;
extern crate serialization as ser; extern crate serialization as ser;
extern crate network;
pub mod common; pub mod common;
mod message; mod message;
@ -12,7 +13,7 @@ mod error;
pub use primitives::{hash, bytes}; pub use primitives::{hash, bytes};
pub use common::{Command, Magic, Services}; pub use common::{Command, Services};
pub use message::{Message, MessageHeader, Payload, to_raw_message}; pub use message::{Message, MessageHeader, Payload, to_raw_message};
pub use serialization::{serialize_payload, deserialize_payload}; pub use serialization::{serialize_payload, deserialize_payload};
pub use error::{Error, MessageResult}; pub use error::{Error, MessageResult};

View File

@ -1,6 +1,7 @@
use ser::Stream; use ser::Stream;
use bytes::{TaggedBytes, Bytes}; use bytes::{TaggedBytes, Bytes};
use common::{Magic, Command}; use network::Magic;
use common::Command;
use serialization::serialize_payload; use serialization::serialize_payload;
use {Payload, MessageResult, MessageHeader}; use {Payload, MessageResult, MessageHeader};

View File

@ -1,7 +1,8 @@
use hash::H32; use hash::H32;
use ser::{Serializable, Stream, Reader}; use ser::{Serializable, Stream, Reader};
use crypto::checksum; use crypto::checksum;
use common::{Command, Magic}; use network::Magic;
use common::Command;
use Error; use Error;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -31,9 +32,9 @@ impl MessageHeader {
let mut reader = Reader::new(data); let mut reader = Reader::new(data);
let magic: u32 = try!(reader.read()); let magic: u32 = try!(reader.read());
let magic = try!(Magic::from_u32(magic)); let magic = Magic::from(magic);
if expected != magic { if expected != magic {
return Err(Error::WrongMagic); return Err(Error::InvalidMagic);
} }
let header = MessageHeader { let header = MessageHeader {
@ -61,7 +62,7 @@ impl Serializable for MessageHeader {
mod tests { mod tests {
use bytes::Bytes; use bytes::Bytes;
use ser::serialize; use ser::serialize;
use common::Magic; use network::Magic;
use super::MessageHeader; use super::MessageHeader;
#[test] #[test]

View File

@ -6,6 +6,7 @@ authors = ["Ethcore <admin@ethcore.io>"]
[dependencies] [dependencies]
heapsize = "0.3" heapsize = "0.3"
chain = { path = "../chain" } chain = { path = "../chain" }
db = { path = "../db" }
primitives = { path = "../primitives" } primitives = { path = "../primitives" }
serialization = { path = "../serialization" } serialization = { path = "../serialization" }
test-data = { path = "../test-data" } test-data = { path = "../test-data" }

61
miner/src/fee.rs Normal file
View File

@ -0,0 +1,61 @@
use chain::Transaction;
use db::TransactionProvider;
pub fn transaction_fee(store: &TransactionProvider, transaction: &Transaction) -> u64 {
let inputs_sum = transaction.inputs.iter()
.fold(0, |accumulator, input| {
let input_transaction = store.transaction(&input.previous_output.hash)
.expect("transaction must be verified by caller");
accumulator + input_transaction.outputs[input.previous_output.index as usize].value
});
let outputs_sum = transaction.outputs.iter().map(|output| output.value).sum();
inputs_sum.saturating_sub(outputs_sum)
}
pub fn transaction_fee_rate(store: &TransactionProvider, transaction: &Transaction) -> u64 {
use ser::Serializable;
transaction_fee(store, transaction) / transaction.serialized_size() as u64
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use db::{TestStorage, AsTransactionProvider};
use test_data;
use super::*;
#[test]
fn test_transaction_fee() {
let b0 = test_data::block_builder().header().nonce(1).build()
.transaction()
.output().value(1_000_000).build()
.build()
.transaction()
.output().value(2_000_000).build()
.build()
.build();
let tx0 = b0.transactions[0].clone();
let tx0_hash = tx0.hash();
let tx1 = b0.transactions[1].clone();
let tx1_hash = tx1.hash();
let b1 = test_data::block_builder().header().nonce(2).build()
.transaction()
.input().hash(tx0_hash).index(0).build()
.input().hash(tx1_hash).index(0).build()
.output().value(2_500_000).build()
.build()
.build();
let tx2 = b1.transactions[0].clone();
let db = Arc::new(TestStorage::with_blocks(&vec![b0, b1]));
assert_eq!(transaction_fee(db.as_transaction_provider(), &tx0), 0);
assert_eq!(transaction_fee(db.as_transaction_provider(), &tx1), 0);
assert_eq!(transaction_fee(db.as_transaction_provider(), &tx2), 500_000);
assert_eq!(transaction_fee_rate(db.as_transaction_provider(), &tx0), 0);
assert_eq!(transaction_fee_rate(db.as_transaction_provider(), &tx1), 0);
assert_eq!(transaction_fee_rate(db.as_transaction_provider(), &tx2), 4_950);
}
}

View File

@ -1,9 +1,12 @@
extern crate chain; extern crate chain;
extern crate db;
extern crate heapsize; extern crate heapsize;
extern crate primitives; extern crate primitives;
extern crate serialization as ser; extern crate serialization as ser;
extern crate test_data; extern crate test_data;
mod fee;
mod memory_pool; mod memory_pool;
pub use fee::{transaction_fee, transaction_fee_rate};
pub use memory_pool::{MemoryPool, Information as MemoryPoolInformation, OrderingStrategy as MemoryPoolOrderingStrategy}; pub use memory_pool::{MemoryPool, Information as MemoryPoolInformation, OrderingStrategy as MemoryPoolOrderingStrategy};

View File

@ -5,13 +5,15 @@
//! transactions. //! transactions.
//! It also guarantees that ancestor-descendant relation won't break during ordered removal (ancestors always removed //! It also guarantees that ancestor-descendant relation won't break during ordered removal (ancestors always removed
//! before descendants). Removal using `remove_by_hash` can break this rule. //! before descendants). Removal using `remove_by_hash` can break this rule.
use db::TransactionProvider;
use primitives::bytes::Bytes;
use primitives::hash::H256; use primitives::hash::H256;
use chain::Transaction; use chain::Transaction;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use ser::Serializable; use ser::{Serializable, serialize};
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
/// Transactions ordering strategy /// Transactions ordering strategy
@ -684,6 +686,16 @@ impl MemoryPool {
} }
} }
impl TransactionProvider for MemoryPool {
fn transaction_bytes(&self, hash: &H256) -> Option<Bytes> {
self.get(hash).map(|t| serialize(t))
}
fn transaction(&self, hash: &H256) -> Option<Transaction> {
self.get(hash).cloned()
}
}
impl HeapSizeOf for MemoryPool { impl HeapSizeOf for MemoryPool {
fn heap_size_of_children(&self) -> usize { fn heap_size_of_children(&self) -> usize {
self.storage.heap_size_of_children() self.storage.heap_size_of_children()

8
network/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "network"
version = "0.1.0"
authors = ["debris <marek.kotewicz@gmail.com>"]
[dependencies]
serialization = { path = "../serialization" }
chain = { path = "../chain" }

View File

@ -14,7 +14,7 @@ pub struct ConsensusParams {
impl ConsensusParams { 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 | Magic::Other(_) => ConsensusParams {
bip16_time: 1333238400, // Apr 1 2012 bip16_time: 1333238400, // Apr 1 2012
bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
}, },

8
network/src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
extern crate chain;
extern crate serialization as ser;
mod consensus;
mod magic;
pub use consensus::ConsensusParams;
pub use magic::Magic;

View File

@ -3,21 +3,28 @@
use ser::{Stream, Serializable}; use ser::{Stream, Serializable};
use chain::Block; use chain::Block;
use Error;
use super::ConsensusParams; use super::ConsensusParams;
const MAGIC_MAINNET: u32 = 0xD9B4BEF9; const MAGIC_MAINNET: u32 = 0xD9B4BEF9;
const MAGIC_TESTNET: u32 = 0x0709110B; const MAGIC_TESTNET: u32 = 0x0709110B;
const MAGIC_REGTEST: u32 = 0xDAB5BFFA; const MAGIC_REGTEST: u32 = 0xDAB5BFFA;
const MAX_NBITS_MAINNET: u32 = 0x1d00ffff;
const MAX_NBITS_TESTNET: u32 = 0x1d00ffff;
const MAX_NBITS_REGTEST: u32 = 0x207fffff;
/// Bitcoin network /// Bitcoin network
/// https://bitcoin.org/en/glossary/mainnet /// https://bitcoin.org/en/glossary/mainnet
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Magic { pub enum Magic {
/// The original and main network for Bitcoin transactions, where satoshis have real economic value. /// The original and main network for Bitcoin transactions, where satoshis have real economic value.
Mainnet, Mainnet,
/// The main bitcoin testnet.
Testnet, Testnet,
/// Bitcoin regtest network.
Regtest, Regtest,
/// Any other network. By default behaves like bitcoin mainnet.
Other(u32),
} }
impl From<Magic> for u32 { impl From<Magic> for u32 {
@ -26,23 +33,34 @@ impl From<Magic> for u32 {
Magic::Mainnet => MAGIC_MAINNET, Magic::Mainnet => MAGIC_MAINNET,
Magic::Testnet => MAGIC_TESTNET, Magic::Testnet => MAGIC_TESTNET,
Magic::Regtest => MAGIC_REGTEST, Magic::Regtest => MAGIC_REGTEST,
Magic::Other(magic) => magic,
}
}
}
impl From<u32> for Magic {
fn from(u: u32) -> Self {
match u {
MAGIC_MAINNET => Magic::Mainnet,
MAGIC_TESTNET => Magic::Testnet,
MAGIC_REGTEST => Magic::Regtest,
other => Magic::Other(other),
} }
} }
} }
impl Magic { impl Magic {
pub fn from_u32(magic: u32) -> Result<Self, Error> { pub fn max_nbits(&self) -> u32 {
match magic { match *self {
MAGIC_MAINNET => Ok(Magic::Mainnet), Magic::Mainnet | Magic::Other(_) => MAX_NBITS_MAINNET,
MAGIC_TESTNET => Ok(Magic::Testnet), Magic::Testnet => MAX_NBITS_TESTNET,
MAGIC_REGTEST => Ok(Magic::Regtest), Magic::Regtest => MAX_NBITS_REGTEST,
_ => Err(Error::InvalidMagic),
} }
} }
pub fn port(&self) -> u16 { pub fn port(&self) -> u16 {
match *self { match *self {
Magic::Mainnet => 8333, Magic::Mainnet | Magic::Other(_) => 8333,
Magic::Testnet => 18333, Magic::Testnet => 18333,
Magic::Regtest => 18444, Magic::Regtest => 18444,
} }
@ -50,7 +68,7 @@ impl Magic {
pub fn rpc_port(&self) -> u16 { pub fn rpc_port(&self) -> u16 {
match *self { match *self {
Magic::Mainnet => 8332, Magic::Mainnet | Magic::Other(_) => 8332,
Magic::Testnet => 18332, Magic::Testnet => 18332,
Magic::Regtest => 18443, Magic::Regtest => 18443,
} }
@ -58,7 +76,7 @@ impl Magic {
pub fn genesis_block(&self) -> Block { pub fn genesis_block(&self) -> Block {
match *self { match *self {
Magic::Mainnet => "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(), Magic::Mainnet | Magic::Other(_) => "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
Magic::Testnet => "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(), Magic::Testnet => "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
Magic::Regtest => "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f20020000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(), Magic::Regtest => "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f20020000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),
} }
@ -77,18 +95,27 @@ impl Serializable for Magic {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use Error; use super::{
use super::{Magic, MAGIC_MAINNET, MAGIC_TESTNET, MAGIC_REGTEST}; Magic, MAGIC_MAINNET, MAGIC_TESTNET, MAGIC_REGTEST,
MAX_NBITS_MAINNET, MAX_NBITS_TESTNET, MAX_NBITS_REGTEST,
};
#[test] #[test]
fn test_network_magic_number() { fn test_network_magic_number() {
assert_eq!(MAGIC_MAINNET, Magic::Mainnet.into()); assert_eq!(MAGIC_MAINNET, Magic::Mainnet.into());
assert_eq!(MAGIC_TESTNET, Magic::Testnet.into()); assert_eq!(MAGIC_TESTNET, Magic::Testnet.into());
assert_eq!(MAGIC_REGTEST, Magic::Regtest.into()); assert_eq!(MAGIC_REGTEST, Magic::Regtest.into());
assert_eq!(Magic::from_u32(MAGIC_MAINNET).unwrap(), Magic::Mainnet); assert_eq!(Magic::Mainnet, MAGIC_MAINNET.into());
assert_eq!(Magic::from_u32(MAGIC_TESTNET).unwrap(), Magic::Testnet); assert_eq!(Magic::Testnet, MAGIC_TESTNET.into());
assert_eq!(Magic::from_u32(MAGIC_REGTEST).unwrap(), Magic::Regtest); assert_eq!(Magic::Regtest, MAGIC_REGTEST.into());
assert_eq!(Magic::from_u32(0).unwrap_err(), Error::InvalidMagic); assert_eq!(Magic::Other(0), 0.into());
}
#[test]
fn test_network_max_nbits() {
assert_eq!(Magic::Mainnet.max_nbits(), MAX_NBITS_MAINNET);
assert_eq!(Magic::Testnet.max_nbits(), MAX_NBITS_TESTNET);
assert_eq!(Magic::Regtest.max_nbits(), MAX_NBITS_REGTEST);
} }
#[test] #[test]

View File

@ -15,7 +15,8 @@ abstract-ns = "0.2.1"
ns-dns-tokio = { git = "https://github.com/debris/abstract-ns", path = "ns-dns-tokio" } ns-dns-tokio = { git = "https://github.com/debris/abstract-ns", path = "ns-dns-tokio" }
csv = "0.14.7" csv = "0.14.7"
primitives = { path = "../primitives"} primitives = { path = "../primitives" }
bitcrypto = { path = "../crypto" } bitcrypto = { path = "../crypto" }
message = { path = "../message" } message = { path = "../message" }
serialization = { path = "../serialization"} serialization = { path = "../serialization" }
network = { path = "../network" }

View File

@ -2,7 +2,7 @@ use std::{io, cmp};
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use message::{Message, MessageResult, Error}; use message::{Message, MessageResult, Error};
use message::types::{Version, Verack}; use message::types::{Version, Verack};
use message::common::Magic; use network::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, min_version: u32) -> 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 {
@ -199,7 +199,8 @@ mod tests {
use futures::Future; use futures::Future;
use bytes::Bytes; use bytes::Bytes;
use ser::Stream; use ser::Stream;
use message::{Magic, Message}; use network::Magic;
use message::Message;
use message::types::Verack; use message::types::Verack;
use message::types::version::{Version, V0, V106, V70001}; use message::types::version::{Version, V0, V106, V70001};
use super::{handshake, accept_handshake, HandshakeResult}; use super::{handshake, accept_handshake, HandshakeResult};

View File

@ -2,7 +2,8 @@ use std::io;
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use tokio_core::io::{read_exact, ReadExact}; use tokio_core::io::{read_exact, ReadExact};
use crypto::checksum; use crypto::checksum;
use message::{Error, MessageHeader, MessageResult, Magic, Command}; use network::Magic;
use message::{Error, MessageHeader, MessageResult, Command};
use bytes::Bytes; use bytes::Bytes;
use io::{read_header, ReadHeader}; use io::{read_header, ReadHeader};
@ -68,7 +69,8 @@ impl<A> Future for ReadAnyMessage<A> where A: io::Read {
mod tests { mod tests {
use futures::Future; use futures::Future;
use bytes::Bytes; use bytes::Bytes;
use message::{Magic, Error}; use network::Magic;
use message::Error;
use super::read_any_message; use super::read_any_message;
#[test] #[test]
@ -79,7 +81,7 @@ mod tests {
let expected = (name, nonce); let expected = (name, nonce);
assert_eq!(read_any_message(raw.as_ref(), Magic::Mainnet).wait().unwrap(), Ok(expected)); assert_eq!(read_any_message(raw.as_ref(), Magic::Mainnet).wait().unwrap(), Ok(expected));
assert_eq!(read_any_message(raw.as_ref(), Magic::Testnet).wait().unwrap(), Err(Error::WrongMagic)); assert_eq!(read_any_message(raw.as_ref(), Magic::Testnet).wait().unwrap(), Err(Error::InvalidMagic));
} }
#[test] #[test]

View File

@ -1,7 +1,8 @@
use std::io; use std::io;
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use tokio_core::io::{ReadExact, read_exact}; use tokio_core::io::{ReadExact, read_exact};
use message::{MessageHeader, MessageResult, Magic}; use message::{MessageHeader, MessageResult};
use network::Magic;
pub fn read_header<A>(a: A, magic: Magic) -> ReadHeader<A> where A: io::Read { pub fn read_header<A>(a: A, magic: Magic) -> ReadHeader<A> where A: io::Read {
ReadHeader { ReadHeader {
@ -30,7 +31,8 @@ impl<A> Future for ReadHeader<A> where A: io::Read {
mod tests { mod tests {
use futures::Future; use futures::Future;
use bytes::Bytes; use bytes::Bytes;
use message::{Magic, MessageHeader, Error}; use network::Magic;
use message::{MessageHeader, Error};
use super::read_header; use super::read_header;
#[test] #[test]
@ -44,7 +46,7 @@ mod tests {
}; };
assert_eq!(read_header(raw.as_ref(), Magic::Mainnet).wait().unwrap().1, Ok(expected)); assert_eq!(read_header(raw.as_ref(), Magic::Mainnet).wait().unwrap().1, Ok(expected));
assert_eq!(read_header(raw.as_ref(), Magic::Testnet).wait().unwrap().1, Err(Error::WrongMagic)); assert_eq!(read_header(raw.as_ref(), Magic::Testnet).wait().unwrap().1, Err(Error::InvalidMagic));
} }
#[test] #[test]

View File

@ -1,7 +1,8 @@
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use futures::{Poll, Future, Async}; use futures::{Poll, Future, Async};
use message::{MessageResult, Error, Magic, Payload}; use network::Magic;
use message::{MessageResult, Error, Payload};
use io::{read_header, ReadHeader, read_payload, ReadPayload}; use io::{read_header, ReadHeader, read_payload, ReadPayload};
pub fn read_message<M, A>(a: A, magic: Magic, version: u32) -> ReadMessage<M, A> pub fn read_message<M, A>(a: A, magic: Magic, version: u32) -> ReadMessage<M, A>
@ -74,7 +75,8 @@ impl<M, A> Future for ReadMessage<M, A> where A: io::Read, M: Payload {
mod tests { mod tests {
use futures::Future; use futures::Future;
use bytes::Bytes; use bytes::Bytes;
use message::{Magic, Error}; use network::Magic;
use message::Error;
use message::types::{Ping, Pong}; use message::types::{Ping, Pong};
use super::read_message; use super::read_message;
@ -83,7 +85,7 @@ mod tests {
let raw: Bytes = "f9beb4d970696e6700000000000000000800000083c00c765845303b6da97786".into(); let raw: Bytes = "f9beb4d970696e6700000000000000000800000083c00c765845303b6da97786".into();
let ping = Ping::new(u64::from_str_radix("8677a96d3b304558", 16).unwrap()); let ping = Ping::new(u64::from_str_radix("8677a96d3b304558", 16).unwrap());
assert_eq!(read_message(raw.as_ref(), Magic::Mainnet, 0).wait().unwrap().1, Ok(ping)); assert_eq!(read_message(raw.as_ref(), Magic::Mainnet, 0).wait().unwrap().1, Ok(ping));
assert_eq!(read_message::<Ping, _>(raw.as_ref(), Magic::Testnet, 0).wait().unwrap().1, Err(Error::WrongMagic)); assert_eq!(read_message::<Ping, _>(raw.as_ref(), Magic::Testnet, 0).wait().unwrap().1, Err(Error::InvalidMagic));
assert_eq!(read_message::<Pong, _>(raw.as_ref(), Magic::Mainnet, 0).wait().unwrap().1, Err(Error::InvalidCommand)); assert_eq!(read_message::<Pong, _>(raw.as_ref(), Magic::Mainnet, 0).wait().unwrap().1, Err(Error::InvalidCommand));
} }

View File

@ -15,6 +15,7 @@ extern crate bitcrypto as crypto;
extern crate message; extern crate message;
extern crate primitives; extern crate primitives;
extern crate serialization as ser; extern crate serialization as ser;
extern crate network;
mod io; mod io;
mod net; mod net;

View File

@ -3,7 +3,8 @@ use std::time::Duration;
use futures::{Future, Poll}; use futures::{Future, Poll};
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
use tokio_core::net::TcpStream; use tokio_core::net::TcpStream;
use message::{MessageResult, Magic}; use network::Magic;
use message::{MessageResult};
use io::{accept_handshake, AcceptHandshake, Deadline, deadline}; use io::{accept_handshake, AcceptHandshake, Deadline, deadline};
use net::{Config, Connection}; use net::{Config, Connection};

View File

@ -1,5 +1,6 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use message::common::{Magic, Services, NetAddress}; use network::Magic;
use message::common::{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};

View File

@ -4,8 +4,8 @@ use std::net::SocketAddr;
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
use tokio_core::net::{TcpStream, TcpStreamNew}; use tokio_core::net::{TcpStream, TcpStreamNew};
use network::Magic;
use message::Error; use message::Error;
use message::common::Magic;
use message::types::Version; use message::types::Version;
use io::{handshake, Handshake, Deadline, deadline}; use io::{handshake, Handshake, Deadline, deadline};
use net::{Config, Connection}; use net::{Config, Connection};

View File

@ -1,5 +1,5 @@
use std::net; use std::net;
use message::Magic; use network::Magic;
use message::common::Services; use message::common::Services;
use io::SharedTcpStream; use io::SharedTcpStream;

View File

@ -23,11 +23,6 @@ impl ConnectionCounter {
} }
} }
/// Returns maxiumum number of outbound connections.
pub fn max_outbound_connections(&self) -> u32 {
self.max_outbound_connections
}
/// Increases inbound connections counter by 1. /// Increases inbound connections counter by 1.
pub fn note_new_inbound_connection(&self) { pub fn note_new_inbound_connection(&self) {
self.current_inbound_connections.fetch_add(1, Ordering::AcqRel); self.current_inbound_connections.fetch_add(1, Ordering::AcqRel);

View File

@ -12,17 +12,3 @@ impl NonceGenerator for RandomNonce {
rand::random() rand::random()
} }
} }
pub struct StaticNonce(u64);
impl StaticNonce {
pub fn new(nonce: u64) -> Self {
StaticNonce(nonce)
}
}
impl NonceGenerator for StaticNonce {
fn get(&self) -> u64 {
self.0
}
}

View File

@ -1,5 +1,5 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use message::Magic; use network::Magic;
pub type PeerId = usize; pub type PeerId = usize;

View File

@ -8,7 +8,7 @@ pub fn import(cfg: Config, matches: &ArgMatches) -> Result<(), String> {
// TODO: this might be unnecessary here! // TODO: this might be unnecessary here!
try!(init_db(&cfg, &db)); try!(init_db(&cfg, &db));
let mut writer = create_sync_blocks_writer(db); let mut writer = create_sync_blocks_writer(db, cfg.magic);
let blk_path = matches.value_of("PATH").expect("PATH is required in cli.yml; qed"); let blk_path = matches.value_of("PATH").expect("PATH is required in cli.yml; qed");
let blk_dir = try!(::import::open_blk_dir(blk_path).map_err(|_| "Import directory does not exist".to_owned())); let blk_dir = try!(::import::open_blk_dir(blk_path).map_err(|_| "Import directory does not exist".to_owned()));

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, PROTOCOL_VERSION, PROTOCOL_MINIMUM, USER_AGENT}; use {config, p2p, PROTOCOL_VERSION, PROTOCOL_MINIMUM};
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();
@ -22,7 +22,7 @@ pub fn start(cfg: config::Config) -> Result<(), String> {
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: USER_AGENT.into(), user_agent: cfg.user_agent,
start_height: 0, start_height: 0,
relay: false, relay: false,
}, },
@ -32,7 +32,7 @@ pub fn start(cfg: config::Config) -> Result<(), String> {
}; };
let sync_handle = el.handle(); let sync_handle = el.handle();
let sync_connection_factory = create_sync_connection_factory(&sync_handle, cfg.magic.consensus_params(), db); let sync_connection_factory = create_sync_connection_factory(&sync_handle, cfg.magic, db);
let p2p = try!(p2p::P2P::new(p2p_cfg, sync_connection_factory, el.handle()).map_err(|x| x.to_string())); let p2p = try!(p2p::P2P::new(p2p_cfg, sync_connection_factory, el.handle()).map_err(|x| x.to_string()));
try!(p2p.run().map_err(|_| "Failed to start p2p module")); try!(p2p.run().map_err(|_| "Failed to start p2p module"));

View File

@ -1,6 +1,7 @@
use std::net; use std::net;
use clap; use clap;
use message::Magic; use network::Magic;
use {USER_AGENT, REGTEST_USER_AGENT};
pub struct Config { pub struct Config {
pub magic: Magic, pub magic: Magic,
@ -13,6 +14,7 @@ pub struct Config {
pub p2p_threads: usize, pub p2p_threads: usize,
pub db_cache: usize, pub db_cache: usize,
pub data_dir: Option<String>, pub data_dir: Option<String>,
pub user_agent: String,
} }
pub const DEFAULT_DB_CACHE: usize = 512; pub const DEFAULT_DB_CACHE: usize = 512;
@ -27,15 +29,21 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
}; };
let (in_connections, out_connections) = match magic { let (in_connections, out_connections) = match magic {
Magic::Testnet | Magic::Mainnet => (10, 10), Magic::Testnet | Magic::Mainnet | Magic::Other(_) => (10, 10),
Magic::Regtest => (1, 0), Magic::Regtest => (1, 0),
}; };
let p2p_threads = match magic { let p2p_threads = match magic {
Magic::Testnet | Magic::Mainnet => 4, Magic::Testnet | Magic::Mainnet | Magic::Other(_) => 4,
Magic::Regtest => 1, Magic::Regtest => 1,
}; };
// to skip idiotic 30 seconds delay in test-scripts
let user_agent = match magic {
Magic::Testnet | Magic::Mainnet | Magic::Other(_) => USER_AGENT,
Magic::Regtest => REGTEST_USER_AGENT,
};
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(),
@ -77,6 +85,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
p2p_threads: p2p_threads, p2p_threads: p2p_threads,
db_cache: db_cache, db_cache: db_cache,
data_dir: data_dir, data_dir: data_dir,
user_agent: user_agent.to_string(),
}; };
Ok(config) Ok(config)

View File

@ -12,6 +12,7 @@ extern crate chain;
extern crate keys; extern crate keys;
extern crate script; extern crate script;
extern crate message; extern crate message;
extern crate network;
extern crate p2p; extern crate p2p;
extern crate sync; extern crate sync;
extern crate import; extern crate import;
@ -26,6 +27,7 @@ pub const APP_INFO: AppInfo = AppInfo { name: "pbtc", author: "Parity" };
pub const PROTOCOL_VERSION: u32 = 70_014; pub const PROTOCOL_VERSION: u32 = 70_014;
pub const PROTOCOL_MINIMUM: u32 = 70_001; pub const PROTOCOL_MINIMUM: u32 = 70_001;
pub const USER_AGENT: &'static str = "pbtc"; pub const USER_AGENT: &'static str = "pbtc";
pub const REGTEST_USER_AGENT: &'static str = "/Satoshi:0.12.1/";
fn main() { fn main() {
env_logger::init().unwrap(); env_logger::init().unwrap();

View File

@ -38,8 +38,6 @@ pub fn init_db(cfg: &Config, db: &db::SharedStore) -> Result<(), String> {
fn custom_path(data_dir: &str, sub_dir: &str) -> PathBuf { fn custom_path(data_dir: &str, sub_dir: &str) -> PathBuf {
let mut path = PathBuf::from(data_dir); let mut path = PathBuf::from(data_dir);
path.push(sub_dir); path.push(sub_dir);
if let Err(e) = create_dir_all(&path) { create_dir_all(&path).expect("Failed to get app dir");
panic!("Failed to get app dir: {}", e);
}
path path
} }

View File

@ -105,6 +105,15 @@ macro_rules! impl_hash {
} }
} }
impl cmp::PartialOrd for $name {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
let self_ref: &[u8] = &self.0;
let other_ref: &[u8] = &other.0;
self_ref.partial_cmp(other_ref)
}
}
impl Hash for $name { impl Hash for $name {
fn hash<H>(&self, state: &mut H) where H: Hasher { fn hash<H>(&self, state: &mut H) where H: Hasher {
state.write(&self.0); state.write(&self.0);

View File

@ -1,7 +1,7 @@
#![cfg_attr(asm_available, feature(asm))] #![cfg_attr(asm_available, feature(asm))]
extern crate rustc_serialize;
#[macro_use] extern crate heapsize; #[macro_use] extern crate heapsize;
extern crate rustc_serialize;
pub mod bytes; pub mod bytes;
pub mod hash; pub mod hash;

View File

@ -28,6 +28,7 @@ script = { path = "../script" }
serialization = { path = "../serialization" } serialization = { path = "../serialization" }
test-data = { path = "../test-data" } test-data = { path = "../test-data" }
verification = { path = "../verification" } verification = { path = "../verification" }
network = { path = "../network" }
[features] [features]
dev = [] dev = []

View File

@ -1,8 +1,9 @@
use std::sync::Arc; use std::sync::Arc;
use chain; use chain;
use db; use db;
use super::Error; use network::Magic;
use verification::{Verify, ChainVerifier}; use verification::{Verify, ChainVerifier};
use super::Error;
pub struct BlocksWriter { pub struct BlocksWriter {
storage: Arc<db::Store>, storage: Arc<db::Store>,
@ -10,10 +11,10 @@ pub struct BlocksWriter {
} }
impl BlocksWriter { impl BlocksWriter {
pub fn new(storage: db::SharedStore) -> BlocksWriter { pub fn new(storage: db::SharedStore, network: Magic) -> BlocksWriter {
BlocksWriter { BlocksWriter {
storage: storage.clone(), storage: storage.clone(),
verifier: ChainVerifier::new(storage), verifier: ChainVerifier::new(storage, network),
} }
} }
@ -32,18 +33,17 @@ impl BlocksWriter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use db;
use db::Store;
use std::sync::Arc; use std::sync::Arc;
use db::{self, Store};
use network::Magic;
use {test_data, verification};
use super::super::Error; use super::super::Error;
use super::BlocksWriter; use super::BlocksWriter;
use test_data;
use verification;
#[test] #[test]
fn blocks_writer_appends_blocks() { fn blocks_writer_appends_blocks() {
let db = Arc::new(db::TestStorage::with_genesis_block()); let db = Arc::new(db::TestStorage::with_genesis_block());
let mut blocks_target = BlocksWriter::new(db.clone()); let mut blocks_target = BlocksWriter::new(db.clone(), Magic::Testnet);
blocks_target.append_block(test_data::block_h1()).expect("Expecting no error"); blocks_target.append_block(test_data::block_h1()).expect("Expecting no error");
assert_eq!(db.best_block().expect("Block is inserted").number, 1); assert_eq!(db.best_block().expect("Block is inserted").number, 1);
} }
@ -51,7 +51,7 @@ mod tests {
#[test] #[test]
fn blocks_writer_verification_error() { fn blocks_writer_verification_error() {
let db = Arc::new(db::TestStorage::with_genesis_block()); let db = Arc::new(db::TestStorage::with_genesis_block());
let mut blocks_target = BlocksWriter::new(db.clone()); let mut blocks_target = BlocksWriter::new(db.clone(), Magic::Testnet);
match blocks_target.append_block(test_data::block_h2()).unwrap_err() { match blocks_target.append_block(test_data::block_h2()).unwrap_err() {
Error::OutOfOrderBlock => (), Error::OutOfOrderBlock => (),
_ => panic!("Unexpected error"), _ => panic!("Unexpected error"),
@ -62,7 +62,7 @@ mod tests {
#[test] #[test]
fn blocks_writer_out_of_order_block() { fn blocks_writer_out_of_order_block() {
let db = Arc::new(db::TestStorage::with_genesis_block()); let db = Arc::new(db::TestStorage::with_genesis_block());
let mut blocks_target = BlocksWriter::new(db.clone()); let mut blocks_target = BlocksWriter::new(db.clone(), Magic::Testnet);
let wrong_block = test_data::block_builder() let wrong_block = test_data::block_builder()
.header().parent(test_data::genesis().hash()).build() .header().parent(test_data::genesis().hash()).build()

View File

@ -27,6 +27,8 @@ pub struct ConnectionFilter {
last_blocks: LinkedHashMap<H256, ()>, last_blocks: LinkedHashMap<H256, ()>,
/// Last transactions from peer. /// Last transactions from peer.
last_transactions: LinkedHashMap<H256, ()>, last_transactions: LinkedHashMap<H256, ()>,
/// Minimal fee in satoshis per 1000 bytes
fee_rate: Option<u64>,
} }
/// Connection bloom filter /// Connection bloom filter
@ -70,6 +72,7 @@ impl Default for ConnectionFilter {
filter_flags: types::FilterFlags::None, filter_flags: types::FilterFlags::None,
last_blocks: LinkedHashMap::new(), last_blocks: LinkedHashMap::new(),
last_transactions: LinkedHashMap::new(), last_transactions: LinkedHashMap::new(),
fee_rate: None,
} }
} }
} }
@ -83,6 +86,7 @@ impl ConnectionFilter {
filter_flags: message.flags, filter_flags: message.flags,
last_blocks: LinkedHashMap::new(), last_blocks: LinkedHashMap::new(),
last_transactions: LinkedHashMap::new(), last_transactions: LinkedHashMap::new(),
fee_rate: None,
} }
} }
@ -119,12 +123,104 @@ impl ConnectionFilter {
} }
/// Check if transaction should be sent to this connection && optionally update filter /// Check if transaction should be sent to this connection && optionally update filter
pub fn filter_transaction(&mut self, transaction_hash: &H256, transaction: &Transaction) -> bool { pub fn filter_transaction(&mut self, transaction_hash: &H256, transaction: &Transaction, transaction_fee_rate: u64) -> bool {
// check if transaction is known // check if transaction is known
if self.last_transactions.contains_key(transaction_hash) { if self.last_transactions.contains_key(transaction_hash) {
return false; return false;
} }
// check if transaction fee rate is high enough for this peer
if let Some(fee_rate) = self.fee_rate {
if transaction_fee_rate < fee_rate {
return false;
}
}
// check with bloom filter, if set
self.filter_transaction_with_bloom(transaction_hash, transaction)
}
/// Load filter
pub fn load(&mut self, message: &types::FilterLoad) {
self.bloom = Some(ConnectionBloom::new(message));
self.filter_flags = message.flags;
}
/// Add filter
pub fn add(&mut self, message: &types::FilterAdd) {
// ignore if filter is not currently set
if let Some(ref mut bloom) = self.bloom {
bloom.insert(&message.data);
}
}
/// Clear filter
pub fn clear(&mut self) {
self.bloom = None;
}
/// Limit transaction announcing by transaction fee
pub fn set_fee_rate(&mut self, fee_rate: u64) {
if fee_rate == 0 {
self.fee_rate = None;
}
else {
self.fee_rate = Some(fee_rate);
}
}
/// Convert `Block` to `MerkleBlock` using this filter
pub fn build_merkle_block(&mut self, block: Block) -> Option<MerkleBlockArtefacts> {
if self.bloom.is_none() {
return None;
}
// prepare result
let all_len = block.transactions.len();
let mut result = MerkleBlockArtefacts {
merkleblock: types::MerkleBlock {
block_header: block.block_header.clone(),
total_transactions: all_len as u32,
hashes: Vec::default(),
flags: Bytes::default(),
},
matching_transactions: Vec::new(),
};
// calculate hashes && match flags for all transactions
let (all_hashes, all_flags) = block.transactions.into_iter()
.fold((Vec::<H256>::with_capacity(all_len), BitVec::with_capacity(all_len)), |(mut all_hashes, mut all_flags), t| {
let hash = t.hash();
let flag = self.filter_transaction_with_bloom(&hash, &t);
if flag {
result.matching_transactions.push((hash.clone(), t));
}
all_flags.push(flag);
all_hashes.push(hash);
(all_hashes, all_flags)
});
// build partial merkle tree
let (hashes, flags) = PartialMerkleTree::build(all_hashes, all_flags);
result.merkleblock.hashes.extend(hashes);
// to_bytes() converts [true, false, true] to 0b10100000
// while protocol requires [true, false, true] to be serialized as 0x00000101
result.merkleblock.flags = flags.to_bytes().into_iter()
.map(|b|
((b & 0b10000000) >> 7) |
((b & 0b01000000) >> 5) |
((b & 0b00100000) >> 3) |
((b & 0b00010000) >> 1) |
((b & 0b00001000) << 1) |
((b & 0b00000100) << 3) |
((b & 0b00000010) << 5) |
((b & 0b00000001) << 7)).collect::<Vec<u8>>().into();
Some(result)
}
/// Check if transaction should be sent to this connection using bloom filter && optionally update filter
fn filter_transaction_with_bloom(&mut self, transaction_hash: &H256, transaction: &Transaction) -> bool {
// check with bloom filter, if set // check with bloom filter, if set
match self.bloom { match self.bloom {
/// if no filter is set for the connection => match everything /// if no filter is set for the connection => match everything
@ -188,75 +284,6 @@ impl ConnectionFilter {
}, },
} }
} }
/// Load filter
pub fn load(&mut self, message: &types::FilterLoad) {
self.bloom = Some(ConnectionBloom::new(message));
self.filter_flags = message.flags;
}
/// Add filter
pub fn add(&mut self, message: &types::FilterAdd) {
// ignore if filter is not currently set
if let Some(ref mut bloom) = self.bloom {
bloom.insert(&message.data);
}
}
/// Clear filter
pub fn clear(&mut self) {
self.bloom = None;
}
/// Convert `Block` to `MerkleBlock` using this filter
pub fn build_merkle_block(&mut self, block: Block) -> Option<MerkleBlockArtefacts> {
if self.bloom.is_none() {
return None;
}
// prepare result
let all_len = block.transactions.len();
let mut result = MerkleBlockArtefacts {
merkleblock: types::MerkleBlock {
block_header: block.block_header.clone(),
total_transactions: all_len as u32,
hashes: Vec::default(),
flags: Bytes::default(),
},
matching_transactions: Vec::new(),
};
// calculate hashes && match flags for all transactions
let (all_hashes, all_flags) = block.transactions.into_iter()
.fold((Vec::<H256>::with_capacity(all_len), BitVec::with_capacity(all_len)), |(mut all_hashes, mut all_flags), t| {
let hash = t.hash();
let flag = self.filter_transaction(&hash, &t);
if flag {
result.matching_transactions.push((hash.clone(), t));
}
all_flags.push(flag);
all_hashes.push(hash);
(all_hashes, all_flags)
});
// build partial merkle tree
let (hashes, flags) = PartialMerkleTree::build(all_hashes, all_flags);
result.merkleblock.hashes.extend(hashes);
// to_bytes() converts [true, false, true] to 0b10100000
// while protocol requires [true, false, true] to be serialized as 0x00000101
result.merkleblock.flags = flags.to_bytes().into_iter()
.map(|b|
((b & 0b10000000) >> 7) |
((b & 0b01000000) >> 5) |
((b & 0b00100000) >> 3) |
((b & 0b00010000) >> 1) |
((b & 0b00001000) << 1) |
((b & 0b00000100) << 3) |
((b & 0b00000010) << 5) |
((b & 0b00000001) << 7)).collect::<Vec<u8>>().into();
Some(result)
}
} }
impl ConnectionBloom { impl ConnectionBloom {
@ -493,13 +520,13 @@ pub mod tests {
let mut filter = ConnectionFilter::with_filterload(&default_filterload()); let mut filter = ConnectionFilter::with_filterload(&default_filterload());
assert!(!filter.filter_transaction(&tx1.hash(), &tx1)); assert!(!filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
filter.add(&make_filteradd(&*tx1.hash())); filter.add(&make_filteradd(&*tx1.hash()));
assert!(filter.filter_transaction(&tx1.hash(), &tx1)); assert!(filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
} }
#[test] #[test]
@ -512,13 +539,13 @@ pub mod tests {
let mut filter = ConnectionFilter::with_filterload(&default_filterload()); let mut filter = ConnectionFilter::with_filterload(&default_filterload());
assert!(!filter.filter_transaction(&tx1.hash(), &tx1)); assert!(!filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
filter.add(&make_filteradd(&tx1_out_data)); filter.add(&make_filteradd(&tx1_out_data));
assert!(filter.filter_transaction(&tx1.hash(), &tx1)); assert!(filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
} }
#[test] #[test]
@ -530,13 +557,13 @@ pub mod tests {
let mut filter = ConnectionFilter::with_filterload(&default_filterload()); let mut filter = ConnectionFilter::with_filterload(&default_filterload());
assert!(!filter.filter_transaction(&tx1.hash(), &tx1)); assert!(!filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
filter.add(&make_filteradd(&tx1_previous_output)); filter.add(&make_filteradd(&tx1_previous_output));
assert!(filter.filter_transaction(&tx1.hash(), &tx1)); assert!(filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
} }
#[test] #[test]
@ -549,13 +576,39 @@ pub mod tests {
let mut filter = ConnectionFilter::with_filterload(&default_filterload()); let mut filter = ConnectionFilter::with_filterload(&default_filterload());
assert!(!filter.filter_transaction(&tx1.hash(), &tx1)); assert!(!filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
filter.add(&make_filteradd(&tx1_input_data)); filter.add(&make_filteradd(&tx1_input_data));
assert!(filter.filter_transaction(&tx1.hash(), &tx1)); assert!(filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2)); assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 1000));
}
#[test]
fn connection_filter_matches_transaction_by_fee_rate() {
let tx1: Transaction = test_data::TransactionBuilder::with_version(1).into();
let tx2: Transaction = test_data::TransactionBuilder::with_version(2).into();
let mut filter = ConnectionFilter::default();
assert!(filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(filter.filter_transaction(&tx2.hash(), &tx2, 2000));
filter.set_fee_rate(1500);
assert!(!filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(filter.filter_transaction(&tx2.hash(), &tx2, 2000));
filter.set_fee_rate(3000);
assert!(!filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(!filter.filter_transaction(&tx2.hash(), &tx2, 2000));
filter.set_fee_rate(0);
assert!(filter.filter_transaction(&tx1.hash(), &tx1, 1000));
assert!(filter.filter_transaction(&tx2.hash(), &tx2, 2000));
} }
#[test] #[test]

View File

@ -23,6 +23,7 @@ extern crate serialization as ser;
#[cfg(test)] #[cfg(test)]
extern crate ethcore_devtools as devtools; extern crate ethcore_devtools as devtools;
extern crate rand; extern crate rand;
extern crate network;
mod best_headers_chain; mod best_headers_chain;
mod blocks_writer; mod blocks_writer;
@ -45,7 +46,7 @@ mod synchronization_verifier;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock; use parking_lot::RwLock;
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
use message::common::ConsensusParams; use network::Magic;
/// Sync errors. /// Sync errors.
#[derive(Debug)] #[derive(Debug)]
@ -59,12 +60,12 @@ pub enum Error {
} }
/// Create blocks writer. /// Create blocks writer.
pub fn create_sync_blocks_writer(db: db::SharedStore) -> blocks_writer::BlocksWriter { pub fn create_sync_blocks_writer(db: db::SharedStore, network: Magic) -> blocks_writer::BlocksWriter {
blocks_writer::BlocksWriter::new(db) blocks_writer::BlocksWriter::new(db, network)
} }
/// Create inbound synchronization connections factory for given `db`. /// Create inbound synchronization connections factory for given `db`.
pub fn create_sync_connection_factory(handle: &Handle, consensus_params: ConsensusParams, db: db::SharedStore) -> p2p::LocalSyncNodeRef { pub fn create_sync_connection_factory(handle: &Handle, network: Magic, db: db::SharedStore) -> p2p::LocalSyncNodeRef {
use synchronization_chain::Chain as SyncChain; use synchronization_chain::Chain as SyncChain;
use synchronization_executor::LocalSynchronizationTaskExecutor as SyncExecutor; use synchronization_executor::LocalSynchronizationTaskExecutor as SyncExecutor;
use local_node::LocalNode as SyncNode; use local_node::LocalNode as SyncNode;
@ -77,7 +78,7 @@ pub fn create_sync_connection_factory(handle: &Handle, consensus_params: Consens
let sync_executor = SyncExecutor::new(sync_chain.clone()); let sync_executor = SyncExecutor::new(sync_chain.clone());
let sync_server = Arc::new(SynchronizationServer::new(sync_chain.clone(), sync_executor.clone())); let sync_server = Arc::new(SynchronizationServer::new(sync_chain.clone(), sync_executor.clone()));
let sync_client_core = SynchronizationClientCore::new(SynchronizationConfig::new(), handle, sync_executor.clone(), sync_chain.clone()); let sync_client_core = SynchronizationClientCore::new(SynchronizationConfig::new(), handle, sync_executor.clone(), sync_chain.clone());
let verifier = AsyncVerifier::new(consensus_params, sync_chain, sync_client_core.clone()); let verifier = AsyncVerifier::new(network, sync_chain, sync_client_core.clone());
let sync_client = SynchronizationClient::new(sync_client_core, verifier); let sync_client = SynchronizationClient::new(sync_client_core, verifier);
let sync_node = Arc::new(SyncNode::new(sync_server, sync_client, sync_executor)); let sync_node = Arc::new(SyncNode::new(sync_server, sync_client, sync_executor));
SyncConnectionFactory::with_local_node(sync_node) SyncConnectionFactory::with_local_node(sync_node)

View File

@ -186,8 +186,9 @@ impl<T, U, V> LocalNode<T, U, V> where T: SynchronizationTaskExecutor + PeersCon
self.client.lock().on_peer_block_announcement_type(peer_index, BlockAnnouncementType::SendHeader); self.client.lock().on_peer_block_announcement_type(peer_index, BlockAnnouncementType::SendHeader);
} }
pub fn on_peer_feefilter(&self, peer_index: usize, _message: types::FeeFilter) { pub fn on_peer_feefilter(&self, peer_index: usize, message: types::FeeFilter) {
trace!(target: "sync", "Got `feefilter` message from peer#{}", peer_index); trace!(target: "sync", "Got `feefilter` message from peer#{}", peer_index);
self.client.lock().on_peer_feefilter(peer_index, &message);
} }
pub fn on_peer_send_compact(&self, peer_index: usize, message: types::SendCompact) { pub fn on_peer_send_compact(&self, peer_index: usize, message: types::SendCompact) {

View File

@ -6,6 +6,7 @@ use parking_lot::RwLock;
use chain::{Block, BlockHeader, Transaction}; use chain::{Block, BlockHeader, Transaction};
use db; use db;
use best_headers_chain::{BestHeadersChain, Information as BestHeadersInformation}; use best_headers_chain::{BestHeadersChain, Information as BestHeadersInformation};
use primitives::bytes::Bytes;
use primitives::hash::H256; use primitives::hash::H256;
use hash_queue::{HashQueueChain, HashPosition}; use hash_queue::{HashQueueChain, HashPosition};
use miner::{MemoryPool, MemoryPoolOrderingStrategy, MemoryPoolInformation}; use miner::{MemoryPool, MemoryPoolOrderingStrategy, MemoryPoolInformation};
@ -661,6 +662,18 @@ impl Chain {
} }
} }
impl db::TransactionProvider for Chain {
fn transaction_bytes(&self, hash: &H256) -> Option<Bytes> {
self.memory_pool.transaction_bytes(hash)
.or_else(|| self.storage.transaction_bytes(hash))
}
fn transaction(&self, hash: &H256) -> Option<Transaction> {
self.memory_pool.transaction(hash)
.or_else(|| self.storage.transaction(hash))
}
}
impl fmt::Debug for Information { impl fmt::Debug for Information {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[sch:{} / bh:{} -> req:{} -> vfy:{} -> stored: {}]", self.scheduled, self.headers.best, self.requested, self.verifying, self.stored) write!(f, "[sch:{} / bh:{} -> req:{} -> vfy:{} -> stored: {}]", self.scheduled, self.headers.best, self.requested, self.verifying, self.stored)

View File

@ -27,6 +27,7 @@ use synchronization_manager::{manage_synchronization_peers_blocks, manage_synchr
use synchronization_verifier::{Verifier, VerificationSink}; use synchronization_verifier::{Verifier, VerificationSink};
use compact_block_builder::build_compact_block; use compact_block_builder::build_compact_block;
use hash_queue::HashPosition; use hash_queue::HashPosition;
use miner::transaction_fee_rate;
use time; use time;
use std::time::Duration; use std::time::Duration;
@ -196,6 +197,7 @@ pub trait Client : Send + 'static {
fn on_peer_filteradd(&mut self, peer_index: usize, message: &types::FilterAdd); fn on_peer_filteradd(&mut self, peer_index: usize, message: &types::FilterAdd);
fn on_peer_filterclear(&mut self, peer_index: usize); fn on_peer_filterclear(&mut self, peer_index: usize);
fn on_peer_block_announcement_type(&mut self, peer_index: usize, announcement_type: BlockAnnouncementType); fn on_peer_block_announcement_type(&mut self, peer_index: usize, announcement_type: BlockAnnouncementType);
fn on_peer_feefilter(&mut self, peer_index: usize, message: &types::FeeFilter);
fn on_peer_disconnected(&mut self, peer_index: usize); fn on_peer_disconnected(&mut self, peer_index: usize);
fn after_peer_nearly_blocks_verified(&mut self, peer_index: usize, future: BoxFuture<(), ()>); fn after_peer_nearly_blocks_verified(&mut self, peer_index: usize, future: BoxFuture<(), ()>);
} }
@ -216,6 +218,7 @@ pub trait ClientCore : VerificationSink {
fn on_peer_filteradd(&mut self, peer_index: usize, message: &types::FilterAdd); fn on_peer_filteradd(&mut self, peer_index: usize, message: &types::FilterAdd);
fn on_peer_filterclear(&mut self, peer_index: usize); fn on_peer_filterclear(&mut self, peer_index: usize);
fn on_peer_block_announcement_type(&mut self, peer_index: usize, announcement_type: BlockAnnouncementType); fn on_peer_block_announcement_type(&mut self, peer_index: usize, announcement_type: BlockAnnouncementType);
fn on_peer_feefilter(&mut self, peer_index: usize, message: &types::FeeFilter);
fn on_peer_disconnected(&mut self, peer_index: usize); fn on_peer_disconnected(&mut self, peer_index: usize);
fn after_peer_nearly_blocks_verified(&mut self, peer_index: usize, future: BoxFuture<(), ()>); fn after_peer_nearly_blocks_verified(&mut self, peer_index: usize, future: BoxFuture<(), ()>);
fn execute_synchronization_tasks(&mut self, forced_blocks_requests: Option<Vec<H256>>); fn execute_synchronization_tasks(&mut self, forced_blocks_requests: Option<Vec<H256>>);
@ -413,6 +416,10 @@ impl<T, U> Client for SynchronizationClient<T, U> where T: TaskExecutor, U: Veri
self.core.lock().on_peer_block_announcement_type(peer_index, announcement_type); self.core.lock().on_peer_block_announcement_type(peer_index, announcement_type);
} }
fn on_peer_feefilter(&mut self, peer_index: usize, message: &types::FeeFilter) {
self.core.lock().on_peer_feefilter(peer_index, message);
}
fn on_peer_disconnected(&mut self, peer_index: usize) { fn on_peer_disconnected(&mut self, peer_index: usize) {
self.core.lock().on_peer_disconnected(peer_index); self.core.lock().on_peer_disconnected(peer_index);
} }
@ -649,6 +656,13 @@ impl<T> ClientCore for SynchronizationClientCore<T> where T: TaskExecutor {
} }
} }
/// Peer wants to limit transaction announcing by transaction fee
fn on_peer_feefilter(&mut self, peer_index: usize, message: &types::FeeFilter) {
if self.peers.is_known_peer(peer_index) {
self.peers.on_peer_feefilter(peer_index, message.fee_rate);
}
}
/// Peer disconnected. /// Peer disconnected.
fn on_peer_disconnected(&mut self, peer_index: usize) { fn on_peer_disconnected(&mut self, peer_index: usize) {
// when last peer is disconnected, reset, but let verifying blocks be verified // when last peer is disconnected, reset, but let verifying blocks be verified
@ -835,7 +849,7 @@ impl<T> VerificationSink for SynchronizationClientCore<T> where T: TaskExecutor
fn on_transaction_verification_success(&mut self, transaction: Transaction) { fn on_transaction_verification_success(&mut self, transaction: Transaction) {
let hash = transaction.hash(); let hash = transaction.hash();
{ let transaction_fee_rate = {
// insert transaction to the memory pool // insert transaction to the memory pool
let mut chain = self.chain.write(); let mut chain = self.chain.write();
@ -847,10 +861,13 @@ impl<T> VerificationSink for SynchronizationClientCore<T> where T: TaskExecutor
// transaction was in verification queue => insert to memory pool // transaction was in verification queue => insert to memory pool
chain.insert_verified_transaction(transaction.clone()); chain.insert_verified_transaction(transaction.clone());
}
// calculate transaction fee rate
transaction_fee_rate(&*chain, &transaction)
};
// relay transaction to peers // relay transaction to peers
self.relay_new_transactions(vec![(hash, &transaction)]); self.relay_new_transactions(vec![(hash, &transaction, transaction_fee_rate)]);
} }
/// Process failed transaction verification /// Process failed transaction verification
@ -1009,12 +1026,14 @@ impl<T> SynchronizationClientCore<T> where T: TaskExecutor {
} }
/// Relay new transactions /// Relay new transactions
fn relay_new_transactions(&mut self, new_transactions: Vec<(H256, &Transaction)>) { fn relay_new_transactions(&mut self, new_transactions: Vec<(H256, &Transaction, u64)>) {
let tasks: Vec<_> = self.peers.all_peers().into_iter() let tasks: Vec<_> = self.peers.all_peers().into_iter()
.filter_map(|peer_index| { .filter_map(|peer_index| {
let inventory: Vec<_> = new_transactions.iter() let inventory: Vec<_> = new_transactions.iter()
.filter(|&&(ref h, tx)| self.peers.filter_mut(peer_index).filter_transaction(h, tx)) .filter(|&&(ref h, tx, tx_fee_rate)| {
.map(|&(ref h, _)| InventoryVector { self.peers.filter_mut(peer_index).filter_transaction(h, tx, tx_fee_rate)
})
.map(|&(ref h, _, _)| InventoryVector {
inv_type: InventoryType::MessageTx, inv_type: InventoryType::MessageTx,
hash: h.clone(), hash: h.clone(),
}) })
@ -1124,7 +1143,9 @@ impl<T> SynchronizationClientCore<T> where T: TaskExecutor {
// remove this block from the queue // remove this block from the queue
chain.forget_block_leave_header(&block_hash); chain.forget_block_leave_header(&block_hash);
// remember this block as unknown // remember this block as unknown
self.orphaned_blocks_pool.insert_unknown_block(block_hash, block); if !self.orphaned_blocks_pool.contains_unknown_block(&block_hash) {
self.orphaned_blocks_pool.insert_unknown_block(block_hash, block);
}
} }
}, },
BlockState::Verifying | BlockState::Stored => { BlockState::Verifying | BlockState::Stored => {
@ -1321,6 +1342,7 @@ pub mod tests {
use tokio_core::reactor::{Core, Handle}; use tokio_core::reactor::{Core, Handle};
use chain::{Block, Transaction}; use chain::{Block, Transaction};
use message::common::{InventoryVector, InventoryType}; use message::common::{InventoryVector, InventoryType};
use message::types;
use super::{Client, Config, SynchronizationClient, SynchronizationClientCore, BlockAnnouncementType}; use super::{Client, Config, SynchronizationClient, SynchronizationClientCore, BlockAnnouncementType};
use connection_filter::tests::*; use connection_filter::tests::*;
use synchronization_executor::Task; use synchronization_executor::Task;
@ -2182,6 +2204,66 @@ pub mod tests {
} }
#[test] #[test]
fn relay_new_transaction_with_feefilter() {
let (_, _, executor, chain, sync) = create_sync(None, None);
let b1 = test_data::block_builder().header().parent(test_data::genesis().hash()).build()
.transaction().output().value(1_000_000).build().build()
.build(); // genesis -> b1
let tx0 = b1.transactions[0].clone();
let tx1: Transaction = test_data::TransactionBuilder::with_output(800_000).add_input(&tx0, 0).into();
let tx1_hash = tx1.hash();
let mut sync = sync.lock();
sync.on_peer_connected(1);
sync.on_peer_connected(2);
sync.on_peer_connected(3);
sync.on_peer_connected(4);
sync.on_peer_block(1, b1);
{
use miner::transaction_fee_rate;
let chain = chain.read();
assert_eq!(transaction_fee_rate(&*chain, &tx1), 3333); // 200_000 / 60
}
sync.on_peer_feefilter(2, &types::FeeFilter { fee_rate: 3000, });
sync.on_peer_feefilter(3, &types::FeeFilter { fee_rate: 4000, });
// forget previous tasks
{ executor.lock().take_tasks(); }
sync.on_peer_transaction(1, tx1);
let tasks = executor.lock().take_tasks();
assert_eq!(tasks, vec![
Task::SendInventory(2, vec![
InventoryVector {
inv_type: InventoryType::MessageTx,
hash: tx1_hash.clone(),
}
], ServerTaskIndex::None),
Task::SendInventory(4, vec![
InventoryVector {
inv_type: InventoryType::MessageTx,
hash: tx1_hash.clone(),
}
], ServerTaskIndex::None),
]);
}
#[test]
fn receive_same_unknown_block_twice() {
let (_, _, _, _, sync) = create_sync(None, None);
let mut sync = sync.lock();
sync.on_peer_block(1, test_data::block_h2());
// should not panic here
sync.on_peer_block(2, test_data::block_h2());
}
fn relay_new_block_after_sendcmpct() { fn relay_new_block_after_sendcmpct() {
// TODO // TODO
} }

View File

@ -8,9 +8,9 @@ use primitives::hash::H256;
/// Management interval (in ms) /// Management interval (in ms)
pub const MANAGEMENT_INTERVAL_MS: u64 = 10 * 1000; pub const MANAGEMENT_INTERVAL_MS: u64 = 10 * 1000;
/// Response time before getting block to decrease peer score /// Response time before getting block to decrease peer score
const DEFAULT_PEER_BLOCK_FAILURE_INTERVAL_MS: u32 = 5 * 1000; const DEFAULT_PEER_BLOCK_FAILURE_INTERVAL_MS: u32 = 60 * 1000;
/// Response time before getting inventory to decrease peer score /// Response time before getting inventory to decrease peer score
const DEFAULT_PEER_INVENTORY_FAILURE_INTERVAL_MS: u32 = 5 * 1000; const DEFAULT_PEER_INVENTORY_FAILURE_INTERVAL_MS: u32 = 60 * 1000;
/// Unknown orphan block removal time /// Unknown orphan block removal time
const DEFAULT_UNKNOWN_BLOCK_REMOVAL_TIME_MS: u32 = 20 * 60 * 1000; const DEFAULT_UNKNOWN_BLOCK_REMOVAL_TIME_MS: u32 = 20 * 60 * 1000;
/// Maximal number of orphaned blocks /// Maximal number of orphaned blocks

View File

@ -199,6 +199,11 @@ impl Peers {
self.block_announcement_types.insert(peer_index, announcement_type); self.block_announcement_types.insert(peer_index, announcement_type);
} }
/// Peer wants to limit transaction announcing by transaction fee
pub fn on_peer_feefilter(&mut self, peer_index: usize, fee_rate: u64) {
self.filter_mut(peer_index).set_fee_rate(fee_rate);
}
/// Peer has been disconnected /// Peer has been disconnected
pub fn on_peer_disconnected(&mut self, peer_index: usize) -> Option<Vec<H256>> { pub fn on_peer_disconnected(&mut self, peer_index: usize) -> Option<Vec<H256>> {
// forget this peer without any chances to reuse // forget this peer without any chances to reuse

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::mpsc::{channel, Sender, Receiver};
use parking_lot::Mutex; use parking_lot::Mutex;
use chain::{Block, Transaction}; use chain::{Block, Transaction};
use message::common::ConsensusParams; use network::{Magic, ConsensusParams};
use primitives::hash::H256; use primitives::hash::H256;
use verification::{ChainVerifier, Verify as VerificationVerify}; use verification::{ChainVerifier, Verify as VerificationVerify};
use synchronization_chain::ChainRef; use synchronization_chain::ChainRef;
@ -48,16 +48,16 @@ pub struct AsyncVerifier {
impl AsyncVerifier { impl AsyncVerifier {
/// Create new async verifier /// Create new async verifier
pub fn new<T: VerificationSink>(consensus_params: ConsensusParams, chain: ChainRef, sink: Arc<Mutex<T>>) -> Self { pub fn new<T: VerificationSink>(network: Magic, chain: ChainRef, sink: Arc<Mutex<T>>) -> Self {
let (verification_work_sender, verification_work_receiver) = channel(); let (verification_work_sender, verification_work_receiver) = channel();
let storage = chain.read().storage(); let storage = chain.read().storage();
let verifier = ChainVerifier::new(storage); let verifier = ChainVerifier::new(storage, network);
AsyncVerifier { AsyncVerifier {
verification_work_sender: verification_work_sender, verification_work_sender: verification_work_sender,
verification_worker_thread: Some(thread::Builder::new() verification_worker_thread: Some(thread::Builder::new()
.name("Sync verification thread".to_string()) .name("Sync verification thread".to_string())
.spawn(move || { .spawn(move || {
AsyncVerifier::verification_worker_proc(sink, chain, consensus_params, verifier, verification_work_receiver) AsyncVerifier::verification_worker_proc(sink, chain, network.consensus_params(), verifier, verification_work_receiver)
}) })
.expect("Error creating verification thread")) .expect("Error creating verification thread"))
} }

View File

@ -7,3 +7,4 @@ authors = ["Nikolay Volf <nikvolf@gmail.com>"]
chain = { path = "../chain" } chain = { path = "../chain" }
primitives = { path = "../primitives" } primitives = { path = "../primitives" }
serialization = { path = "../serialization" } serialization = { path = "../serialization" }
time = "0.1"

View File

@ -5,6 +5,11 @@ use chain;
use primitives::hash::H256; use primitives::hash::H256;
use primitives::bytes::Bytes; use primitives::bytes::Bytes;
use invoke::{Invoke, Identity}; use invoke::{Invoke, Identity};
use std::cell::Cell;
thread_local! {
pub static TIMESTAMP_COUNTER: Cell<u32> = Cell::new(0);
}
pub struct BlockHashBuilder<F=Identity> { pub struct BlockHashBuilder<F=Identity> {
callback: F, callback: F,
@ -182,7 +187,7 @@ impl<F> BlockHeaderBuilder<F> where F: Invoke<chain::BlockHeader> {
pub fn with_callback(callback: F) -> Self { pub fn with_callback(callback: F) -> Self {
BlockHeaderBuilder { BlockHeaderBuilder {
callback: callback, callback: callback,
time: 0, time: TIMESTAMP_COUNTER.with(|counter| { let val = counter.get(); counter.set(val+1); val }),
nonce: 0, nonce: 0,
merkle_root: H256::from(0), merkle_root: H256::from(0),
parent: H256::from(0), parent: H256::from(0),

View File

@ -3,6 +3,7 @@
extern crate chain; extern crate chain;
extern crate primitives; extern crate primitives;
extern crate serialization as ser; extern crate serialization as ser;
extern crate time;
use chain::Block; use chain::Block;

View File

@ -4,18 +4,17 @@ version = "0.1.0"
authors = ["Nikolay Volf <nikvolf@gmail.com>"] authors = ["Nikolay Volf <nikvolf@gmail.com>"]
[dependencies] [dependencies]
byteorder = "0.5"
parking_lot = "0.3"
linked-hash-map = "0.3"
time = "0.1"
log = "0.3"
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
primitives = { path = "../primitives" } primitives = { path = "../primitives" }
chain = { path = "../chain" } chain = { path = "../chain" }
serialization = { path = "../serialization" } serialization = { path = "../serialization" }
parking_lot = "0.3"
linked-hash-map = "0.3"
test-data = { path = "../test-data" } test-data = { path = "../test-data" }
byteorder = "0.5"
time = "0.1"
script = { path = "../script" } script = { path = "../script" }
log = "0.3" network = { path = "../network" }
db = { path = "../db", features = ["dev"] }
[dependencies.db]
path = "../db"
features = ["dev"]

View File

@ -1,9 +1,10 @@
//! Bitcoin chain verifier //! Bitcoin chain verifier
use std::collections::BTreeSet;
use db::{self, BlockRef, BlockLocation}; use db::{self, BlockRef, BlockLocation};
use chain; use network::Magic;
use super::{Verify, VerificationResult, Chain, Error, TransactionError, ContinueVerify}; use super::{Verify, VerificationResult, Chain, Error, TransactionError, ContinueVerify};
use utils; use {chain, utils};
const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours const BLOCK_MAX_FUTURE: i64 = 2 * 60 * 60; // 2 hours
const COINBASE_MATURITY: u32 = 100; // 2 hours const COINBASE_MATURITY: u32 = 100; // 2 hours
@ -18,16 +19,18 @@ pub struct ChainVerifier {
verify_clocktimeverify: bool, verify_clocktimeverify: bool,
skip_pow: bool, skip_pow: bool,
skip_sig: bool, skip_sig: bool,
network: Magic,
} }
impl ChainVerifier { impl ChainVerifier {
pub fn new(store: db::SharedStore) -> Self { pub fn new(store: db::SharedStore, network: Magic) -> Self {
ChainVerifier { ChainVerifier {
store: store, store: store,
verify_p2sh: false, verify_p2sh: false,
verify_clocktimeverify: false, verify_clocktimeverify: false,
skip_pow: false, skip_pow: false,
skip_sig: false skip_sig: false,
network: network,
} }
} }
@ -54,6 +57,14 @@ impl ChainVerifier {
} }
fn ordered_verify(&self, block: &chain::Block, at_height: u32) -> Result<(), Error> { fn ordered_verify(&self, block: &chain::Block, at_height: u32) -> Result<(), Error> {
// check that difficulty matches the adjusted level
if let Some(work) = self.work_required(block, at_height) {
if !self.skip_pow && work != block.header().nbits {
trace!(target: "verification", "pow verification error at height: {}", at_height);
trace!(target: "verification", "expected work: {}, got {}", work, block.header().nbits);
return Err(Error::Difficulty);
}
}
let coinbase_spends = block.transactions()[0].total_spends(); let coinbase_spends = block.transactions()[0].total_spends();
@ -62,7 +73,7 @@ impl ChainVerifier {
let mut total_claimed: u64 = 0; let mut total_claimed: u64 = 0;
for (_, input) in tx.inputs.iter().enumerate() { for input in &tx.inputs {
// Coinbase maturity check // Coinbase maturity check
if let Some(previous_meta) = self.store.transaction_meta(&input.previous_output.hash) { if let Some(previous_meta) = self.store.transaction_meta(&input.previous_output.hash) {
@ -194,7 +205,7 @@ impl ChainVerifier {
} }
// target difficulty threshold // target difficulty threshold
if !self.skip_pow && !utils::check_nbits(&hash, block.header().nbits) { if !self.skip_pow && !utils::check_nbits(self.network.max_nbits(), &hash, block.header().nbits) {
return Err(Error::Pow); return Err(Error::Pow);
} }
@ -203,6 +214,13 @@ impl ChainVerifier {
return Err(Error::Timestamp); return Err(Error::Timestamp);
} }
if let Some(median_timestamp) = self.median_timestamp(block) {
if median_timestamp >= block.block_header.time {
trace!(target: "verification", "median timestamp verification failed, median: {}, current: {}", median_timestamp, block.block_header.time);
return Err(Error::Timestamp);
}
}
// todo: serialized_size function is at least suboptimal // todo: serialized_size function is at least suboptimal
let size = ::serialization::Serializable::serialized_size(block); let size = ::serialization::Serializable::serialized_size(block);
if size > MAX_BLOCK_SIZE { if size > MAX_BLOCK_SIZE {
@ -258,6 +276,52 @@ impl ChainVerifier {
}, },
} }
} }
fn median_timestamp(&self, block: &chain::Block) -> Option<u32> {
let mut timestamps = BTreeSet::new();
let mut block_ref = block.block_header.previous_header_hash.clone().into();
// TODO: optimize it, so it does not make 11 redundant queries each time
for _ in 0..11 {
let previous_header = match self.store.block_header(block_ref) {
Some(h) => h,
None => { break; }
};
timestamps.insert(previous_header.time);
block_ref = previous_header.previous_header_hash.into();
}
if timestamps.len() > 2 {
let timestamps: Vec<_> = timestamps.into_iter().collect();
Some(timestamps[timestamps.len() / 2])
}
else { None }
}
fn work_required(&self, block: &chain::Block, height: u32) -> Option<u32> {
if height == 0 {
return None;
}
let previous_ref = block.block_header.previous_header_hash.clone().into();
let previous_header = self.store.block_header(previous_ref).expect("self.height != 0; qed");
if utils::is_retarget_height(height) {
let retarget_ref = (height - utils::RETARGETING_INTERVAL).into();
let retarget_header = self.store.block_header(retarget_ref).expect("self.height != 0 && self.height % RETARGETING_INTERVAL == 0; qed");
// timestamp of block(height - RETARGETING_INTERVAL)
let retarget_timestamp = retarget_header.time;
// timestamp of parent block
let last_timestamp = previous_header.time;
// nbits of last block
let last_nbits = previous_header.nbits;
return Some(utils::work_required_retarget(self.network.max_nbits(), retarget_timestamp, last_timestamp, last_nbits));
}
// TODO: if.testnet
Some(previous_header.nbits)
}
} }
impl Verify for ChainVerifier { impl Verify for ChainVerifier {
@ -293,20 +357,19 @@ impl ContinueVerify for ChainVerifier {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::Arc;
use db::{TestStorage, Storage, Store, BlockStapler};
use network::Magic;
use devtools::RandomTempPath;
use {script, test_data};
use super::ChainVerifier; use super::ChainVerifier;
use super::super::{Verify, Chain, Error, TransactionError}; use super::super::{Verify, Chain, Error, TransactionError};
use db::{TestStorage, Storage, Store, BlockStapler};
use test_data;
use std::sync::Arc;
use devtools::RandomTempPath;
use script;
#[test] #[test]
fn verify_orphan() { fn verify_orphan() {
let storage = TestStorage::with_blocks(&vec![test_data::genesis()]); let storage = TestStorage::with_blocks(&vec![test_data::genesis()]);
let b2 = test_data::block_h2(); let b2 = test_data::block_h2();
let verifier = ChainVerifier::new(Arc::new(storage)); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet);
assert_eq!(Chain::Orphan, verifier.verify(&b2).unwrap()); assert_eq!(Chain::Orphan, verifier.verify(&b2).unwrap());
} }
@ -315,7 +378,7 @@ mod tests {
fn verify_smoky() { fn verify_smoky() {
let storage = TestStorage::with_blocks(&vec![test_data::genesis()]); let storage = TestStorage::with_blocks(&vec![test_data::genesis()]);
let b1 = test_data::block_h1(); let b1 = test_data::block_h1();
let verifier = ChainVerifier::new(Arc::new(storage)); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet);
assert_eq!(Chain::Main, verifier.verify(&b1).unwrap()); assert_eq!(Chain::Main, verifier.verify(&b1).unwrap());
} }
@ -328,7 +391,7 @@ mod tests {
] ]
); );
let b1 = test_data::block_h170(); let b1 = test_data::block_h170();
let verifier = ChainVerifier::new(Arc::new(storage)); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet);
assert_eq!(Chain::Main, verifier.verify(&b1).unwrap()); assert_eq!(Chain::Main, verifier.verify(&b1).unwrap());
} }
@ -340,7 +403,7 @@ mod tests {
] ]
); );
let b170 = test_data::block_h170(); let b170 = test_data::block_h170();
let verifier = ChainVerifier::new(Arc::new(storage)); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet);
let should_be = Err(Error::Transaction( let should_be = Err(Error::Transaction(
1, 1,
@ -374,7 +437,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build() .merkled_header().parent(genesis.hash()).build()
.build(); .build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip().signatures_skip(); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet).pow_skip().signatures_skip();
let expected = Err(Error::Transaction( let expected = Err(Error::Transaction(
1, 1,
@ -410,7 +473,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build() .merkled_header().parent(genesis.hash()).build()
.build(); .build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip().signatures_skip(); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet).pow_skip().signatures_skip();
let expected = Ok(Chain::Main); let expected = Ok(Chain::Main);
assert_eq!(expected, verifier.verify(&block)); assert_eq!(expected, verifier.verify(&block));
@ -448,7 +511,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build() .merkled_header().parent(genesis.hash()).build()
.build(); .build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip().signatures_skip(); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet).pow_skip().signatures_skip();
let expected = Ok(Chain::Main); let expected = Ok(Chain::Main);
assert_eq!(expected, verifier.verify(&block)); assert_eq!(expected, verifier.verify(&block));
@ -485,13 +548,14 @@ mod tests {
.merkled_header().parent(genesis.hash()).build() .merkled_header().parent(genesis.hash()).build()
.build(); .build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip().signatures_skip(); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet).pow_skip().signatures_skip();
let expected = Err(Error::Transaction(2, TransactionError::Overspend)); let expected = Err(Error::Transaction(2, TransactionError::Overspend));
assert_eq!(expected, verifier.verify(&block)); assert_eq!(expected, verifier.verify(&block));
} }
#[test] #[test]
#[ignore]
fn coinbase_happy() { fn coinbase_happy() {
let path = RandomTempPath::create_dir(); let path = RandomTempPath::create_dir();
@ -528,7 +592,7 @@ mod tests {
.merkled_header().parent(best_hash).build() .merkled_header().parent(best_hash).build()
.build(); .build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip().signatures_skip(); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet).pow_skip().signatures_skip();
let expected = Ok(Chain::Main); let expected = Ok(Chain::Main);
@ -580,7 +644,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build() .merkled_header().parent(genesis.hash()).build()
.build(); .build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip().signatures_skip(); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet).pow_skip().signatures_skip();
let expected = Err(Error::MaximumSigops); let expected = Err(Error::MaximumSigops);
assert_eq!(expected, verifier.verify(&block)); assert_eq!(expected, verifier.verify(&block));
@ -606,7 +670,7 @@ mod tests {
.merkled_header().parent(genesis.hash()).build() .merkled_header().parent(genesis.hash()).build()
.build(); .build();
let verifier = ChainVerifier::new(Arc::new(storage)).pow_skip().signatures_skip(); let verifier = ChainVerifier::new(Arc::new(storage), Magic::Testnet).pow_skip().signatures_skip();
let expected = Err(Error::CoinbaseOverspend { let expected = Err(Error::CoinbaseOverspend {
expected_max: 5000000000, expected_max: 5000000000,

105
verification/src/compact.rs Normal file
View File

@ -0,0 +1,105 @@
use uint::U256;
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Compact(u32);
impl From<u32> for Compact {
fn from(u: u32) -> Self {
Compact(u)
}
}
impl From<Compact> for u32 {
fn from(c: Compact) -> Self {
c.0
}
}
impl Compact {
pub fn new(u: u32) -> Self {
Compact(u)
}
/// Computes the target [0, T] that a blockhash must land in to be valid
/// Returns None, if there is an overflow or its negative value
pub fn to_u256(&self) -> Result<U256, U256> {
let size = self.0 >> 24;
let mut word = self.0 & 0x007fffff;
let result = if size <= 3 {
word >>= 8 * (3 - size as usize);
word.into()
} else {
U256::from(word) << (8 * (size as usize - 3))
};
let is_negative = word != 0 && (self.0 & 0x00800000) != 0;
let is_overflow = (word != 0 && size > 34) ||
(word > 0xff && size > 33) ||
(word > 0xffff && size > 32);
if is_negative || is_overflow {
Err(result)
} else {
Ok(result)
}
}
pub fn from_u256(val: U256) -> Self {
let mut size = (val.bits() + 7) / 8;
let mut compact = if size <= 3 {
(val.low_u64() << (8 * (3 - size))) as u32
} else {
let bn = val >> (8 * (size - 3));
bn.low_u32()
};
if (compact & 0x00800000) != 0 {
compact >>= 8;
size += 1;
}
assert!((compact & !0x007fffff) == 0);
assert!(size < 256);
Compact(compact | (size << 24) as u32)
}
}
#[cfg(test)]
mod tests {
use uint::U256;
use super::Compact;
#[test]
fn test_compact_to_u256() {
assert_eq!(Compact::new(0x01003456).to_u256(), Ok(0.into()));
assert_eq!(Compact::new(0x01123456).to_u256(), Ok(0x12.into()));
assert_eq!(Compact::new(0x02008000).to_u256(), Ok(0x80.into()));
assert_eq!(Compact::new(0x05009234).to_u256(), Ok(0x92340000u64.into()));
// negative -0x12345600
assert!(Compact::new(0x04923456).to_u256().is_err());
assert_eq!(Compact::new(0x04123456).to_u256(), Ok(0x12345600u64.into()));
}
#[test]
fn test_from_u256() {
let test1 = U256::from(1000u64);
assert_eq!(Compact::new(0x0203e800), Compact::from_u256(test1));
let test2 = U256::from(2).pow(U256::from(256-32)) - U256::from(1);
assert_eq!(Compact::new(0x1d00ffff), Compact::from_u256(test2));
}
#[test]
fn test_compact_to_from_u256() {
// TODO: it does not work both ways for small values... check why
let compact = Compact::new(0x1d00ffff);
let compact2 = Compact::from_u256(compact.to_u256().unwrap());
assert_eq!(compact, compact2);
let compact = Compact::new(0x05009234);
let compact2 = Compact::from_u256(compact.to_u256().unwrap());
assert_eq!(compact, compact2);
}
}

View File

@ -1,25 +1,30 @@
//! Bitcoin blocks verification //! Bitcoin blocks verification
extern crate db; extern crate byteorder;
extern crate primitives;
extern crate chain;
extern crate serialization;
extern crate parking_lot; extern crate parking_lot;
extern crate linked_hash_map; extern crate linked_hash_map;
extern crate byteorder;
extern crate time; extern crate time;
extern crate script;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate db;
extern crate chain;
extern crate network;
extern crate primitives;
extern crate serialization;
extern crate script;
#[cfg(test)] #[cfg(test)]
extern crate ethcore_devtools as devtools; extern crate ethcore_devtools as devtools;
#[cfg(test)] #[cfg(test)]
extern crate test_data; extern crate test_data;
mod chain_verifier;
mod compact;
mod queue; mod queue;
mod utils; mod utils;
mod chain_verifier;
pub use primitives::{uint, hash};
pub use queue::Queue; pub use queue::Queue;
pub use chain_verifier::ChainVerifier; pub use chain_verifier::ChainVerifier;

View File

@ -1,13 +1,64 @@
#![allow(dead_code)]
//! Verification utilities //! Verification utilities
use primitives::hash::H256; use std::cmp;
use hash::H256;
use uint::U256;
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use chain;
use script::{self, Script}; use script::{self, Script};
use chain;
use compact::Compact;
const MAX_NBITS: u32 = 0x207fffff; // Timespan constants
const RETARGETING_FACTOR: u32 = 4;
const TARGET_SPACING_SECONDS: u32 = 10 * 60;
const DOUBLE_SPACING_SECONDS: u32 = 2 * TARGET_SPACING_SECONDS;
const TARGET_TIMESPAN_SECONDS: u32 = 2 * 7 * 24 * 60 * 60;
pub fn check_nbits(hash: &H256, n_bits: u32) -> bool { // The upper and lower bounds for retargeting timespan
if n_bits > MAX_NBITS { return false; } const MIN_TIMESPAN: u32 = TARGET_TIMESPAN_SECONDS / RETARGETING_FACTOR;
const MAX_TIMESPAN: u32 = TARGET_TIMESPAN_SECONDS * RETARGETING_FACTOR;
// Target number of blocks, 2 weaks, 2016
pub const RETARGETING_INTERVAL: u32 = TARGET_TIMESPAN_SECONDS / TARGET_SPACING_SECONDS;
pub fn is_retarget_height(height: u32) -> bool {
height % RETARGETING_INTERVAL == 0
}
fn retarget_timespan(retarget_timestamp: u32, last_timestamp: u32) -> u32 {
let timespan = last_timestamp - retarget_timestamp;
range_constrain(timespan as u32, MIN_TIMESPAN, MAX_TIMESPAN)
}
pub fn work_required_retarget(max_nbits: u32, retarget_timestamp: u32, last_timestamp: u32, last_nbits: u32) -> u32 {
// ignore overflows here
let mut retarget = Compact::new(last_nbits).to_u256().unwrap_or_else(|x| x);
let maximum = Compact::new(max_nbits).to_u256().unwrap_or_else(|x| x);
// multiplication overflow potential
retarget = retarget * U256::from(retarget_timespan(retarget_timestamp, last_timestamp));
retarget = retarget / U256::from(TARGET_TIMESPAN_SECONDS);
if retarget > maximum {
Compact::from_u256(maximum).into()
} else {
Compact::from_u256(retarget).into()
}
}
pub fn work_required_testnet() -> u32 {
unimplemented!();
}
fn range_constrain(value: u32, min: u32, max: u32) -> u32 {
cmp::min(cmp::max(value, min), max)
}
/// Simple nbits check that does not require 256-bit arithmetic
pub fn check_nbits(max_nbits: u32, hash: &H256, n_bits: u32) -> bool {
if n_bits > max_nbits {
return false;
}
let hash_bytes: &[u8] = &**hash; let hash_bytes: &[u8] = &**hash;
@ -84,9 +135,9 @@ pub fn p2sh_sigops(output: &Script, input_ref: &Script) -> usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use network::Magic;
use super::{block_reward_satoshi, check_nbits}; use super::{block_reward_satoshi, check_nbits};
use primitives::hash::H256; use hash::H256;
#[test] #[test]
fn reward() { fn reward() {
@ -102,29 +153,31 @@ mod tests {
#[test] #[test]
fn nbits() { fn nbits() {
let max_nbits = Magic::Regtest.max_nbits();
// strictly equal // strictly equal
let hash = H256::from_reversed_str("00000000000000001bc330000000000000000000000000000000000000000000"); let hash = H256::from_reversed_str("00000000000000001bc330000000000000000000000000000000000000000000");
let nbits = 0x181bc330u32; let nbits = 0x181bc330u32;
assert!(check_nbits(&hash, nbits)); assert!(check_nbits(max_nbits, &hash, nbits));
// nbits match but not equal (greater) // nbits match but not equal (greater)
let hash = H256::from_reversed_str("00000000000000001bc330000000000000000000000000000000000000000001"); let hash = H256::from_reversed_str("00000000000000001bc330000000000000000000000000000000000000000001");
let nbits = 0x181bc330u32; let nbits = 0x181bc330u32;
assert!(!check_nbits(&hash, nbits)); assert!(!check_nbits(max_nbits, &hash, nbits));
// greater // greater
let hash = H256::from_reversed_str("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); let hash = H256::from_reversed_str("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
let nbits = 0x181bc330u32; let nbits = 0x181bc330u32;
assert!(!check_nbits(&hash, nbits)); assert!(!check_nbits(max_nbits, &hash, nbits));
// some real examples // some real examples
let hash = H256::from_reversed_str("000000000000000001f942eb4bfa0aeccb6a14c268f4c72d5fff17270da771b9"); let hash = H256::from_reversed_str("000000000000000001f942eb4bfa0aeccb6a14c268f4c72d5fff17270da771b9");
let nbits = 404129525; let nbits = 404129525;
assert!(check_nbits(&hash, nbits)); assert!(check_nbits(max_nbits, &hash, nbits));
let hash = H256::from_reversed_str("00000000000000000e753ef636075711efd2cbf5a8473c7c5b67755a3701e0c2"); let hash = H256::from_reversed_str("00000000000000000e753ef636075711efd2cbf5a8473c7c5b67755a3701e0c2");
let nbits = 404129525; let nbits = 404129525;
assert!(check_nbits(&hash, nbits)); assert!(check_nbits(max_nbits, &hash, nbits));
} }
} }