Merge branch 'master' into p2p

This commit is contained in:
debris 2016-10-18 01:46:03 +02:00
commit f61f6de84c
17 changed files with 1026 additions and 173 deletions

6
.gitignore vendored
View File

@ -7,5 +7,11 @@
*.swn
*.DS_Store
# Visual Studio Code stuff
/.vscode
# GitEye stuff
/.project
# idea ide
.idea

43
.travis.yml Normal file
View File

@ -0,0 +1,43 @@
sudo: required
dist: trusty
language: rust
branches:
only:
- master
matrix:
fast_finish: false
include:
- rust: stable
cache:
apt: true
directories:
- $TRAVIS_BUILD_DIR/target
- $HOME/.cargo
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
- gcc-4.8
- g++-4.8
script:
- ./tools/test.sh
after_success: |
[ false ] &&
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
[ $TRAVIS_RUST_VERSION = stable ] &&
cargo doc --no-deps --verbose &&
echo '<meta http-equiv=refresh content=0;url=pbtc/index.html>' > target/doc/index.html &&
pip install --user ghp-import &&
/home/travis/.local/bin/ghp-import -n target/doc &&
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
env:
global:
- CXX="g++-4.8"
- CC="gcc-4.8"
- secure: IbTkR3KY7N9e3lc6hreWuY2mhuKyf7k6LSRUMMrOeb1k9TIncYMbR6Sl2W1t5+CJVp7WqVoUbP9YIH8o8a9BBuFldTyAz3tapa908KrZKIRlb/uCTef11z/jzasye4Ttj+358DBrCXZqueFw5Bh5Bnaie3r5bSYWTcQU693Bm/tKFp0lh4fSjzGyk7kRZ4V4rz2YHudZOcNIXN+QfY8aY6dxQraw82GUnNXJLzGiC5GJgeSV1uSnxMUM/lb4+zP1qRlcLBLnwHSTH/3vHH5xsOaRwHEYOQT9pTDyxMnJzDgYfjH4mNu3jSvZ7WFrP+Sza/yR+3sjHpOCFN6rsWc88iTq6Nwp+ESfyjHiLT+jqWs7r57sg2VfwHXuzoEW5GGagrIoF/pDEWJvhtNRYvdYevDtlZYPTfhSR4WOPkMPCg7Ln2W7a7vrSH2iRNxawUDnq3bOIyGGBeGwBJFDOEd6CmP+ojjUUm9L5I4berYACgYEDuZ4bpRX3WpGR1yAhSF4o5BTh+88EWU/VhL2ceXreHlztma3KQ5526Ip46lC4eLFrl/w64zaupesEZFjLsOFm7U9Vx9IM6aqBzOFr8Mt8DlPLaTRmRrbzPRYqHg0MLpHkH8S/HyNbK0xvqbQFRHQ6XPXNJVJXZPz8XPUYWYPK9ayplVdRN9nonHQO7F0gV8=

10
Cargo.lock generated
View File

@ -6,6 +6,7 @@ dependencies = [
"db 0.1.0",
"keys 0.1.0",
"message 0.1.0",
"miner 0.1.0",
"p2p 0.1.0",
"script 0.1.0",
]
@ -213,6 +214,15 @@ dependencies = [
"serialization 0.1.0",
]
[[package]]
name = "miner"
version = "0.1.0"
dependencies = [
"chain 0.1.0",
"primitives 0.1.0",
"serialization 0.1.0",
]
[[package]]
name = "mio"
version = "0.6.0"

View File

@ -9,6 +9,7 @@ description = "Parity bitcoin client."
clap = { version = "2", features = ["yaml"] }
keys = { path = "keys" }
message = { path = "message" }
miner = { path = "miner" }
p2p = { path = "p2p" }
script = { path = "script" }
db = { path = "db" }

View File

@ -1,6 +1,13 @@
# parity-bitcoin
The Parity Bitcoin client
[![Build Status][travis-image]][travis-url]
[Internal Documentation][doc-url]
![Graph][graph]
[graph]: https://github.com/ethcore/parity-bitcoin/blob/master/tools/graph.png
[travis-image]: https://travis-ci.com/ethcore/parity-bitcoin.svg?token=DMFvZu71iaTbUYx9UypX&branch=master
[travis-url]: https://travis-ci.com/ethcore/parity-bitcoin
[doc-url]: https://ethcore.github.io/parity-bitcoin/pbtc/index.html

View File

@ -6,7 +6,7 @@ use hex::FromHex;
use bytes::Bytes;
use ser::{
Deserializable, Reader, Error as ReaderError, deserialize,
Serializable, Stream, serialize
Serializable, Stream, serialize, serialized_list_size
};
use crypto::dhash256;
use hash::H256;
@ -42,6 +42,11 @@ impl Serializable for OutPoint {
.append(&self.hash)
.append(&self.index);
}
#[inline]
fn serialized_size(&self) -> usize {
self.hash.serialized_size() + self.index.serialized_size()
}
}
impl Deserializable for OutPoint {
@ -79,6 +84,13 @@ impl Serializable for TransactionInput {
.append(&self.script_sig)
.append(&self.sequence);
}
#[inline]
fn serialized_size(&self) -> usize {
self.previous_output.serialized_size() +
self.script_sig.serialized_size() +
self.sequence.serialized_size()
}
}
impl Deserializable for TransactionInput {
@ -119,6 +131,12 @@ impl Serializable for TransactionOutput {
.append(&self.value)
.append(&self.script_pubkey);
}
#[inline]
fn serialized_size(&self) -> usize {
self.value.serialized_size() +
self.script_pubkey.serialized_size()
}
}
impl Deserializable for TransactionOutput {
@ -167,6 +185,14 @@ impl Serializable for Transaction {
.append_list(&self.outputs)
.append(&self.lock_time);
}
#[inline]
fn serialized_size(&self) -> usize {
self.version.serialized_size() +
serialized_list_size(&self.inputs) +
serialized_list_size(&self.outputs) +
self.lock_time.serialized_size()
}
}
impl Deserializable for Transaction {
@ -205,6 +231,7 @@ impl Transaction {
#[cfg(test)]
mod tests {
use hash::H256;
use ser::Serializable;
use super::Transaction;
// real transaction from block 80000
@ -231,4 +258,11 @@ mod tests {
let hash = H256::from_reversed_str("5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2");
assert_eq!(t.hash(), hash);
}
#[test]
fn test_transaction_serialized_len() {
let raw_tx: &'static str = "0100000001a6b97044d03da79c005b20ea9c0e1a6d9dc12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac00000000";
let tx: Transaction = raw_tx.into();
assert_eq!(tx.serialized_size(), raw_tx.len() / 2);
}
}

9
miner/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "miner"
version = "0.1.0"
authors = ["Ethcore <admin@ethcore.io>"]
[dependencies]
chain = { path = "../chain" }
primitives = { path = "../primitives" }
serialization = { path = "../serialization" }

9
miner/src/lib.rs Normal file
View File

@ -0,0 +1,9 @@
extern crate chain;
extern crate primitives;
extern crate serialization as ser;
pub mod memory_pool;
pub use primitives::{hash};
pub use self::memory_pool::{MemoryPool, Information as MemoryPoolInformation};

647
miner/src/memory_pool.rs Normal file
View File

@ -0,0 +1,647 @@
//! Transactions memory pool
//!
//! `MemoryPool` keeps track of all transactions seen by the node (received from other peers) and own transactions
//! and orders them by given strategies. It works like multi-indexed priority queue, giving option to pop 'top'
//! transactions.
//! 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.
use std::rc::Rc;
use hash::H256;
use chain::Transaction;
use std::collections::HashMap;
use std::collections::HashSet;
use ser::Serializable;
/// Transactions ordering strategy
#[derive(Debug)]
pub enum OrderingStrategy {
/// Order transactions by their timestamp
ByTimestamp,
/// Order transactions by miner score
ByMinerScore,
}
/// Information on current `MemoryPool` state
#[derive(Debug)]
pub struct Information {
/// The number of transactions currently in the `MemoryPool`
pub transactions_count: usize,
/// The total number of bytes in the transactions in the `MemoryPool`
pub transactions_size_in_bytes: usize,
}
/// Transactions memory pool
#[derive(Debug)]
pub struct MemoryPool {
/// Transactions storage
storage: Storage,
}
/// Single entry
#[derive(Debug)]
pub struct Entry {
/// Transaction
transaction: Transaction,
/// Transaction ancestors hashes
ancestors: Rc<HashSet<H256>>,
/// Transaction hash (stored for effeciency)
hash: H256,
/// Transaction size (stored for effeciency)
size: usize,
/// 'Timestamp' when transaction has entered memory pool
timestamp: u64,
/// Transaction fee (stored for efficiency)
miner_fee: i64,
/// Virtual transaction fee (a way to prioritize/penalize transaction)
miner_virtual_fee: i64,
}
/// Multi-index transactions storage
#[derive(Debug)]
struct Storage {
/// Transactions counter (for timestamp ordering)
counter: u64,
/// Total transactions size (when serialized) in bytes
transactions_size_in_bytes: usize,
/// By-input storage
by_input: HashMap<H256, HashSet<H256>>,
/// By-hash storage
by_hash: HashMap<H256, Entry>,
/// By-entry-time storage
by_timestamp: timestamp_strategy::Storage,
/// By-score storage
by_miner_score: miner_score_strategy::Storage,
}
macro_rules! ordering_strategy {
($strategy: ident; $($member: ident: $member_type: ty), *; $comparer: expr) => {
mod $strategy {
use std::rc::Rc;
use std::cmp::Ordering;
use hash::H256;
use std::collections::HashSet;
use std::collections::BTreeSet;
use super::Entry;
/// Lightweight struct maintain transactions ordering
#[derive(Debug, Eq, PartialEq)]
pub struct OrderedEntry {
/// Transaction hash
hash: H256,
/// Transaction ancestors
ancestors: Rc<HashSet<H256>>,
/// Transaction data
$($member: $member_type), *
}
impl OrderedEntry {
pub fn for_entry(entry: &Entry) -> OrderedEntry {
OrderedEntry {
hash: entry.hash.clone(),
ancestors: entry.ancestors.clone(),
$($member: entry.$member.clone()), *
}
}
}
impl PartialOrd for OrderedEntry {
fn partial_cmp(&self, other: &OrderedEntry) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OrderedEntry {
fn cmp(&self, other: &Self) -> Ordering {
if self.ancestors.contains(&other.hash) {
return Ordering::Greater
}
if other.ancestors.contains(&self.hash) {
return Ordering::Less
}
let order = $comparer(&self, other);
if order != Ordering::Equal {
return order
}
self.hash.cmp(&other.hash)
}
}
/// By-timestamp ordering storage
#[derive(Debug)]
pub struct Storage {
data: BTreeSet<OrderedEntry>,
}
impl Storage {
pub fn new() -> Self {
Storage {
data: BTreeSet::new()
}
}
/// Insert entry to storage
pub fn insert(&mut self, entry: &Entry) {
self.data.replace(OrderedEntry::for_entry(entry));
}
/// Remove entry from storage
pub fn remove(&mut self, entry: &Entry) {
self.data.remove(&OrderedEntry::for_entry(entry));
}
/// An iterator that takes first n entries using predicate
pub fn take(&self, n: usize) -> Vec<H256> {
self.data.iter()
.map(|ref entry| entry.hash.clone())
.take(n)
.collect()
}
}
}
}
}
ordering_strategy!(timestamp_strategy;
timestamp: u64;
|me: &Self, other: &Self|
me.timestamp.cmp(&other.timestamp));
ordering_strategy!(miner_score_strategy;
size: usize, miner_fee: i64, miner_virtual_fee: i64;
|me: &Self, other: &Self| {
// lesser miner score means later removal
let left = (me.miner_fee + me.miner_virtual_fee) * (other.size as i64);
let right = (other.miner_fee + other.miner_virtual_fee) * (me.size as i64);
right.cmp(&left)
});
macro_rules! insert_to_orderings {
($me: expr, $entry: expr) => (
$me.by_timestamp.insert(&$entry);
$me.by_miner_score.insert(&$entry);
)
}
macro_rules! remove_from_orderings {
($me: expr, $entry: expr) => (
$me.by_timestamp.remove(&$entry);
$me.by_miner_score.remove(&$entry);
)
}
impl Storage {
pub fn new() -> Self {
Storage {
counter: 0,
transactions_size_in_bytes: 0,
by_input: HashMap::new(),
by_hash: HashMap::new(),
by_timestamp: timestamp_strategy::Storage::new(),
by_miner_score: miner_score_strategy::Storage::new(),
}
}
pub fn insert(&mut self, entry: Entry) {
self.transactions_size_in_bytes += entry.size;
self.update_by_input_on_insert(&entry);
self.update_descendants_on_insert(&entry);
insert_to_orderings!(self, entry);
self.by_hash.insert(entry.hash.clone(), entry);
}
pub fn remove(&mut self, entry: &Entry) -> Option<Entry> {
self.transactions_size_in_bytes -= entry.size;
self.update_descendants_on_remove(&entry);
self.update_by_input_on_remove(&entry);
remove_from_orderings!(self, entry);
self.by_hash.remove(&entry.hash)
}
pub fn get_by_hash(&self, h: &H256) -> Option<&Entry> {
self.by_hash.get(h)
}
pub fn contains(&self, hash: &H256) -> bool {
self.by_hash.contains_key(hash)
}
pub fn remove_by_hash(&mut self, h: &H256) -> Option<Entry> {
self.by_hash.remove(h)
.map(|entry| {
self.remove(&entry);
entry
})
}
pub fn remove_n_with_strategy(&mut self, n: usize, strategy: OrderingStrategy) -> Vec<Transaction> {
let hashes = {
match strategy {
OrderingStrategy::ByTimestamp => self.by_timestamp.take(n),
OrderingStrategy::ByMinerScore => self.by_miner_score.take(n),
}
};
hashes
.iter()
.map(|ref hash| {
let entry = self.remove_by_hash(&hash)
.expect("`hash` is read from by_* index; all hashes from indexes are also stored in `by_hash`; remove returns entry from `by_hash`; qed");
entry.transaction
})
.collect()
}
pub fn set_virtual_fee(&mut self, h: &H256, virtual_fee: i64) {
if let Some(ref mut entry) = self.by_hash.get_mut(h) {
self.by_miner_score.remove(&entry);
entry.miner_virtual_fee = virtual_fee;
self.by_miner_score.insert(&entry);
}
}
pub fn get_transactions_ids(&self) -> Vec<H256> {
self.by_hash.keys().map(|h| h.clone()).collect()
}
fn update_by_input_on_insert(&mut self, entry: &Entry) {
// maintain map { ancestor: list<descendants> } to support inserting
// acendants & descendants in random order
for input_hash in entry.transaction.inputs.iter().map(|input| &input.previous_output.hash) {
self.by_input.entry(input_hash.clone()).or_insert_with(|| HashSet::new()).insert(entry.hash.clone());
}
}
fn update_descendants_on_insert(&mut self, entry: &Entry) {
// this code will run only when ancestor transaction is inserted
// in memory pool after its descendants
if let Some(descendants) = self.by_input.get(&entry.hash) {
for descendant in descendants.iter() {
if let Some(mut descendant_entry) = self.by_hash.remove(descendant) {
{
remove_from_orderings!(self, descendant_entry);
let ancestors = Rc::make_mut(&mut descendant_entry.ancestors);
ancestors.insert(entry.hash.clone());
}
insert_to_orderings!(self, descendant_entry);
self.by_hash.insert(descendant_entry.hash.clone(), descendant_entry);
};
}
}
}
fn update_by_input_on_remove(&mut self, entry: &Entry) {
for input_hash in entry.transaction.inputs.iter().map(|input| &input.previous_output.hash) {
let remove_entry = {
let by_input_item = self.by_input.get_mut(input_hash)
.expect("`entry.transaction` is immutable; on insert all transaction' inputs are stored in `by_input`; `by_input` only modified here; qed");
by_input_item.remove(&entry.hash);
by_input_item.is_empty()
};
if remove_entry {
self.by_input.remove(input_hash);
}
}
}
fn update_descendants_on_remove(&mut self, entry: &Entry) {
// this code will run when there still are dependent transactions in the pool
if let Some(descendants) = self.by_input.get(&entry.hash) {
for descendant in descendants.iter() {
if let Some(mut descendant_entry) = self.by_hash.remove(descendant) {
{
remove_from_orderings!(self, descendant_entry);
let ancestors = Rc::make_mut(&mut descendant_entry.ancestors);
ancestors.remove(&entry.hash);
}
insert_to_orderings!(self, descendant_entry);
self.by_hash.insert(descendant_entry.hash.clone(), descendant_entry);
};
}
}
}
}
impl MemoryPool {
/// Creates new memory pool
pub fn new() -> Self {
MemoryPool {
storage: Storage::new(),
}
}
/// Insert verified transaction to the `MemoryPool`
pub fn insert_verified(&mut self, t: Transaction) {
let entry = self.make_entry(t);
self.storage.insert(entry);
}
/// Removes single transaction by its hash.
/// All descedants remain in the pool.
pub fn remove_by_hash(&mut self, h: &H256) -> Option<Transaction> {
self.storage.remove_by_hash(h).map(|entry| entry.transaction)
}
/// Removes up to n transactions the `MemoryPool`, using selected strategy.
/// Ancestors are always removed before descendant transactions.
pub fn remove_n_with_strategy(&mut self, n: usize, strategy: OrderingStrategy) -> Vec<Transaction> {
self.storage.remove_n_with_strategy(n, strategy)
}
/// Set miner virtual fee for transaction
pub fn set_virtual_fee(&mut self, h: &H256, virtual_fee: i64) {
self.storage.set_virtual_fee(h, virtual_fee)
}
/// Get transaction by hash
pub fn get(&self, hash: &H256) -> Option<&Transaction> {
self.storage.get_by_hash(hash).map(|ref entry| &entry.transaction)
}
/// Checks if transaction is in the mempool
pub fn contains(&self, hash: &H256) -> bool {
self.storage.contains(hash)
}
/// Returns information on `MemoryPool` (as in GetMemPoolInfo RPC)
/// https://bitcoin.org/en/developer-reference#getmempoolinfo
pub fn information(&self) -> Information {
Information {
transactions_count: self.storage.by_hash.len(),
transactions_size_in_bytes: self.storage.transactions_size_in_bytes
}
}
/// Returns TXIDs of all transactions in `MemoryPool` (as in GetRawMemPool RPC)
/// https://bitcoin.org/en/developer-reference#getrawmempool
pub fn get_transactions_ids(&self) -> Vec<H256> {
self.storage.get_transactions_ids()
}
fn make_entry(&mut self, t: Transaction) -> Entry {
let hash = t.hash();
let ancestors = Rc::new(self.get_ancestors(&t));
let size = self.get_transaction_size(&t);
let timestamp = self.get_timestamp();
let miner_fee = self.get_transaction_miner_fee(&t);
Entry {
transaction: t,
hash: hash,
ancestors: ancestors,
size: size,
timestamp: timestamp,
miner_fee: miner_fee,
miner_virtual_fee: 0,
}
}
fn get_ancestors(&self, t: &Transaction) -> HashSet<H256> {
let mut ancestors: HashSet<H256> = HashSet::new();
let ancestors_entries = t.inputs.iter()
.filter_map(|ref input| self.storage.get_by_hash(&input.previous_output.hash));
for ancestor_entry in ancestors_entries {
ancestors.insert(ancestor_entry.hash.clone());
for grand_ancestor in ancestor_entry.ancestors.iter() {
ancestors.insert(grand_ancestor.clone());
}
}
ancestors
}
fn get_transaction_size(&self, t: &Transaction) -> usize {
t.serialized_size()
}
fn get_transaction_miner_fee(&self, t: &Transaction) -> i64 {
let input_value = 0; // TODO: sum all inputs of transaction
let output_value = t.outputs.iter().fold(0, |acc, ref output| acc + output.value);
(output_value - input_value) as i64
}
#[cfg(not(test))]
fn get_timestamp(&mut self) -> u64 {
self.storage.counter += 1;
self.storage.counter
}
#[cfg(test)]
fn get_timestamp(&self) -> u64 {
(self.storage.by_hash.len() % 3usize) as u64
}
}
#[cfg(test)]
mod tests {
use std::cmp::Ordering;
use chain::Transaction;
use super::{MemoryPool, OrderingStrategy};
// output_value = 898126612, size = 225, miner_score ~ 3991673.83
const RAW_TRANSACTION1: &'static str = "01000000017e4e1bfa4cc16a46593390b9f627db9a3b7c3b1802daa826fc0c477c067ea4f1000000006a47304402203cca1b01b307d3dba3d4f819ef4e9ccf839fa7ef901fc39d2b1d6e33c159a0b0022009d135bd47b8465a69f4db7145af34e0f063e926d95d9c21fb4e8cbc2052838c0121039c01d413f0e296cb766b408c528d3526e75a0f63cfc44c1147160613a12e6cb7feffffff02c8f27535000000001976a914e54788b730b91eb9b29917fa10ddbc97996a987988ac4c601200000000001976a91421e8ed0ddc9a365c10fa3130c91c36237a45848888aca7a00600";
// output_value = 423675406, size = 225, miner_score ~ 1883001.80
const RAW_TRANSACTION2: &'static str = "0100000001efaf295b354d8063336a03652664e31b63666f6fbe51b377ad3bdd7b65678a43000000006a47304402206100084828cfc2b71881f1a99447658d5844043f69c1f9bd6c95cb0e11197d4002207c8e23b6233e7317fe0db3e12c8a4291efe671e02c4bbeeb3534e208bb9a3a75012103e091c9c970427a709646990ca16d4d418efc9e5c46ae794c3d09023a7e0e1c57feffffff0226662e19000000001976a914ffad82403bc2a1dd3789b7e653d352515ae86b7288ace85f1200000000001976a914c2f8a6513ebcf6f61edca10199442e33108d540988aca7a00600";
// output_value = 668388826, size = 225, miner_score ~ 2970617.00
const RAW_TRANSACTION3: &'static str = "01000000012eee0922c7385e6a11d4f92da65e16b205b2cdfe294c3140c3bc0427f6c11794000000006a47304402207aab34b1c9bb5464c3c1ddf9fee042d8755b81ed1e8c35b895ee2d7da17a23ac02205dfd9c8d14f44951c9e8c3a70f8820a6b113046db1ca25d52a31a4b68d62e07901210306a47c3c0ce5ad78616ad0695113ee4d7a848155fd9f4a1eb7aeed756e174211feffffff024c601200000000001976a914ae829d4d1a8945dc165a57f1149b4656e48c161988ac8e6dc427000000001976a9146c4f1f52adec5211f456acc12917fe902949b08088aca7a00600";
// output_value = 256505044, size = 226, miner_score ~ 1134978.07
const RAW_TRANSACTION4: &'static str = "01000000012969032b2a920c03fa71eeea5250d0f6259b130a15ed695f79d957e47b9b2d8b000000006b483045022100b34c8a714b295b84686211b37daaa65ef79cf0ce1dc7d7a4e133a5b08a308f6f02201abca9e08bddb56e205bd1c5e689419926031ec955d8c89671a16a7076dce0ec0121020db95d68c760d1e0a5090dbf0ed2cbfd1225c3bc46d4e31e272bcc14b42a9643feffffff021896370f000000001976a914552b2549642c22462dc0e82ea25500dea6bb5e2188acbc5e1200000000001976a914dca40d275f5eb00cefab813925a1b07b9b77159188aca7a00600";
const RAW_TRANSACTION1_HASH: &'static str = "af9d5d566b6c914fc0de758a5f18731f5570ac59d1f0c5d1516f0f2dda2c3f79";
const RAW_TRANSACTION2_HASH: &'static str = "e97719095d91821f691dcbebdf5fb82c4eff8dd33d0c3cc6690aae37ed82a01e";
const RAW_TRANSACTION3_HASH: &'static str = "2d3833b35efc8f2b9d1c2140505b207fd71155178ad604e03364448f7007fc04";
const RAW_TRANSACTION4_HASH: &'static str = "4a15e0b41b1a47381f2f2baa16087688d1cd9078416960deed1faae852a469ce";
fn construct_memory_pool() -> MemoryPool {
let transaction1: Transaction = RAW_TRANSACTION1.into();
let transaction2: Transaction = RAW_TRANSACTION2.into();
let transaction3: Transaction = RAW_TRANSACTION3.into();
let transaction4: Transaction = RAW_TRANSACTION4.into();
let transaction1_hash = RAW_TRANSACTION1_HASH.into();
let transaction4_hash = RAW_TRANSACTION4_HASH.into();
assert_eq!(transaction1.hash(), transaction1_hash);
assert_eq!(transaction2.hash(), RAW_TRANSACTION2_HASH.into());
assert_eq!(transaction3.hash(), RAW_TRANSACTION3_HASH.into());
assert_eq!(transaction4.hash(), transaction4_hash);
// hash of t4 must be lesser than hash of t4
assert_eq!(transaction4_hash.cmp(&transaction1_hash), Ordering::Less);
let mut pool = MemoryPool::new();
pool.insert_verified(RAW_TRANSACTION1.into());
pool.insert_verified(RAW_TRANSACTION2.into());
pool.insert_verified(RAW_TRANSACTION3.into());
pool.insert_verified(RAW_TRANSACTION4.into());
pool
}
#[test]
fn test_memory_pool_remove_by_hash() {
let mut pool = construct_memory_pool();
let pool_transactions = pool.get_transactions_ids();
assert_eq!(pool_transactions.len(), 4);
// check pool transactions
let ref hash_to_remove = pool_transactions[0];
assert!(*hash_to_remove == RAW_TRANSACTION1_HASH.into()
|| *hash_to_remove == RAW_TRANSACTION2_HASH.into()
|| *hash_to_remove == RAW_TRANSACTION3_HASH.into()
|| *hash_to_remove == RAW_TRANSACTION4_HASH.into());
assert_eq!(pool.get_transactions_ids().len(), 4);
// remove and check remaining transactions
let removed = pool.remove_by_hash(&hash_to_remove);
assert!(removed.is_some());
assert_eq!(removed.unwrap().hash(), *hash_to_remove);
assert_eq!(pool.get_transactions_ids().len(), 3);
}
#[test]
fn test_memory_pool_transaction_dependent_transactions_parent_after_child() {
let parent_transaction: Transaction = "00000000000164000000000000000000000000".into();
let child_transaction: Transaction = "0000000001545ac9cffeaa3ee074f08a5306e703cb30883192ed9b10ee9ddb76824e4985070000000000000000000000000000".into();
let grandchild_transaction: Transaction = "0000000001cc1d8279403880bfdd7682c28ed8441a138f96ae1dc5fd90bc928b88d48107a90000000000000000000164000000000000000000000000".into();
let parent_transaction_hash = parent_transaction.hash();
let child_transaction_hash = child_transaction.hash();
let grandchild_transaction_hash = grandchild_transaction.hash();
// insert child, then parent
let mut pool = MemoryPool::new();
pool.insert_verified(grandchild_transaction); // timestamp 0
pool.insert_verified(child_transaction); // timestamp 1
pool.insert_verified(parent_transaction); // timestamp 2
// check that parent transaction was removed before child trnasaction
let transactions = pool.remove_n_with_strategy(3, OrderingStrategy::ByTimestamp);
assert_eq!(transactions.len(), 3);
assert_eq!(transactions[0].hash(), parent_transaction_hash);
assert_eq!(transactions[1].hash(), child_transaction_hash);
assert_eq!(transactions[2].hash(), grandchild_transaction_hash);
}
#[test]
fn test_memory_pool_transaction_dependent_transactions_parent_before_child() {
let parent_transaction: Transaction = "00000000000164000000000000000000000000".into();
let child_transaction: Transaction = "0000000001545ac9cffeaa3ee074f08a5306e703cb30883192ed9b10ee9ddb76824e4985070000000000000000000164000000000000000000000000".into();
let grandchild_transaction: Transaction = "0000000001cc1d8279403880bfdd7682c28ed8441a138f96ae1dc5fd90bc928b88d48107a90000000000000000000164000000000000000000000000".into();
let parent_transaction_hash = parent_transaction.hash();
let child_transaction_hash = child_transaction.hash();
let grandchild_transaction_hash = grandchild_transaction.hash();
// insert child, then parent
let mut pool = MemoryPool::new();
pool.insert_verified(parent_transaction); // timestamp 0
pool.insert_verified(child_transaction); // timestamp 1
pool.insert_verified(grandchild_transaction); // timestamp 2
// check that parent transaction was removed before child trnasaction
let transactions = pool.remove_n_with_strategy(3, OrderingStrategy::ByTimestamp);
assert_eq!(transactions.len(), 3);
assert_eq!(transactions[0].hash(), parent_transaction_hash);
assert_eq!(transactions[1].hash(), child_transaction_hash);
assert_eq!(transactions[2].hash(), grandchild_transaction_hash);
}
#[test]
fn test_memory_pool_transaction_dependent_transactions_insert_after_remove_by_hash() {
let raw_parent_transaction = "00000000000164000000000000000000000000";
let raw_child_transaction = "0000000001545ac9cffeaa3ee074f08a5306e703cb30883192ed9b10ee9ddb76824e4985070000000000000000000164000000000000000000000000";
let raw_grandchild_transaction = "0000000001cc1d8279403880bfdd7682c28ed8441a138f96ae1dc5fd90bc928b88d48107a90000000000000000000164000000000000000000000000";
let parent_transaction: Transaction = raw_parent_transaction.into();
let child_transaction: Transaction = raw_child_transaction.into();
let grandchild_transaction: Transaction = raw_grandchild_transaction.into();
let parent_transaction_hash = parent_transaction.hash();
let child_transaction_hash = child_transaction.hash();
let grandchild_transaction_hash = grandchild_transaction.hash();
// insert child, then parent
let mut pool = MemoryPool::new();
pool.insert_verified(parent_transaction);
pool.insert_verified(child_transaction);
pool.insert_verified(grandchild_transaction);
// remove child transaction & make sure that other transactions are still there
pool.remove_by_hash(&child_transaction_hash);
assert_eq!(pool.get_transactions_ids().len(), 2);
// insert child transaction back to the pool & assert transactions are removed in correct order
pool.insert_verified(raw_child_transaction.into());
let transactions = pool.remove_n_with_strategy(3, OrderingStrategy::ByMinerScore);
assert_eq!(transactions.len(), 3);
assert_eq!(transactions[0].hash(), parent_transaction_hash);
assert_eq!(transactions[1].hash(), child_transaction_hash);
assert_eq!(transactions[2].hash(), grandchild_transaction_hash);
}
#[test]
fn test_memory_pool_get_information() {
let mut pool = construct_memory_pool();
let pool_sizes = [901, 676, 451, 226, 0];
let removals = [RAW_TRANSACTION1_HASH, RAW_TRANSACTION2_HASH, RAW_TRANSACTION3_HASH, RAW_TRANSACTION4_HASH];
// check pool information after removing each transaction
for i in 0..pool_sizes.len() {
let expected_pool_count = 5 - i - 1;
let expected_pool_size = pool_sizes[i];
let info = pool.information();
assert_eq!(info.transactions_count, expected_pool_count);
assert_eq!(info.transactions_size_in_bytes, expected_pool_size);
if expected_pool_size != 0 {
pool.remove_by_hash(&removals[i].into());
}
}
}
#[test]
fn test_memory_pool_timestamp_ordering_strategy() {
let mut pool = construct_memory_pool();
// remove transactions [4, 1, 2] (timestamps: [0, 0, 1])
let transactions = pool.remove_n_with_strategy(3, OrderingStrategy::ByTimestamp);
assert_eq!(transactions.len(), 3);
assert_eq!(transactions[0].hash(), RAW_TRANSACTION4_HASH.into());
assert_eq!(transactions[1].hash(), RAW_TRANSACTION1_HASH.into());
assert_eq!(transactions[2].hash(), RAW_TRANSACTION2_HASH.into());
assert_eq!(pool.get_transactions_ids().len(), 1);
// remove transactions [3] (timestamps: [2])
let transactions = pool.remove_n_with_strategy(3, OrderingStrategy::ByTimestamp);
assert_eq!(transactions.len(), 1);
assert_eq!(transactions[0].hash(), RAW_TRANSACTION3_HASH.into());
}
#[test]
fn test_memory_pool_miner_score_ordering_strategy() {
let mut pool = construct_memory_pool();
let transactions = pool.remove_n_with_strategy(4, OrderingStrategy::ByMinerScore);
assert_eq!(transactions.len(), 4);
assert_eq!(transactions[0].hash(), RAW_TRANSACTION1_HASH.into());
assert_eq!(transactions[1].hash(), RAW_TRANSACTION3_HASH.into());
assert_eq!(transactions[2].hash(), RAW_TRANSACTION2_HASH.into());
assert_eq!(transactions[3].hash(), RAW_TRANSACTION4_HASH.into());
assert_eq!(pool.get_transactions_ids().len(), 0);
}
#[test]
fn test_memory_pool_miner_score_ordering_strategy_with_virtual_fee() {
let mut pool = construct_memory_pool();
// increase miner score of transaction 4 to move it to position #1
pool.set_virtual_fee(&RAW_TRANSACTION4_HASH.into(), 1000000000);
// decrease miner score of transaction 3 to move it to position #4
pool.set_virtual_fee(&RAW_TRANSACTION3_HASH.into(), -500000000);
let transactions = pool.remove_n_with_strategy(4, OrderingStrategy::ByMinerScore);
assert_eq!(transactions.len(), 4);
assert_eq!(transactions[0].hash(), RAW_TRANSACTION4_HASH.into());
assert_eq!(transactions[1].hash(), RAW_TRANSACTION1_HASH.into());
assert_eq!(transactions[2].hash(), RAW_TRANSACTION2_HASH.into());
assert_eq!(transactions[3].hash(), RAW_TRANSACTION3_HASH.into());
assert_eq!(pool.remove_n_with_strategy(1, OrderingStrategy::ByMinerScore).len(), 0);
}
}

View File

@ -1,4 +1,5 @@
use std::{fmt, ops, cmp, str};
use std::hash::{Hash, Hasher};
use hex::{ToHex, FromHex, FromHexError};
macro_rules! impl_hash {
@ -96,6 +97,8 @@ macro_rules! impl_hash {
}
}
impl Eq for $name {}
impl cmp::PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
let self_ref: &[u8] = &self.0;
@ -104,6 +107,13 @@ macro_rules! impl_hash {
}
}
impl Hash for $name {
fn hash<H>(&self, state: &mut H) where H: Hasher {
state.write(&self.0);
state.finish();
}
}
impl $name {
pub fn reversed(&self) -> Self {
let mut result = self.clone();

View File

@ -82,6 +82,15 @@ impl Serializable for CompactInteger {
}
}
}
fn serialized_size(&self) -> usize {
match self.0 {
0...0xfc => 1,
0xfd...0xffff => 3,
0x10000...0xffff_ffff => 5,
_ => 9,
}
}
}
impl Deserializable for CompactInteger {

View File

@ -9,6 +9,11 @@ impl Serializable for bool {
fn serialize(&self, s: &mut Stream) {
s.write_u8(*self as u8).unwrap();
}
#[inline]
fn serialized_size(&self) -> usize {
1
}
}
impl Serializable for i32 {
@ -16,6 +21,11 @@ impl Serializable for i32 {
fn serialize(&self, s: &mut Stream) {
s.write_i32::<LittleEndian>(*self).unwrap();
}
#[inline]
fn serialized_size(&self) -> usize {
4
}
}
impl Serializable for i64 {
@ -23,6 +33,11 @@ impl Serializable for i64 {
fn serialize(&self, s: &mut Stream) {
s.write_i64::<LittleEndian>(*self).unwrap();
}
#[inline]
fn serialized_size(&self) -> usize {
8
}
}
impl Serializable for u8 {
@ -30,6 +45,11 @@ impl Serializable for u8 {
fn serialize(&self, s: &mut Stream) {
s.write_u8(*self).unwrap();
}
#[inline]
fn serialized_size(&self) -> usize {
1
}
}
impl Serializable for u16 {
@ -37,6 +57,11 @@ impl Serializable for u16 {
fn serialize(&self, s: &mut Stream) {
s.write_u16::<LittleEndian>(*self).unwrap();
}
#[inline]
fn serialized_size(&self) -> usize {
2
}
}
impl Serializable for u32 {
@ -44,6 +69,11 @@ impl Serializable for u32 {
fn serialize(&self, s: &mut Stream) {
s.write_u32::<LittleEndian>(*self).unwrap();
}
#[inline]
fn serialized_size(&self) -> usize {
4
}
}
impl Serializable for u64 {
@ -51,6 +81,11 @@ impl Serializable for u64 {
fn serialize(&self, s: &mut Stream) {
s.write_u64::<LittleEndian>(*self).unwrap();
}
#[inline]
fn serialized_size(&self) -> usize {
8
}
}
impl Deserializable for bool {
@ -114,6 +149,12 @@ impl Serializable for String {
.append(&CompactInteger::from(bytes.len()))
.append_slice(bytes);
}
#[inline]
fn serialized_size(&self) -> usize {
let bytes: &[u8] = self.as_ref();
CompactInteger::from(bytes.len()).serialized_size() + bytes.len()
}
}
impl Deserializable for String {
@ -129,6 +170,11 @@ macro_rules! impl_ser_for_hash {
fn serialize(&self, stream: &mut Stream) {
stream.append_slice(&**self);
}
#[inline]
fn serialized_size(&self) -> usize {
$size
}
}
impl Deserializable for $name {
@ -157,6 +203,11 @@ impl Serializable for Bytes {
.append(&CompactInteger::from(self.len()))
.append_slice(&self);
}
#[inline]
fn serialized_size(&self) -> usize {
CompactInteger::from(self.len()).serialized_size() + self.len()
}
}
impl Deserializable for Bytes {

View File

@ -10,4 +10,4 @@ pub use primitives::{hash, bytes};
pub use compact_integer::CompactInteger;
pub use self::reader::{Reader, Deserializable, deserialize, Error};
pub use self::stream::{Stream, Serializable, serialize};
pub use self::stream::{Stream, Serializable, serialize, serialized_list_size};

View File

@ -9,9 +9,20 @@ pub fn serialize(t: &Serializable) -> Bytes {
stream.out()
}
pub fn serialized_list_size<T>(t: &[T]) -> usize where T: Serializable {
CompactInteger::from(t.len()).serialized_size() +
t.iter().map(Serializable::serialized_size).sum::<usize>()
}
pub trait Serializable {
/// Serialize the struct and appends it to the end of stream.
fn serialize(&self, s: &mut Stream);
/// Hint about the size of serialized struct.
fn serialized_size(&self) -> usize where Self: Sized {
// fallback implementation
serialize(self).len()
}
}
/// Stream used for serialization.

View File

@ -4,199 +4,204 @@ digraph dependencies {
N2[label="db",shape=box];
N3[label="keys",shape=box];
N4[label="message",shape=box];
N5[label="p2p",shape=box];
N6[label="script",shape=box];
N7[label="ansi_term",shape=box];
N8[label="arrayvec",shape=box];
N9[label="nodrop",shape=box];
N10[label="odds",shape=box];
N11[label="base58",shape=box];
N12[label="bitcrypto",shape=box];
N13[label="primitives",shape=box];
N14[label="rust-crypto",shape=box];
N15[label="bitflags v0.4.0",shape=box];
N16[label="bitflags v0.7.0",shape=box];
N17[label="byteorder",shape=box];
N18[label="cfg-if",shape=box];
N19[label="chain",shape=box];
N20[label="rustc-serialize",shape=box];
N21[label="serialization",shape=box];
N22[label="libc",shape=box];
N23[label="strsim",shape=box];
N24[label="term_size",shape=box];
N25[label="unicode-segmentation",shape=box];
N26[label="unicode-width",shape=box];
N27[label="vec_map",shape=box];
N28[label="yaml-rust",shape=box];
N29[label="crossbeam",shape=box];
N30[label="elastic-array",shape=box];
N31[label="ethcore-devtools",shape=box];
N32[label="parking_lot",shape=box];
N33[label="rocksdb",shape=box];
N34[label="deque",shape=box];
N35[label="rand",shape=box];
N36[label="eth-secp256k1",shape=box];
N37[label="gcc",shape=box];
N38[label="futures",shape=box];
N39[label="log",shape=box];
N40[label="futures-cpupool",shape=box];
N41[label="num_cpus v1.1.0",shape=box];
N42[label="rayon",shape=box];
N43[label="kernel32-sys",shape=box];
N44[label="winapi",shape=box];
N45[label="winapi-build",shape=box];
N46[label="lazy_static",shape=box];
N47[label="lazycell",shape=box];
N48[label="mio",shape=box];
N49[label="miow",shape=box];
N50[label="net2",shape=box];
N51[label="nix",shape=box];
N52[label="slab",shape=box];
N53[label="ws2_32-sys",shape=box];
N54[label="rustc_version",shape=box];
N55[label="semver",shape=box];
N56[label="void",shape=box];
N57[label="num_cpus v0.2.13",shape=box];
N58[label="owning_ref",shape=box];
N59[label="time",shape=box];
N60[label="tokio-core",shape=box];
N61[label="parking_lot_core",shape=box];
N62[label="smallvec",shape=box];
N63[label="rocksdb-sys",shape=box];
N64[label="scoped-tls",shape=box];
N5[label="miner",shape=box];
N6[label="p2p",shape=box];
N7[label="script",shape=box];
N8[label="ansi_term",shape=box];
N9[label="arrayvec",shape=box];
N10[label="nodrop",shape=box];
N11[label="odds",shape=box];
N12[label="base58",shape=box];
N13[label="bitcrypto",shape=box];
N14[label="primitives",shape=box];
N15[label="rust-crypto",shape=box];
N16[label="bitflags v0.4.0",shape=box];
N17[label="bitflags v0.7.0",shape=box];
N18[label="byteorder",shape=box];
N19[label="cfg-if",shape=box];
N20[label="chain",shape=box];
N21[label="rustc-serialize",shape=box];
N22[label="serialization",shape=box];
N23[label="libc",shape=box];
N24[label="strsim",shape=box];
N25[label="term_size",shape=box];
N26[label="unicode-segmentation",shape=box];
N27[label="unicode-width",shape=box];
N28[label="vec_map",shape=box];
N29[label="yaml-rust",shape=box];
N30[label="crossbeam",shape=box];
N31[label="elastic-array",shape=box];
N32[label="ethcore-devtools",shape=box];
N33[label="parking_lot",shape=box];
N34[label="rocksdb",shape=box];
N35[label="deque",shape=box];
N36[label="rand",shape=box];
N37[label="eth-secp256k1",shape=box];
N38[label="gcc",shape=box];
N39[label="futures",shape=box];
N40[label="log",shape=box];
N41[label="futures-cpupool",shape=box];
N42[label="num_cpus v1.1.0",shape=box];
N43[label="rayon",shape=box];
N44[label="kernel32-sys",shape=box];
N45[label="winapi",shape=box];
N46[label="winapi-build",shape=box];
N47[label="lazy_static",shape=box];
N48[label="lazycell",shape=box];
N49[label="mio",shape=box];
N50[label="miow",shape=box];
N51[label="net2",shape=box];
N52[label="nix",shape=box];
N53[label="slab",shape=box];
N54[label="ws2_32-sys",shape=box];
N55[label="rustc_version",shape=box];
N56[label="semver",shape=box];
N57[label="void",shape=box];
N58[label="num_cpus v0.2.13",shape=box];
N59[label="owning_ref",shape=box];
N60[label="time",shape=box];
N61[label="tokio-core",shape=box];
N62[label="parking_lot_core",shape=box];
N63[label="smallvec",shape=box];
N64[label="rocksdb-sys",shape=box];
N65[label="scoped-tls",shape=box];
N0 -> N1[label="",style=dashed];
N0 -> N2[label="",style=dashed];
N0 -> N3[label="",style=dashed];
N0 -> N4[label="",style=dashed];
N0 -> N5[label="",style=dashed];
N0 -> N6[label="",style=dashed];
N1 -> N7[label="",style=dashed];
N1 -> N16[label="",style=dashed];
N1 -> N22[label="",style=dashed];
N0 -> N7[label="",style=dashed];
N1 -> N8[label="",style=dashed];
N1 -> N17[label="",style=dashed];
N1 -> N23[label="",style=dashed];
N1 -> N24[label="",style=dashed];
N1 -> N25[label="",style=dashed];
N1 -> N26[label="",style=dashed];
N1 -> N27[label="",style=dashed];
N1 -> N28[label="",style=dashed];
N2 -> N13[label="",style=dashed];
N2 -> N17[label="",style=dashed];
N2 -> N19[label="",style=dashed];
N2 -> N21[label="",style=dashed];
N2 -> N30[label="",style=dashed];
N1 -> N29[label="",style=dashed];
N2 -> N14[label="",style=dashed];
N2 -> N18[label="",style=dashed];
N2 -> N20[label="",style=dashed];
N2 -> N22[label="",style=dashed];
N2 -> N31[label="",style=dashed];
N2 -> N32[label="",style=dashed];
N2 -> N33[label="",style=dashed];
N3 -> N11[label="",style=dashed];
N2 -> N34[label="",style=dashed];
N3 -> N12[label="",style=dashed];
N3 -> N13[label="",style=dashed];
N3 -> N20[label="",style=dashed];
N3 -> N35[label="",style=dashed];
N3 -> N14[label="",style=dashed];
N3 -> N21[label="",style=dashed];
N3 -> N36[label="",style=dashed];
N3 -> N46[label="",style=dashed];
N4 -> N12[label="",style=dashed];
N3 -> N37[label="",style=dashed];
N3 -> N47[label="",style=dashed];
N4 -> N13[label="",style=dashed];
N4 -> N17[label="",style=dashed];
N4 -> N19[label="",style=dashed];
N4 -> N21[label="",style=dashed];
N5 -> N4[label="",style=dashed];
N5 -> N12[label="",style=dashed];
N5 -> N13[label="",style=dashed];
N5 -> N32[label="",style=dashed];
N5 -> N35[label="",style=dashed];
N5 -> N38[label="",style=dashed];
N5 -> N40[label="",style=dashed];
N5 -> N59[label="",style=dashed];
N5 -> N60[label="",style=dashed];
N6 -> N3[label="",style=dashed];
N6 -> N12[label="",style=dashed];
N4 -> N14[label="",style=dashed];
N4 -> N18[label="",style=dashed];
N4 -> N20[label="",style=dashed];
N4 -> N22[label="",style=dashed];
N5 -> N14[label="",style=dashed];
N5 -> N20[label="",style=dashed];
N5 -> N22[label="",style=dashed];
N6 -> N4[label="",style=dashed];
N6 -> N13[label="",style=dashed];
N6 -> N19[label="",style=dashed];
N6 -> N21[label="",style=dashed];
N8 -> N9[label=""];
N8 -> N10[label=""];
N6 -> N14[label="",style=dashed];
N6 -> N33[label="",style=dashed];
N6 -> N36[label="",style=dashed];
N6 -> N39[label="",style=dashed];
N6 -> N41[label="",style=dashed];
N6 -> N60[label="",style=dashed];
N6 -> N61[label="",style=dashed];
N7 -> N3[label="",style=dashed];
N7 -> N13[label="",style=dashed];
N7 -> N14[label="",style=dashed];
N7 -> N20[label="",style=dashed];
N7 -> N22[label="",style=dashed];
N9 -> N10[label=""];
N12 -> N13[label="",style=dashed];
N12 -> N14[label="",style=dashed];
N13 -> N20[label="",style=dashed];
N14 -> N20[label="",style=dashed];
N14 -> N22[label="",style=dashed];
N14 -> N35[label="",style=dashed];
N14 -> N37[label="",style=dashed];
N14 -> N59[label="",style=dashed];
N19 -> N12[label="",style=dashed];
N19 -> N13[label="",style=dashed];
N19 -> N20[label="",style=dashed];
N19 -> N21[label="",style=dashed];
N21 -> N13[label="",style=dashed];
N21 -> N17[label="",style=dashed];
N24 -> N22[label="",style=dashed];
N24 -> N43[label="",style=dashed];
N24 -> N44[label="",style=dashed];
N31 -> N35[label="",style=dashed];
N32 -> N58[label="",style=dashed];
N32 -> N61[label="",style=dashed];
N33 -> N22[label="",style=dashed];
N33 -> N63[label="",style=dashed];
N34 -> N35[label="",style=dashed];
N35 -> N22[label="",style=dashed];
N36 -> N8[label="",style=dashed];
N36 -> N20[label="",style=dashed];
N36 -> N22[label="",style=dashed];
N36 -> N35[label="",style=dashed];
N36 -> N37[label="",style=dashed];
N37 -> N42[label="",style=dashed];
N38 -> N39[label="",style=dashed];
N40 -> N29[label="",style=dashed];
N40 -> N38[label="",style=dashed];
N40 -> N41[label="",style=dashed];
N41 -> N22[label="",style=dashed];
N42 -> N34[label="",style=dashed];
N42 -> N35[label="",style=dashed];
N42 -> N57[label="",style=dashed];
N43 -> N44[label="",style=dashed];
N43 -> N45[label="",style=dashed];
N48 -> N22[label="",style=dashed];
N48 -> N39[label="",style=dashed];
N48 -> N43[label="",style=dashed];
N48 -> N44[label="",style=dashed];
N48 -> N47[label=""];
N48 -> N49[label=""];
N48 -> N50[label=""];
N48 -> N51[label=""];
N48 -> N52[label="",style=dashed];
N49 -> N43[label=""];
N49 -> N44[label=""];
N9 -> N11[label=""];
N10 -> N11[label=""];
N13 -> N14[label="",style=dashed];
N13 -> N15[label="",style=dashed];
N14 -> N21[label="",style=dashed];
N15 -> N21[label="",style=dashed];
N15 -> N23[label="",style=dashed];
N15 -> N36[label="",style=dashed];
N15 -> N38[label="",style=dashed];
N15 -> N60[label="",style=dashed];
N20 -> N13[label="",style=dashed];
N20 -> N14[label="",style=dashed];
N20 -> N21[label="",style=dashed];
N20 -> N22[label="",style=dashed];
N22 -> N14[label="",style=dashed];
N22 -> N18[label="",style=dashed];
N25 -> N23[label="",style=dashed];
N25 -> N44[label="",style=dashed];
N25 -> N45[label="",style=dashed];
N32 -> N36[label="",style=dashed];
N33 -> N59[label="",style=dashed];
N33 -> N62[label="",style=dashed];
N34 -> N23[label="",style=dashed];
N34 -> N64[label="",style=dashed];
N35 -> N36[label="",style=dashed];
N36 -> N23[label="",style=dashed];
N37 -> N9[label="",style=dashed];
N37 -> N21[label="",style=dashed];
N37 -> N23[label="",style=dashed];
N37 -> N36[label="",style=dashed];
N37 -> N38[label="",style=dashed];
N38 -> N43[label="",style=dashed];
N39 -> N40[label="",style=dashed];
N41 -> N30[label="",style=dashed];
N41 -> N39[label="",style=dashed];
N41 -> N42[label="",style=dashed];
N42 -> N23[label="",style=dashed];
N43 -> N35[label="",style=dashed];
N43 -> N36[label="",style=dashed];
N43 -> N58[label="",style=dashed];
N44 -> N45[label="",style=dashed];
N44 -> N46[label="",style=dashed];
N49 -> N23[label="",style=dashed];
N49 -> N40[label="",style=dashed];
N49 -> N44[label="",style=dashed];
N49 -> N45[label="",style=dashed];
N49 -> N48[label=""];
N49 -> N50[label=""];
N49 -> N53[label=""];
N50 -> N18[label=""];
N50 -> N22[label=""];
N50 -> N43[label=""];
N49 -> N51[label=""];
N49 -> N52[label=""];
N49 -> N53[label="",style=dashed];
N50 -> N44[label=""];
N50 -> N53[label=""];
N51 -> N15[label=""];
N51 -> N18[label=""];
N51 -> N22[label=""];
N50 -> N45[label=""];
N50 -> N51[label=""];
N50 -> N54[label=""];
N51 -> N19[label=""];
N51 -> N23[label=""];
N51 -> N44[label=""];
N51 -> N45[label=""];
N51 -> N54[label=""];
N51 -> N55[label=""];
N51 -> N56[label=""];
N53 -> N44[label=""];
N53 -> N45[label=""];
N54 -> N55[label=""];
N57 -> N22[label="",style=dashed];
N59 -> N22[label="",style=dashed];
N59 -> N43[label="",style=dashed];
N59 -> N44[label="",style=dashed];
N60 -> N38[label="",style=dashed];
N60 -> N39[label="",style=dashed];
N60 -> N48[label="",style=dashed];
N60 -> N52[label="",style=dashed];
N60 -> N64[label="",style=dashed];
N61 -> N22[label="",style=dashed];
N61 -> N35[label="",style=dashed];
N61 -> N43[label="",style=dashed];
N61 -> N44[label="",style=dashed];
N61 -> N62[label="",style=dashed];
N63 -> N22[label="",style=dashed];
N63 -> N37[label="",style=dashed];
N52 -> N16[label=""];
N52 -> N19[label=""];
N52 -> N23[label=""];
N52 -> N55[label=""];
N52 -> N56[label=""];
N52 -> N57[label=""];
N54 -> N45[label=""];
N54 -> N46[label=""];
N55 -> N56[label=""];
N58 -> N23[label="",style=dashed];
N60 -> N23[label="",style=dashed];
N60 -> N44[label="",style=dashed];
N60 -> N45[label="",style=dashed];
N61 -> N39[label="",style=dashed];
N61 -> N40[label="",style=dashed];
N61 -> N49[label="",style=dashed];
N61 -> N53[label="",style=dashed];
N61 -> N65[label="",style=dashed];
N62 -> N23[label="",style=dashed];
N62 -> N36[label="",style=dashed];
N62 -> N44[label="",style=dashed];
N62 -> N45[label="",style=dashed];
N62 -> N63[label="",style=dashed];
N64 -> N23[label="",style=dashed];
N64 -> N38[label="",style=dashed];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 KiB

After

Width:  |  Height:  |  Size: 438 KiB

View File

@ -7,6 +7,7 @@ cargo test\
-p bitcrypto\
-p keys\
-p message\
-p miner\
-p p2p\
-p primitives\
-p script\