2016-10-14 14:37:32 -07:00
//! 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.
2016-10-17 00:08:49 -07:00
//! It also guarantees that ancestor-descendant relation won't break during ordered removal (ancestors always removed
2016-11-08 12:01:25 -08:00
//! before descendants). Removal using `remove_by_hash` can break this rule.
2016-11-29 05:40:55 -08:00
use db ::{ TransactionProvider , PreviousTransactionOutputProvider } ;
2016-11-24 23:39:56 -08:00
use primitives ::bytes ::Bytes ;
2016-11-02 05:18:52 -07:00
use primitives ::hash ::H256 ;
2016-11-29 05:40:55 -08:00
use chain ::{ Transaction , OutPoint , TransactionOutput } ;
2016-10-17 00:08:49 -07:00
use std ::cmp ::Ordering ;
2016-10-14 14:37:32 -07:00
use std ::collections ::HashMap ;
use std ::collections ::HashSet ;
2016-10-19 05:02:13 -07:00
use std ::collections ::BTreeSet ;
2016-12-02 03:45:14 -08:00
use std ::collections ::VecDeque ;
use std ::hash ::{ Hash , Hasher } ;
2016-11-24 23:39:56 -08:00
use ser ::{ Serializable , serialize } ;
2016-10-19 03:07:11 -07:00
use heapsize ::HeapSizeOf ;
2016-10-14 14:37:32 -07:00
/// Transactions ordering strategy
2016-11-08 12:01:25 -08:00
#[ cfg_attr(feature= " cargo-clippy " , allow(enum_variant_names)) ]
2016-10-17 00:08:49 -07:00
#[ derive(Debug, Clone, Copy) ]
2016-10-14 14:37:32 -07:00
pub enum OrderingStrategy {
2016-10-17 00:08:49 -07:00
/// Order transactions by the time they have entered the memory pool
2016-10-14 14:37:32 -07:00
ByTimestamp ,
2016-10-17 00:08:49 -07:00
/// Order transactions by their individual mining score
ByTransactionScore ,
/// Order transactions by their in-pool package mining score (score for mining this transaction + all descendants transactions)
ByPackageScore ,
2016-10-14 14:37:32 -07:00
}
/// Information on current `MemoryPool` state
#[ derive(Debug) ]
pub struct Information {
2016-10-19 03:07:11 -07:00
/// Number of transactions currently in the `MemoryPool`
2016-10-14 14:37:32 -07:00
pub transactions_count : usize ,
2016-10-19 03:07:11 -07:00
/// Total number of bytes occupied by transactions from the `MemoryPool`
2016-10-14 14:37:32 -07:00
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 ,
2016-10-17 00:08:49 -07:00
/// In-pool ancestors hashes for this transaction
ancestors : HashSet < H256 > ,
2016-10-14 14:37:32 -07:00
/// Transaction hash (stored for effeciency)
hash : H256 ,
/// Transaction size (stored for effeciency)
size : usize ,
2016-10-17 00:08:49 -07:00
/// Throughout index of this transaction in memory pool (non persistent)
storage_index : u64 ,
2016-10-14 14:37:32 -07:00
/// Transaction fee (stored for efficiency)
miner_fee : i64 ,
2016-10-17 02:17:00 -07:00
/// Virtual transaction fee (a way to prioritize/penalize transaction)
2016-10-14 14:37:32 -07:00
miner_virtual_fee : i64 ,
2016-10-17 00:08:49 -07:00
/// size + Sum(size) for all in-pool descendants
package_size : usize ,
/// miner_fee + Sum(miner_fee) for all in-pool descendants
package_miner_fee : i64 ,
/// miner_virtual_fee + Sum(miner_virtual_fee) for all in-pool descendants
package_miner_virtual_fee : i64 ,
2016-10-14 14:37:32 -07:00
}
/// Multi-index transactions storage
#[ derive(Debug) ]
struct Storage {
2016-10-17 00:08:49 -07:00
/// Throughout transactions counter
2016-10-14 14:37:32 -07:00
counter : u64 ,
/// Total transactions size (when serialized) in bytes
transactions_size_in_bytes : usize ,
/// By-hash storage
by_hash : HashMap < H256 , Entry > ,
2016-12-02 03:45:14 -08:00
/// Transactions by previous output
by_previous_output : HashMap < HashedOutPoint , H256 > ,
2016-10-17 00:08:49 -07:00
/// References storage
references : ReferenceStorage ,
}
2016-11-08 12:01:25 -08:00
/// Multi-index storage which holds references to entries from `Storage::by_hash`
2016-10-17 00:08:49 -07:00
#[ derive(Debug, Clone) ]
struct ReferenceStorage {
/// By-input storage
by_input : HashMap < H256 , HashSet < H256 > > ,
/// Pending entries
pending : HashSet < H256 > ,
2016-10-19 05:02:13 -07:00
/// Ordered storage
ordered : OrderedReferenceStorage ,
}
2016-11-08 12:01:25 -08:00
/// Multi-index orderings storage which holds ordered references to entries from `Storage::by_hash`
2016-10-19 05:02:13 -07:00
#[ derive(Debug, Clone) ]
struct OrderedReferenceStorage {
2016-10-14 14:37:32 -07:00
/// By-entry-time storage
2016-10-19 05:02:13 -07:00
by_storage_index : BTreeSet < ByTimestampOrderedEntry > ,
2016-10-14 14:37:32 -07:00
/// By-score storage
2016-10-19 05:02:13 -07:00
by_transaction_score : BTreeSet < ByTransactionScoreOrderedEntry > ,
2016-10-17 00:08:49 -07:00
/// By-package-score strategy
2016-10-19 05:02:13 -07:00
by_package_score : BTreeSet < ByPackageScoreOrderedEntry > ,
2016-10-14 14:37:32 -07:00
}
2016-10-19 05:02:13 -07:00
#[ derive(Debug, Clone, PartialEq, Eq) ]
struct ByTimestampOrderedEntry {
/// Transaction hash
hash : H256 ,
/// Throughout index of this transaction in memory pool (non persistent)
storage_index : u64 ,
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
#[ derive(Debug, Eq, PartialEq, Clone) ]
struct ByTransactionScoreOrderedEntry {
/// Transaction hash
hash : H256 ,
/// Transaction size
size : usize ,
/// Transaction fee
miner_fee : i64 ,
/// Virtual transaction fee
miner_virtual_fee : i64 ,
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
#[ derive(Debug, Eq, PartialEq, Clone) ]
struct ByPackageScoreOrderedEntry {
/// Transaction hash
hash : H256 ,
/// size + Sum(size) for all in-pool descendants
package_size : usize ,
/// miner_fee + Sum(miner_fee) for all in-pool descendants
package_miner_fee : i64 ,
/// miner_virtual_fee + Sum(miner_virtual_fee) for all in-pool descendants
package_miner_virtual_fee : i64 ,
}
2016-10-14 14:37:32 -07:00
2016-12-02 03:45:14 -08:00
#[ derive(Debug, PartialEq, Eq, Clone) ]
struct HashedOutPoint {
/// Transasction output point
out_point : OutPoint ,
}
impl From < OutPoint > for HashedOutPoint {
fn from ( out_point : OutPoint ) -> Self {
HashedOutPoint {
out_point : out_point ,
}
}
}
impl Hash for HashedOutPoint {
fn hash < H > ( & self , state : & mut H ) where H : Hasher {
state . write ( & serialize ( & self . out_point ) ) ;
state . finish ( ) ;
}
}
2016-10-19 05:02:13 -07:00
impl < ' a > From < & ' a Entry > for ByTimestampOrderedEntry {
fn from ( entry : & ' a Entry ) -> Self {
ByTimestampOrderedEntry {
hash : entry . hash . clone ( ) ,
storage_index : entry . storage_index ,
}
}
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
impl < ' a > From < & ' a Entry > for ByTransactionScoreOrderedEntry {
fn from ( entry : & ' a Entry ) -> Self {
ByTransactionScoreOrderedEntry {
hash : entry . hash . clone ( ) ,
size : entry . size ,
miner_fee : entry . miner_fee ,
miner_virtual_fee : entry . miner_virtual_fee ,
}
}
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
impl < ' a > From < & ' a Entry > for ByPackageScoreOrderedEntry {
fn from ( entry : & ' a Entry ) -> Self {
ByPackageScoreOrderedEntry {
hash : entry . hash . clone ( ) ,
package_size : entry . package_size ,
package_miner_fee : entry . package_miner_fee ,
package_miner_virtual_fee : entry . package_miner_virtual_fee ,
}
}
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
impl PartialOrd for ByTimestampOrderedEntry {
fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
Some ( self . cmp ( other ) )
}
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
impl Ord for ByTimestampOrderedEntry {
fn cmp ( & self , other : & Self ) -> Ordering {
let order = self . storage_index . cmp ( & other . storage_index ) ;
if order ! = Ordering ::Equal {
return order
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
self . hash . cmp ( & other . hash )
}
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
impl PartialOrd for ByTransactionScoreOrderedEntry {
fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
Some ( self . cmp ( other ) )
}
}
impl Ord for ByTransactionScoreOrderedEntry {
fn cmp ( & self , other : & Self ) -> Ordering {
// lesser miner score means later removal
let left = ( self . miner_fee + self . miner_virtual_fee ) * ( other . size as i64 ) ;
let right = ( other . miner_fee + other . miner_virtual_fee ) * ( self . size as i64 ) ;
let order = right . cmp ( & left ) ;
if order ! = Ordering ::Equal {
return order
2016-10-14 14:37:32 -07:00
}
2016-10-19 05:02:13 -07:00
self . hash . cmp ( & other . hash )
2016-10-14 14:37:32 -07:00
}
}
2016-10-19 05:02:13 -07:00
impl PartialOrd for ByPackageScoreOrderedEntry {
fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
Some ( self . cmp ( other ) )
}
}
2016-10-17 00:08:49 -07:00
2016-10-19 05:02:13 -07:00
impl Ord for ByPackageScoreOrderedEntry {
fn cmp ( & self , other : & Self ) -> Ordering {
2016-10-17 00:08:49 -07:00
// lesser miner score means later removal
2016-10-19 05:02:13 -07:00
let left = ( self . package_miner_fee + self . package_miner_virtual_fee ) * ( other . package_size as i64 ) ;
let right = ( other . package_miner_fee + other . package_miner_virtual_fee ) * ( self . package_size as i64 ) ;
let order = right . cmp ( & left ) ;
if order ! = Ordering ::Equal {
return order
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
self . hash . cmp ( & other . hash )
}
2016-10-14 14:37:32 -07:00
}
2016-10-19 03:07:11 -07:00
impl HeapSizeOf for Entry {
fn heap_size_of_children ( & self ) -> usize {
2016-11-08 12:01:25 -08:00
self . transaction . heap_size_of_children ( ) + self . ancestors . heap_size_of_children ( )
2016-10-19 03:07:11 -07:00
}
}
2016-10-14 14:37:32 -07:00
impl Storage {
pub fn new ( ) -> Self {
Storage {
counter : 0 ,
transactions_size_in_bytes : 0 ,
by_hash : HashMap ::new ( ) ,
2016-12-02 03:45:14 -08:00
by_previous_output : HashMap ::new ( ) ,
2016-10-17 00:08:49 -07:00
references : ReferenceStorage {
by_input : HashMap ::new ( ) ,
pending : HashSet ::new ( ) ,
2016-10-19 05:02:13 -07:00
ordered : OrderedReferenceStorage {
by_storage_index : BTreeSet ::new ( ) ,
by_transaction_score : BTreeSet ::new ( ) ,
by_package_score : BTreeSet ::new ( ) ,
} ,
2016-10-17 00:08:49 -07:00
} ,
2016-10-14 14:37:32 -07:00
}
}
pub fn insert ( & mut self , entry : Entry ) {
2016-10-17 00:08:49 -07:00
// update pool information
2016-10-14 14:37:32 -07:00
self . transactions_size_in_bytes + = entry . size ;
2016-10-17 00:08:49 -07:00
// remember that this transactions depends on its inputs
for input_hash in entry . transaction . inputs . iter ( ) . map ( | input | & input . previous_output . hash ) {
2016-11-08 12:01:25 -08:00
self . references . by_input . entry ( input_hash . clone ( ) ) . or_insert_with ( HashSet ::new ) . insert ( entry . hash . clone ( ) ) ;
2016-10-17 00:08:49 -07:00
}
2016-10-14 14:37:32 -07:00
2016-10-17 00:08:49 -07:00
// update score of all packages this transaction is in
2016-11-08 12:01:25 -08:00
for ancestor_hash in & entry . ancestors {
2016-10-19 05:02:13 -07:00
if let Some ( mut ancestor_entry ) = self . by_hash . get_mut ( ancestor_hash ) {
let removed = self . references . ordered . by_package_score . remove ( & ( ancestor_entry as & Entry ) . into ( ) ) ;
2016-10-17 00:08:49 -07:00
ancestor_entry . package_size + = entry . size ;
ancestor_entry . package_miner_fee + = entry . package_miner_fee ;
ancestor_entry . package_miner_virtual_fee + = entry . package_miner_virtual_fee ;
2016-10-14 14:37:32 -07:00
2016-10-17 00:08:49 -07:00
if removed {
2016-10-19 05:02:13 -07:00
self . references . ordered . by_package_score . insert ( ( ancestor_entry as & Entry ) . into ( ) ) ;
2016-10-17 00:08:49 -07:00
}
}
}
2016-10-14 14:37:32 -07:00
2016-10-17 00:08:49 -07:00
// insert either to pending queue or to orderings
2016-10-19 05:02:13 -07:00
if self . references . has_in_pool_ancestors ( None , & self . by_hash , & entry . transaction ) {
2016-10-17 00:08:49 -07:00
self . references . pending . insert ( entry . hash . clone ( ) ) ;
}
else {
2016-10-19 05:02:13 -07:00
self . references . ordered . insert_to_orderings ( & entry ) ;
2016-10-17 00:08:49 -07:00
}
2016-10-14 14:37:32 -07:00
2016-12-02 03:45:14 -08:00
// remember that all inputs of this transaction are spent
for input in & entry . transaction . inputs {
let previous_tx = self . by_previous_output . insert ( input . previous_output . clone ( ) . into ( ) , entry . hash . clone ( ) ) ;
assert_eq! ( previous_tx , None ) ; // transaction must be verified before => no double spend
}
2016-10-17 00:08:49 -07:00
// add to by_hash storage
2016-11-08 12:01:25 -08:00
self . by_hash . insert ( entry . hash . clone ( ) , entry ) ;
2016-10-14 14:37:32 -07:00
}
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 )
}
2016-12-02 03:45:14 -08:00
pub fn is_output_spent ( & self , prevout : & OutPoint ) -> bool {
self . by_previous_output . contains_key ( & prevout . clone ( ) . into ( ) )
}
2016-10-17 00:08:49 -07:00
pub fn set_virtual_fee ( & mut self , h : & H256 , virtual_fee : i64 ) {
// for updating ancestors
let mut miner_virtual_fee_change = 0 i64 ;
let mut ancestors : Option < Vec < H256 > > = None ;
2016-10-14 14:37:32 -07:00
2016-10-17 00:08:49 -07:00
// modify the entry itself
2016-10-19 05:02:13 -07:00
if let Some ( mut entry ) = self . by_hash . get_mut ( h ) {
let insert_to_package_score = self . references . ordered . by_package_score . remove ( & ( entry as & Entry ) . into ( ) ) ;
let insert_to_transaction_score = self . references . ordered . by_transaction_score . remove ( & ( entry as & Entry ) . into ( ) ) ;
2016-10-17 00:08:49 -07:00
miner_virtual_fee_change = virtual_fee - entry . miner_virtual_fee ;
if ! entry . ancestors . is_empty ( ) {
ancestors = Some ( entry . ancestors . iter ( ) . cloned ( ) . collect ( ) ) ;
2016-10-14 14:37:32 -07:00
}
entry . miner_virtual_fee = virtual_fee ;
2016-10-17 00:08:49 -07:00
if insert_to_transaction_score {
2016-10-19 05:02:13 -07:00
self . references . ordered . by_transaction_score . insert ( ( entry as & Entry ) . into ( ) ) ;
2016-10-17 00:08:49 -07:00
}
if insert_to_package_score {
2016-10-19 05:02:13 -07:00
self . references . ordered . by_package_score . insert ( ( entry as & Entry ) . into ( ) ) ;
2016-10-17 00:08:49 -07:00
}
}
// now modify all ancestor entries
2016-10-18 05:34:39 -07:00
if miner_virtual_fee_change ! = 0 {
2016-10-17 00:08:49 -07:00
ancestors . map ( | ancestors | {
for ancestor_hash in ancestors {
2016-10-19 05:02:13 -07:00
if let Some ( mut ancestor_entry ) = self . by_hash . get_mut ( & ancestor_hash ) {
let insert_to_package_score = self . references . ordered . by_package_score . remove ( & ( ancestor_entry as & Entry ) . into ( ) ) ;
2016-10-17 00:08:49 -07:00
ancestor_entry . package_miner_virtual_fee + = miner_virtual_fee_change ;
if insert_to_package_score {
2016-10-19 05:02:13 -07:00
self . references . ordered . by_package_score . insert ( ( ancestor_entry as & Entry ) . into ( ) ) ;
2016-10-17 00:08:49 -07:00
}
}
}
} ) ;
2016-10-14 14:37:32 -07:00
}
}
2016-11-28 07:33:24 -08:00
pub fn read_by_hash ( & self , h : & H256 ) -> Option < & Transaction > {
self . by_hash . get ( h ) . map ( | e | & e . transaction )
}
2016-10-17 00:08:49 -07:00
pub fn read_with_strategy ( & self , strategy : OrderingStrategy ) -> Option < H256 > {
match strategy {
2016-10-19 05:02:13 -07:00
OrderingStrategy ::ByTimestamp = > self . references . ordered . by_storage_index . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
OrderingStrategy ::ByTransactionScore = > self . references . ordered . by_transaction_score . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
OrderingStrategy ::ByPackageScore = > self . references . ordered . by_package_score . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
2016-10-17 00:08:49 -07:00
}
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
pub fn read_n_with_strategy ( & self , mut n : usize , strategy : OrderingStrategy ) -> Vec < H256 > {
if n = = 0 {
return Vec ::new ( ) ;
}
if n = = 1 {
return self . read_with_strategy ( strategy )
. map_or ( Vec ::new ( ) , | h | vec! [ h ] ) ;
}
let mut references = self . references . clone ( ) ;
let mut result : Vec < H256 > = Vec ::new ( ) ;
let mut removed : HashSet < H256 > = HashSet ::new ( ) ;
loop {
if n = = 0 {
break ;
}
n - = 1 ;
let top_hash = match strategy {
2016-10-19 05:02:13 -07:00
OrderingStrategy ::ByTimestamp = > references . ordered . by_storage_index . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
OrderingStrategy ::ByTransactionScore = > references . ordered . by_transaction_score . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
OrderingStrategy ::ByPackageScore = > references . ordered . by_package_score . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
2016-10-17 00:08:49 -07:00
} ;
match top_hash {
None = > break ,
Some ( top_hash ) = > {
self . by_hash . get ( & top_hash ) . map ( | entry | {
// simulate removal
removed . insert ( top_hash . clone ( ) ) ;
2016-11-08 12:01:25 -08:00
references . remove ( Some ( & removed ) , & self . by_hash , entry ) ;
2016-10-17 00:08:49 -07:00
// return this entry
result . push ( top_hash ) ;
} ) ;
} ,
}
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
result
}
pub fn remove_by_hash ( & mut self , h : & H256 ) -> Option < Entry > {
self . by_hash . remove ( h )
. map ( | entry | {
// update pool information
self . transactions_size_in_bytes - = entry . size ;
2016-12-02 03:45:14 -08:00
// forget that all inputs of this transaction are spent
for input in & entry . transaction . inputs {
let spent_in_tx = self . by_previous_output . remove ( & input . previous_output . clone ( ) . into ( ) ) . expect ( " every spent output must be indexed " ) ;
assert_eq! ( & spent_in_tx , h ) ;
}
2016-10-17 00:08:49 -07:00
// remove from storage
2016-10-19 05:02:13 -07:00
self . references . remove ( None , & self . by_hash , & entry ) ;
2016-10-17 00:08:49 -07:00
entry
} )
2016-10-14 14:37:32 -07:00
}
2016-12-02 03:45:14 -08:00
pub fn remove_by_prevout ( & mut self , prevout : & OutPoint ) -> Option < Vec < Transaction > > {
let mut queue : VecDeque < OutPoint > = VecDeque ::new ( ) ;
let mut removed : Vec < Transaction > = Vec ::new ( ) ;
queue . push_back ( prevout . clone ( ) ) ;
while let Some ( prevout ) = queue . pop_front ( ) {
if let Some ( entry_hash ) = self . by_previous_output . get ( & prevout . clone ( ) . into ( ) ) . cloned ( ) {
let entry = self . remove_by_hash ( & entry_hash ) . expect ( " checket that it exists line above; qed " ) ;
queue . extend ( entry . transaction . outputs . iter ( ) . enumerate ( ) . map ( | ( idx , _ ) | OutPoint {
hash : entry_hash . clone ( ) ,
index : idx as u32 ,
} ) ) ;
removed . push ( entry . transaction ) ;
}
}
Some ( removed )
}
2016-10-17 00:08:49 -07:00
pub fn remove_by_parent_hash ( & mut self , h : & H256 ) -> Option < Vec < Transaction > > {
2016-10-14 14:37:32 -07:00
// this code will run only when ancestor transaction is inserted
// in memory pool after its descendants
2016-10-17 00:08:49 -07:00
if let Some ( mut descendants ) = self . references . by_input . get ( h ) . map ( | d | d . iter ( ) . cloned ( ) . collect ::< Vec < H256 > > ( ) ) {
// prepare Vec of all descendants hashes
let mut all_descendants : HashSet < H256 > = HashSet ::new ( ) ;
while let Some ( descendant ) = descendants . pop ( ) {
if all_descendants . contains ( & descendant ) {
continue
}
all_descendants . insert ( descendant . clone ( ) ) ;
if let Some ( grand_descendants ) = self . references . by_input . get ( & descendant ) {
descendants . extend ( grand_descendants . iter ( ) . cloned ( ) ) ;
}
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
// topologically sort descendants
let mut all_descendants : Vec < _ > = all_descendants . iter ( ) . collect ( ) ;
all_descendants . sort_by ( | left , right | {
let left = self . by_hash . get ( left )
. expect ( " `left` is read from `by_input`; all entries from `by_input` have corresponding entries in `by_hash`; qed " ) ;
let right = self . by_hash . get ( right )
. expect ( " `right` is read from `by_input`; all entries from `by_input` have corresponding entries in `by_hash`; qed " ) ;
if left . ancestors . contains ( & right . hash ) {
return Ordering ::Greater ;
}
if right . ancestors . contains ( & left . hash ) {
return Ordering ::Less ;
}
Ordering ::Equal
} ) ;
// move all descendants out of storage for later insertion
Some ( all_descendants . into_iter ( )
2016-11-08 12:01:25 -08:00
. filter_map ( | hash | self . remove_by_hash ( hash ) . map ( | entry | entry . transaction ) )
2016-10-17 00:08:49 -07:00
. collect ( ) )
}
else {
None
2016-10-14 14:37:32 -07:00
}
}
2016-10-17 00:08:49 -07:00
pub fn remove_with_strategy ( & mut self , strategy : OrderingStrategy ) -> Option < Transaction > {
let top_hash = match strategy {
2016-10-19 05:02:13 -07:00
OrderingStrategy ::ByTimestamp = > self . references . ordered . by_storage_index . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
OrderingStrategy ::ByTransactionScore = > self . references . ordered . by_transaction_score . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
OrderingStrategy ::ByPackageScore = > self . references . ordered . by_package_score . iter ( ) . map ( | entry | entry . hash . clone ( ) ) . nth ( 0 ) ,
2016-10-17 00:08:49 -07:00
} ;
top_hash . map ( | hash | self . remove_by_hash ( & hash )
. expect ( " `hash` is read from `references`; entries in `references` have corresponging entries in `by_hash`; `remove_by_hash` removes entry from `by_hash`; qed " )
. transaction )
}
pub fn remove_n_with_strategy ( & mut self , mut n : usize , strategy : OrderingStrategy ) -> Vec < Transaction > {
let mut result : Vec < Transaction > = Vec ::new ( ) ;
loop {
if n = = 0 {
break ;
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
n - = 1 ;
result . push ( match self . remove_with_strategy ( strategy ) {
Some ( transaction ) = > transaction ,
None = > break ,
} )
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
result
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
pub fn get_transactions_ids ( & self ) -> Vec < H256 > {
2016-11-08 12:01:25 -08:00
self . by_hash . keys ( ) . cloned ( ) . collect ( )
2016-10-17 00:08:49 -07:00
}
2016-10-19 05:02:13 -07:00
}
impl ReferenceStorage {
pub fn has_in_pool_ancestors ( & self , removed : Option < & HashSet < H256 > > , by_hash : & HashMap < H256 , Entry > , transaction : & Transaction ) -> bool {
transaction . inputs . iter ( )
. any ( | input | by_hash . contains_key ( & input . previous_output . hash )
& & ! removed . map_or ( false , | r | r . contains ( & input . previous_output . hash ) ) )
}
2016-10-14 14:37:32 -07:00
2016-10-19 05:02:13 -07:00
pub fn remove ( & mut self , removed : Option < & HashSet < H256 > > , by_hash : & HashMap < H256 , Entry > , entry : & Entry ) {
2016-10-17 00:08:49 -07:00
// for each pending descendant transaction
2016-10-19 05:02:13 -07:00
if let Some ( descendants ) = self . by_input . get ( & entry . hash ) {
2016-11-08 12:01:25 -08:00
let descendants = descendants . iter ( ) . filter_map ( | hash | by_hash . get ( hash ) ) ;
2016-10-17 00:08:49 -07:00
for descendant in descendants {
// if there are no more ancestors of this transaction in the pool
// => can move from pending to orderings
2016-10-19 05:02:13 -07:00
if ! self . has_in_pool_ancestors ( removed , by_hash , & descendant . transaction ) {
self . pending . remove ( & descendant . hash ) ;
2016-10-17 00:08:49 -07:00
if let Some ( descendant_entry ) = by_hash . get ( & descendant . hash ) {
2016-11-08 12:01:25 -08:00
self . ordered . insert_to_orderings ( descendant_entry ) ;
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
}
2016-10-14 14:37:32 -07:00
}
}
2016-10-19 05:02:13 -07:00
self . by_input . remove ( & entry . hash ) ;
2016-10-17 00:08:49 -07:00
// remove from pending
2016-10-19 05:02:13 -07:00
self . pending . remove ( & entry . hash ) ;
2016-10-17 00:08:49 -07:00
// remove from orderings
2016-10-19 05:02:13 -07:00
self . ordered . remove_from_orderings ( entry ) ;
2016-10-17 00:08:49 -07:00
}
2016-10-19 05:02:13 -07:00
}
2016-10-17 00:08:49 -07:00
2016-10-19 05:02:13 -07:00
impl OrderedReferenceStorage {
pub fn insert_to_orderings ( & mut self , entry : & Entry ) {
self . by_storage_index . insert ( entry . into ( ) ) ;
self . by_transaction_score . insert ( entry . into ( ) ) ;
self . by_package_score . insert ( entry . into ( ) ) ;
}
pub fn remove_from_orderings ( & mut self , entry : & Entry ) {
self . by_storage_index . remove ( & entry . into ( ) ) ;
self . by_transaction_score . remove ( & entry . into ( ) ) ;
self . by_package_score . remove ( & entry . into ( ) ) ;
2016-10-14 14:37:32 -07:00
}
}
2016-10-19 03:07:11 -07:00
impl HeapSizeOf for Storage {
fn heap_size_of_children ( & self ) -> usize {
self . by_hash . heap_size_of_children ( ) + self . references . heap_size_of_children ( )
}
}
impl HeapSizeOf for ReferenceStorage {
fn heap_size_of_children ( & self ) -> usize {
self . by_input . heap_size_of_children ( )
+ self . pending . heap_size_of_children ( )
2016-10-19 07:15:34 -07:00
+ self . ordered . heap_size_of_children ( )
}
}
impl HeapSizeOf for OrderedReferenceStorage {
fn heap_size_of_children ( & self ) -> usize {
// HeapSizeOf is not implemented for BTreeSet => rough estimation here
use std ::mem ::size_of ;
let len = self . by_storage_index . len ( ) ;
len * ( size_of ::< ByTimestampOrderedEntry > ( )
+ size_of ::< ByTransactionScoreOrderedEntry > ( )
+ size_of ::< ByPackageScoreOrderedEntry > ( ) )
2016-10-19 03:07:11 -07:00
}
}
2016-11-08 12:01:25 -08:00
impl Default for MemoryPool {
fn default ( ) -> Self {
2016-10-14 14:37:32 -07:00
MemoryPool {
storage : Storage ::new ( ) ,
}
}
2016-11-08 12:01:25 -08:00
}
impl MemoryPool {
/// Creates new memory pool
pub fn new ( ) -> Self {
MemoryPool ::default ( )
}
2016-10-14 14:37:32 -07:00
/// Insert verified transaction to the `MemoryPool`
pub fn insert_verified ( & mut self , t : Transaction ) {
let entry = self . make_entry ( t ) ;
2016-10-17 00:08:49 -07:00
let descendants = self . storage . remove_by_parent_hash ( & entry . hash ) ;
2016-10-14 14:37:32 -07:00
self . storage . insert ( entry ) ;
2016-10-17 00:08:49 -07:00
if let Some ( descendants_iter ) = descendants . map ( | d | d . into_iter ( ) ) {
for descendant in descendants_iter {
let descendant_entry = self . make_entry ( descendant ) ;
self . storage . insert ( descendant_entry ) ;
}
}
2016-10-14 14:37:32 -07:00
}
2016-10-17 00:08:49 -07:00
/// Removes single transaction by its hash.
/// All descedants remain in the pool.
2016-10-14 14:37:32 -07:00
pub fn remove_by_hash ( & mut self , h : & H256 ) -> Option < Transaction > {
self . storage . remove_by_hash ( h ) . map ( | entry | entry . transaction )
}
2016-12-02 03:45:14 -08:00
/// Removes transaction (and all its descendants) which has spent given output
pub fn remove_by_prevout ( & mut self , prevout : & OutPoint ) -> Option < Vec < Transaction > > {
self . storage . remove_by_prevout ( prevout )
}
2016-11-28 07:33:24 -08:00
/// Reads single transaction by its hash.
pub fn read_by_hash ( & self , h : & H256 ) -> Option < & Transaction > {
self . storage . read_by_hash ( h )
}
2016-10-17 00:08:49 -07:00
/// Reads hash of the 'top' transaction from the `MemoryPool` using selected strategy.
/// Ancestors are always returned before descendant transactions.
pub fn read_with_strategy ( & mut self , strategy : OrderingStrategy ) -> Option < H256 > {
self . storage . read_with_strategy ( strategy )
}
/// Reads hashes of up to n transactions from the `MemoryPool`, using selected strategy.
/// Ancestors are always returned before descendant transactions.
2016-10-19 03:07:11 -07:00
/// Use this function with care, only if really needed (heavy memory usage)
2016-10-17 00:08:49 -07:00
pub fn read_n_with_strategy ( & mut self , n : usize , strategy : OrderingStrategy ) -> Vec < H256 > {
self . storage . read_n_with_strategy ( n , strategy )
}
/// Removes the 'top' transaction from the `MemoryPool` using selected strategy.
/// Ancestors are always removed before descendant transactions.
pub fn remove_with_strategy ( & mut self , strategy : OrderingStrategy ) -> Option < Transaction > {
self . storage . remove_with_strategy ( strategy )
}
/// Removes up to n transactions from the `MemoryPool`, using selected strategy.
/// Ancestors are always removed before descendant transactions.
2016-10-14 14:37:32 -07:00
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 > {
2016-11-08 12:01:25 -08:00
self . storage . get_by_hash ( hash ) . map ( | entry | & entry . transaction )
2016-10-14 14:37:32 -07:00
}
/// 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 ( ) ,
2016-10-19 03:07:11 -07:00
transactions_size_in_bytes : self . storage . transactions_size_in_bytes ,
2016-10-14 14:37:32 -07:00
}
}
/// 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 ( ) ;
2016-10-17 00:08:49 -07:00
let ancestors = self . get_ancestors ( & t ) ;
2016-10-14 14:37:32 -07:00
let size = self . get_transaction_size ( & t ) ;
2016-10-17 00:08:49 -07:00
let storage_index = self . get_storage_index ( ) ;
2016-10-14 14:37:32 -07:00
let miner_fee = self . get_transaction_miner_fee ( & t ) ;
Entry {
transaction : t ,
hash : hash ,
ancestors : ancestors ,
2016-10-17 00:08:49 -07:00
storage_index : storage_index ,
2016-10-14 14:37:32 -07:00
size : size ,
miner_fee : miner_fee ,
miner_virtual_fee : 0 ,
2016-10-17 00:08:49 -07:00
// following fields are also updated when inserted to storage
package_size : size ,
package_miner_fee : miner_fee ,
package_miner_virtual_fee : 0 ,
2016-10-14 14:37:32 -07:00
}
}
fn get_ancestors ( & self , t : & Transaction ) -> HashSet < H256 > {
let mut ancestors : HashSet < H256 > = HashSet ::new ( ) ;
let ancestors_entries = t . inputs . iter ( )
2016-11-08 12:01:25 -08:00
. filter_map ( | input | self . storage . get_by_hash ( & input . previous_output . hash ) ) ;
2016-10-14 14:37:32 -07:00
for ancestor_entry in ancestors_entries {
ancestors . insert ( ancestor_entry . hash . clone ( ) ) ;
2016-11-08 12:01:25 -08:00
for grand_ancestor in & ancestor_entry . ancestors {
2016-10-14 14:37:32 -07:00
ancestors . insert ( grand_ancestor . clone ( ) ) ;
}
}
ancestors
}
fn get_transaction_size ( & self , t : & Transaction ) -> usize {
2016-10-17 02:17:00 -07:00
t . serialized_size ( )
2016-10-14 14:37:32 -07:00
}
fn get_transaction_miner_fee ( & self , t : & Transaction ) -> i64 {
let input_value = 0 ; // TODO: sum all inputs of transaction
2016-11-08 12:01:25 -08:00
let output_value = t . outputs . iter ( ) . fold ( 0 , | acc , output | acc + output . value ) ;
2016-10-14 14:37:32 -07:00
( output_value - input_value ) as i64
}
#[ cfg(not(test)) ]
2016-10-17 00:08:49 -07:00
fn get_storage_index ( & mut self ) -> u64 {
2016-10-14 14:37:32 -07:00
self . storage . counter + = 1 ;
self . storage . counter
}
#[ cfg(test) ]
2016-10-17 00:08:49 -07:00
fn get_storage_index ( & self ) -> u64 {
2016-10-14 14:37:32 -07:00
( self . storage . by_hash . len ( ) % 3 usize ) as u64
}
}
2016-11-24 23:39:56 -08:00
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 ( )
}
}
2016-11-29 05:40:55 -08:00
impl PreviousTransactionOutputProvider for MemoryPool {
fn previous_transaction_output ( & self , prevout : & OutPoint ) -> Option < TransactionOutput > {
self . get ( & prevout . hash )
2016-11-29 07:19:54 -08:00
. and_then ( | tx | tx . outputs . get ( prevout . index as usize ) )
2016-11-29 05:40:55 -08:00
. cloned ( )
}
2016-11-30 06:19:23 -08:00
2016-12-02 03:45:14 -08:00
fn is_spent ( & self , prevout : & OutPoint ) -> bool {
self . storage . is_output_spent ( prevout )
2016-11-30 06:19:23 -08:00
}
2016-11-29 05:40:55 -08:00
}
2016-10-19 03:07:11 -07:00
impl HeapSizeOf for MemoryPool {
fn heap_size_of_children ( & self ) -> usize {
self . storage . heap_size_of_children ( )
}
}
2016-10-14 14:37:32 -07:00
#[ cfg(test) ]
mod tests {
2016-12-02 03:45:14 -08:00
use db ::PreviousTransactionOutputProvider ;
use chain ::{ Transaction , OutPoint } ;
2016-10-19 03:07:11 -07:00
use heapsize ::HeapSizeOf ;
2016-10-14 14:37:32 -07:00
use super ::{ MemoryPool , OrderingStrategy } ;
2016-10-20 07:21:28 -07:00
use test_data ::{ ChainBuilder , TransactionBuilder } ;
2016-10-14 14:37:32 -07:00
2016-10-20 07:21:28 -07:00
fn to_memory_pool ( chain : & mut ChainBuilder ) -> MemoryPool {
2016-10-14 14:37:32 -07:00
let mut pool = MemoryPool ::new ( ) ;
2016-10-20 07:21:28 -07:00
for transaction in chain . transactions . iter ( ) . cloned ( ) {
pool . insert_verified ( transaction ) ;
}
2016-10-14 14:37:32 -07:00
pool
}
2016-10-19 03:07:11 -07:00
#[ test ]
fn test_memory_pool_heap_size ( ) {
let mut pool = MemoryPool ::new ( ) ;
let size1 = pool . heap_size_of_children ( ) ;
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
2016-10-19 03:07:11 -07:00
let size2 = pool . heap_size_of_children ( ) ;
assert! ( size2 > size1 ) ;
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
2016-10-19 03:07:11 -07:00
let size3 = pool . heap_size_of_children ( ) ;
assert! ( size3 > size2 ) ;
}
2016-10-17 00:08:49 -07:00
#[ test ]
fn test_memory_pool_insert_same_transaction ( ) {
let mut pool = MemoryPool ::new ( ) ;
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . get_transactions_ids ( ) . len ( ) , 1 ) ;
// insert the same transaction again
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . get_transactions_ids ( ) . len ( ) , 1 ) ;
}
#[ test ]
fn test_memory_pool_read_with_strategy ( ) {
let mut pool = MemoryPool ::new ( ) ;
assert_eq! ( pool . read_with_strategy ( OrderingStrategy ::ByTimestamp ) , None ) ;
assert_eq! ( pool . read_n_with_strategy ( 100 , OrderingStrategy ::ByTimestamp ) , vec! [ ] ) ;
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
assert_eq! ( pool . read_with_strategy ( OrderingStrategy ::ByTimestamp ) , Some ( Transaction ::default ( ) . hash ( ) ) ) ;
assert_eq! ( pool . read_n_with_strategy ( 100 , OrderingStrategy ::ByTimestamp ) , vec! [ Transaction ::default ( ) . hash ( ) ] ) ;
assert_eq! ( pool . read_with_strategy ( OrderingStrategy ::ByTimestamp ) , Some ( Transaction ::default ( ) . hash ( ) ) ) ;
assert_eq! ( pool . read_n_with_strategy ( 100 , OrderingStrategy ::ByTimestamp ) , vec! [ Transaction ::default ( ) . hash ( ) ] ) ;
2016-10-17 00:08:49 -07:00
}
#[ test ]
fn test_memory_pool_remove_with_strategy ( ) {
let mut pool = MemoryPool ::new ( ) ;
assert_eq! ( pool . remove_with_strategy ( OrderingStrategy ::ByTimestamp ) , None ) ;
assert_eq! ( pool . remove_n_with_strategy ( 100 , OrderingStrategy ::ByTimestamp ) , vec! [ ] ) ;
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
2016-10-17 00:08:49 -07:00
let removed = pool . remove_with_strategy ( OrderingStrategy ::ByTimestamp ) ;
assert! ( removed . is_some ( ) ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( removed . unwrap ( ) , Transaction ::default ( ) ) ;
2016-10-17 00:08:49 -07:00
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
2016-10-17 00:08:49 -07:00
let removed = pool . remove_n_with_strategy ( 100 , OrderingStrategy ::ByTimestamp ) ;
assert_eq! ( removed . len ( ) , 1 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( removed [ 0 ] , Transaction ::default ( ) ) ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . remove_with_strategy ( OrderingStrategy ::ByTimestamp ) , None ) ;
assert_eq! ( pool . remove_n_with_strategy ( 100 , OrderingStrategy ::ByTimestamp ) , vec! [ ] ) ;
}
2016-10-14 14:37:32 -07:00
#[ test ]
fn test_memory_pool_remove_by_hash ( ) {
2016-10-20 07:21:28 -07:00
let mut pool = MemoryPool ::new ( ) ;
2016-10-14 14:37:32 -07:00
2016-10-20 07:21:28 -07:00
pool . insert_verified ( Transaction ::default ( ) ) ;
assert_eq! ( pool . get_transactions_ids ( ) . len ( ) , 1 ) ;
2016-10-14 14:37:32 -07:00
// remove and check remaining transactions
2016-10-20 07:21:28 -07:00
let removed = pool . remove_by_hash ( & Transaction ::default ( ) . hash ( ) ) ;
2016-10-14 14:37:32 -07:00
assert! ( removed . is_some ( ) ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( removed . unwrap ( ) , Transaction ::default ( ) ) ;
assert_eq! ( pool . get_transactions_ids ( ) . len ( ) , 0 ) ;
2016-10-17 00:08:49 -07:00
// remove non-existant transaction
2016-10-20 07:21:28 -07:00
assert_eq! ( pool . remove_by_hash ( & TransactionBuilder ::with_version ( 1 ) . hash ( ) ) , None ) ;
assert_eq! ( pool . get_transactions_ids ( ) . len ( ) , 0 ) ;
2016-10-14 14:37:32 -07:00
}
#[ test ]
2016-10-17 00:08:49 -07:00
fn test_memory_pool_insert_parent_after_child ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
TransactionBuilder ::with_output ( 100 ) . store ( chain )
2016-11-09 02:36:52 -08:00
. into_input ( 0 ) . add_output ( 100 ) . store ( chain )
. into_input ( 0 ) . add_output ( 100 ) . store ( chain ) ;
2016-10-14 14:37:32 -07:00
// insert child, then parent
let mut pool = MemoryPool ::new ( ) ;
2016-10-20 07:21:28 -07:00
pool . insert_verified ( chain . at ( 2 ) ) ; // timestamp 0
pool . insert_verified ( chain . at ( 1 ) ) ; // timestamp 1
pool . insert_verified ( chain . at ( 0 ) ) ; // timestamp 2
2016-10-14 14:37:32 -07:00
// check that parent transaction was removed before child trnasaction
let transactions = pool . remove_n_with_strategy ( 3 , OrderingStrategy ::ByTimestamp ) ;
2016-10-17 00:08:49 -07:00
assert_eq! ( transactions . len ( ) , 3 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( transactions [ 0 ] , chain . at ( 0 ) ) ;
assert_eq! ( transactions [ 1 ] , chain . at ( 1 ) ) ;
assert_eq! ( transactions [ 2 ] , chain . at ( 2 ) ) ;
2016-10-14 14:37:32 -07:00
}
#[ test ]
2016-10-17 00:08:49 -07:00
fn test_memory_pool_insert_parent_before_child ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
TransactionBuilder ::with_output ( 100 ) . store ( chain )
2016-11-09 02:36:52 -08:00
. into_input ( 0 ) . add_output ( 100 ) . store ( chain )
. into_input ( 0 ) . add_output ( 100 ) . store ( chain ) ;
2016-10-14 14:37:32 -07:00
2016-10-20 07:21:28 -07:00
// insert parent, then child
let mut pool = to_memory_pool ( chain ) ;
2016-10-14 14:37:32 -07:00
// check that parent transaction was removed before child trnasaction
let transactions = pool . remove_n_with_strategy ( 3 , OrderingStrategy ::ByTimestamp ) ;
2016-10-17 00:08:49 -07:00
assert_eq! ( transactions . len ( ) , 3 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( transactions [ 0 ] , chain . at ( 0 ) ) ;
assert_eq! ( transactions [ 1 ] , chain . at ( 1 ) ) ;
assert_eq! ( transactions [ 2 ] , chain . at ( 2 ) ) ;
2016-10-17 00:08:49 -07:00
}
#[ test ]
fn test_memory_pool_insert_child_after_remove_by_hash ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
TransactionBuilder ::with_output ( 100 ) . store ( chain )
2016-11-09 02:36:52 -08:00
. into_input ( 0 ) . add_output ( 100 ) . store ( chain )
. into_input ( 0 ) . add_output ( 100 ) . store ( chain ) ;
2016-10-17 00:08:49 -07:00
2016-10-20 07:21:28 -07:00
// insert parent, then child
let mut pool = to_memory_pool ( chain ) ;
2016-10-17 00:08:49 -07:00
// remove child transaction & make sure that other transactions are still there
2016-10-20 07:21:28 -07:00
pool . remove_by_hash ( & chain . hash ( 1 ) ) ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . get_transactions_ids ( ) . len ( ) , 2 ) ;
// insert child transaction back to the pool & assert transactions are removed in correct order
2016-10-20 07:21:28 -07:00
pool . insert_verified ( chain . at ( 1 ) ) ;
2016-10-17 00:08:49 -07:00
let transactions = pool . remove_n_with_strategy ( 3 , OrderingStrategy ::ByTransactionScore ) ;
assert_eq! ( transactions . len ( ) , 3 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( transactions [ 0 ] , chain . at ( 0 ) ) ;
assert_eq! ( transactions [ 1 ] , chain . at ( 1 ) ) ;
assert_eq! ( transactions [ 2 ] , chain . at ( 2 ) ) ;
2016-10-14 14:37:32 -07:00
}
#[ test ]
fn test_memory_pool_get_information ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
TransactionBuilder ::with_output ( 10 ) . store ( chain )
2016-11-09 02:36:52 -08:00
. into_input ( 0 ) . add_output ( 20 ) . store ( chain )
. into_input ( 0 ) . add_output ( 30 ) . store ( chain )
. into_input ( 0 ) . add_output ( 40 ) . store ( chain ) ;
2016-10-20 07:21:28 -07:00
let mut pool = MemoryPool ::new ( ) ;
2016-10-14 14:37:32 -07:00
2016-10-20 07:21:28 -07:00
let mut transactions_size = 0 ;
for transaction_index in 0 .. 4 {
pool . insert_verified ( chain . at ( transaction_index ) ) ;
transactions_size + = chain . size ( transaction_index ) ;
let info = pool . information ( ) ;
assert_eq! ( info . transactions_count , transaction_index + 1 ) ;
assert_eq! ( info . transactions_size_in_bytes , transactions_size ) ;
2016-10-14 14:37:32 -07:00
}
}
#[ test ]
fn test_memory_pool_timestamp_ordering_strategy ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
TransactionBuilder ::with_output ( 10 ) . store ( chain )
. set_output ( 20 ) . store ( chain )
. set_output ( 30 ) . store ( chain )
. set_output ( 40 ) . store ( chain ) ;
let mut pool = to_memory_pool ( chain ) ;
// remove transactions [0, 3, 1] (timestamps: [0, 0, 1]) {conflict resolved by hash}
2016-10-14 14:37:32 -07:00
let transactions = pool . remove_n_with_strategy ( 3 , OrderingStrategy ::ByTimestamp ) ;
assert_eq! ( transactions . len ( ) , 3 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( transactions [ 0 ] , chain . at ( 0 ) ) ;
assert_eq! ( transactions [ 1 ] , chain . at ( 3 ) ) ;
assert_eq! ( transactions [ 2 ] , chain . at ( 1 ) ) ;
2016-10-14 14:37:32 -07:00
assert_eq! ( pool . get_transactions_ids ( ) . len ( ) , 1 ) ;
2016-10-20 07:21:28 -07:00
// remove transactions [2] (timestamps: [2])
2016-10-14 14:37:32 -07:00
let transactions = pool . remove_n_with_strategy ( 3 , OrderingStrategy ::ByTimestamp ) ;
assert_eq! ( transactions . len ( ) , 1 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( transactions [ 0 ] , chain . at ( 2 ) ) ;
2016-10-14 14:37:32 -07:00
}
#[ test ]
2016-10-17 00:08:49 -07:00
fn test_memory_pool_transaction_score_ordering_strategy ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
TransactionBuilder ::with_output ( 10 ) . store ( chain )
. set_output ( 40 ) . store ( chain )
. set_output ( 30 ) . store ( chain )
. set_output ( 20 ) . store ( chain ) ;
let mut pool = to_memory_pool ( chain ) ;
2016-10-14 14:37:32 -07:00
2016-10-17 00:08:49 -07:00
let transactions = pool . remove_n_with_strategy ( 4 , OrderingStrategy ::ByTransactionScore ) ;
2016-10-14 14:37:32 -07:00
assert_eq! ( transactions . len ( ) , 4 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( transactions [ 0 ] , chain . at ( 1 ) ) ;
assert_eq! ( transactions [ 1 ] , chain . at ( 2 ) ) ;
assert_eq! ( transactions [ 2 ] , chain . at ( 3 ) ) ;
assert_eq! ( transactions [ 3 ] , chain . at ( 0 ) ) ;
2016-10-14 14:37:32 -07:00
}
#[ test ]
2016-10-17 00:08:49 -07:00
fn test_memory_pool_transaction_score_ordering_strategy_with_virtual_fee ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
TransactionBuilder ::with_output ( 10 ) . store ( chain )
. set_output ( 40 ) . store ( chain )
. set_output ( 30 ) . store ( chain )
. set_output ( 20 ) . store ( chain ) ;
let mut pool = to_memory_pool ( chain ) ;
// increase miner score of transaction 3 to move it to position #1
pool . set_virtual_fee ( & chain . hash ( 3 ) , 100 ) ;
// decrease miner score of transaction 1 to move it to position #4
pool . set_virtual_fee ( & chain . hash ( 1 ) , - 30 ) ;
2016-10-14 14:37:32 -07:00
2016-10-17 00:08:49 -07:00
let transactions = pool . remove_n_with_strategy ( 4 , OrderingStrategy ::ByTransactionScore ) ;
2016-10-14 14:37:32 -07:00
assert_eq! ( transactions . len ( ) , 4 ) ;
2016-10-20 07:21:28 -07:00
assert_eq! ( transactions [ 0 ] , chain . at ( 3 ) ) ;
assert_eq! ( transactions [ 1 ] , chain . at ( 2 ) ) ;
assert_eq! ( transactions [ 2 ] , chain . at ( 0 ) ) ;
assert_eq! ( transactions [ 3 ] , chain . at ( 1 ) ) ;
2016-10-17 00:08:49 -07:00
}
#[ test ]
fn test_memory_pool_package_score_ordering_strategy ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
// all transactions of same size
TransactionBuilder ::with_default_input ( 0 ) . set_output ( 30 ) . store ( chain ) // transaction0
2016-11-09 02:36:52 -08:00
. into_input ( 0 ) . set_output ( 50 ) . store ( chain ) // transaction0 -> transaction1
2016-12-02 03:45:14 -08:00
. set_default_input ( 1 ) . set_output ( 35 ) . store ( chain ) // transaction2
2016-11-09 02:36:52 -08:00
. into_input ( 0 ) . set_output ( 10 ) . store ( chain ) // transaction2 -> transaction3
. into_input ( 0 ) . set_output ( 100 ) . store ( chain ) ; // transaction2 -> transaction3 -> transaction4
2016-10-17 00:08:49 -07:00
let mut pool = MemoryPool ::new ( ) ;
2016-10-20 07:21:28 -07:00
// compared by simple transaction score:
// score({ transaction0 }) = 30/60
2016-10-17 00:08:49 -07:00
// <
2016-10-20 07:21:28 -07:00
// score({ transaction2 }) = 35/60
let expected = vec! [ chain . hash ( 2 ) , chain . hash ( 0 ) ] ;
pool . insert_verified ( chain . at ( 0 ) ) ;
pool . insert_verified ( chain . at ( 2 ) ) ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 2 , OrderingStrategy ::ByPackageScore ) , expected ) ;
2016-10-20 07:21:28 -07:00
// { transaction0, transaction1 } now have bigger score than { transaction2 }:
// score({ transaction0, transaction1 }) = (30 + 50) / 120 ~ 0.667
2016-10-17 00:08:49 -07:00
// >
2016-10-20 07:21:28 -07:00
// score({ transaction2 }) = 35/60 ~ 0.583
2016-10-17 00:08:49 -07:00
// => chain1 is boosted
// => so transaction with lesser individual score (but with bigger package score) is mined first
2016-10-20 07:21:28 -07:00
pool . insert_verified ( chain . at ( 1 ) ) ;
let expected = vec! [ chain . hash ( 0 ) , chain . hash ( 1 ) , chain . hash ( 2 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 3 , OrderingStrategy ::ByPackageScore ) , expected ) ;
2016-10-20 07:21:28 -07:00
// { transaction0, transaction1 } still have bigger score than { transaction2, transaction3 }
// score({ transaction0, transaction1 }) = (30 + 35) / 120 ~ 0.625
2016-10-17 00:08:49 -07:00
// >
2016-10-20 07:21:28 -07:00
// score({ transaction2, transaction3 }) = (35 + 10) / 120 ~ 0.375
2016-10-17 00:08:49 -07:00
// => chain2 is not boosted
2016-10-20 07:21:28 -07:00
pool . insert_verified ( chain . at ( 3 ) ) ;
let expected = vec! [ chain . hash ( 0 ) , chain . hash ( 1 ) , chain . hash ( 2 ) , chain . hash ( 3 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 4 , OrderingStrategy ::ByPackageScore ) , expected ) ;
2016-10-20 07:21:28 -07:00
// { transaction0, transaction1 } now have lesser score than { transaction2, transaction3, transaction4 }
// score({ transaction0, transaction1 }) = (30 + 35) / 120 ~ 0.625
2016-10-17 00:08:49 -07:00
// <
2016-10-20 07:21:28 -07:00
// score({ transaction2, transaction3, transaction4 }) = (35 + 10 + 100) / 180 ~ 0.806
2016-10-17 00:08:49 -07:00
// => chain2 is boosted
2016-10-20 07:21:28 -07:00
pool . insert_verified ( chain . at ( 4 ) ) ;
let expected = vec! [ chain . hash ( 2 ) , chain . hash ( 3 ) , chain . hash ( 4 ) , chain . hash ( 0 ) , chain . hash ( 1 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 5 , OrderingStrategy ::ByPackageScore ) , expected ) ;
2016-10-20 07:21:28 -07:00
// add virtual fee to the transaction1 so that chain1 is back to the position #1
pool . set_virtual_fee ( & chain . hash ( 1 ) , 500 i64 ) ;
let expected = vec! [ chain . hash ( 0 ) , chain . hash ( 1 ) , chain . hash ( 2 ) , chain . hash ( 3 ) , chain . hash ( 4 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 5 , OrderingStrategy ::ByPackageScore ) , expected ) ;
}
#[ test ]
fn test_memory_pool_package_score_ordering_strategy_opposite_insert_order ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
// all transactions of same size
TransactionBuilder ::with_default_input ( 0 ) . set_output ( 17 ) . store ( chain ) // transaction0
2016-11-09 02:36:52 -08:00
. into_input ( 0 ) . set_output ( 50 ) . store ( chain ) // transaction0 -> transaction1
2016-12-02 03:45:14 -08:00
. into_input ( 0 ) . set_output ( 7 ) . store ( chain ) // transaction0 -> transaction1 -> transaction2
. set_default_input ( 1 ) . set_output ( 20 ) . store ( chain ) ; // transaction3
2016-10-17 00:08:49 -07:00
let mut pool = MemoryPool ::new ( ) ;
// chain1_parent is not linked to the chain1_grandchild
// => they are in separate chains now
// => chain2 has greater score than both of these chains
2016-10-20 07:21:28 -07:00
pool . insert_verified ( chain . at ( 3 ) ) ;
pool . insert_verified ( chain . at ( 0 ) ) ;
pool . insert_verified ( chain . at ( 2 ) ) ;
let expected = vec! [ chain . hash ( 3 ) , chain . hash ( 0 ) , chain . hash ( 2 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 3 , OrderingStrategy ::ByPackageScore ) , expected ) ;
// insert the missing transaction to link together chain1
// => it now will have better score than chain2
2016-10-20 07:21:28 -07:00
pool . insert_verified ( chain . at ( 1 ) ) ;
let expected = vec! [ chain . hash ( 0 ) , chain . hash ( 1 ) , chain . hash ( 3 ) , chain . hash ( 2 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 4 , OrderingStrategy ::ByPackageScore ) , expected ) ;
}
#[ test ]
fn test_memory_pool_complex_transactions_tree_opposite_insert_order ( ) {
2016-10-20 07:21:28 -07:00
let chain = & mut ChainBuilder ::new ( ) ;
// all transactions of same size (=> 3 inputs)
// construct level0
2016-12-02 03:45:14 -08:00
TransactionBuilder ::with_default_input ( 0 ) . add_default_input ( 1 ) . add_default_input ( 2 ) . set_output ( 10 ) . add_output ( 10 ) . store ( chain ) // transaction0
. set_default_input ( 3 ) . add_default_input ( 4 ) . add_default_input ( 5 ) . set_output ( 20 ) . add_output ( 20 ) . store ( chain ) // transaction1
. set_default_input ( 6 ) . add_default_input ( 7 ) . add_default_input ( 8 ) . set_output ( 30 ) . add_output ( 30 ) . store ( chain ) // transaction2
2016-10-20 07:21:28 -07:00
// construct level1
2016-12-02 03:45:14 -08:00
. set_default_input ( 9 ) . add_default_input ( 10 ) . add_input ( & chain . at ( 0 ) , 0 ) . set_output ( 40 ) . add_output ( 40 ) . store ( chain ) // transaction0 -> transaction3
. set_default_input ( 11 ) . add_input ( & chain . at ( 0 ) , 1 ) . add_input ( & chain . at ( 1 ) , 0 ) . set_output ( 50 ) . add_output ( 50 ) . store ( chain ) // transaction0 + transaction1 -> transaction4
2016-10-20 07:21:28 -07:00
// construct level3
2016-12-02 03:45:14 -08:00
. set_input ( & chain . at ( 2 ) , 0 ) . add_input ( & chain . at ( 3 ) , 0 ) . add_input ( & chain . at ( 4 ) , 0 ) . set_output ( 60 ) . add_output ( 60 ) . store ( chain ) ; // transaction2 + transaction3 + transaction4 -> transaction5
2016-10-17 00:08:49 -07:00
let mut pool = MemoryPool ::new ( ) ;
// insert level1 + level2. There are two chains:
2016-10-20 07:21:28 -07:00
// score({ transaction3, transaction5 }) = 40 + 60
// score({ transaction4, transaction5 }) = 50 + 60
pool . insert_verified ( chain . at ( 5 ) ) ;
pool . insert_verified ( chain . at ( 3 ) ) ;
pool . insert_verified ( chain . at ( 4 ) ) ;
let expected = vec! [ chain . hash ( 4 ) , chain . hash ( 3 ) , chain . hash ( 5 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 3 , OrderingStrategy ::ByTransactionScore ) , expected ) ;
assert_eq! ( pool . read_n_with_strategy ( 3 , OrderingStrategy ::ByPackageScore ) , expected ) ;
// insert another one transaction from the chain. Three chains:
2016-10-20 07:21:28 -07:00
// score({ transaction3, transaction5 }) = 40 + 60
// score({ transaction4, transaction5 }) = 50 + 60
// score({ transaction2, transaction5 }) = 30 + 60
pool . insert_verified ( chain . at ( 2 ) ) ;
let expected = vec! [ chain . hash ( 4 ) , chain . hash ( 3 ) , chain . hash ( 2 ) , chain . hash ( 5 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 4 , OrderingStrategy ::ByTransactionScore ) , expected ) ;
assert_eq! ( pool . read_n_with_strategy ( 4 , OrderingStrategy ::ByPackageScore ) , expected ) ;
// insert another one transaction from the chain. Three chains:
2016-10-20 07:21:28 -07:00
// score({ transaction3, transaction5 }) = 40 + 60 / 2 = 0.5
// score({ transaction1, transaction4, transaction5 }) = 20 + 50 + 60 / 3 ~ 0.333
// score({ transaction2, transaction5 }) = 30 + 60 / 2 = 0.45
// but second chain will be removed first anyway because previous #1 ({ transaction4, transaction5}) now depends on level 01
pool . insert_verified ( chain . at ( 1 ) ) ;
let expected = vec! [ chain . hash ( 3 ) , chain . hash ( 2 ) , chain . hash ( 1 ) , chain . hash ( 4 ) , chain . hash ( 5 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 5 , OrderingStrategy ::ByTransactionScore ) , expected ) ;
assert_eq! ( pool . read_n_with_strategy ( 5 , OrderingStrategy ::ByPackageScore ) , expected ) ;
// insert another one transaction from the chain. Four chains:
2016-10-20 07:21:28 -07:00
// score({ transaction0, transaction3, transaction5 }) = (10 + 40 + 60) / (60 + 60 + 142) ~ 0.420
// score({ transaction0, transaction4, transaction5 }) = (10 + 50 + 60) / (60 + 60 + 142) ~ 0.458
// score({ transaction1, transaction3, transaction5 }) = (20 + 50 + 60) / (60 + 60 + 142) ~ 0.496
// score({ transaction2, transaction5 }) = (30 + 60) / (60 + 142) ~ 0.445
pool . insert_verified ( chain . at ( 0 ) ) ;
let expected = vec! [ chain . hash ( 2 ) , chain . hash ( 1 ) , chain . hash ( 0 ) , chain . hash ( 4 ) , chain . hash ( 3 ) , chain . hash ( 5 ) ] ;
2016-10-17 00:08:49 -07:00
assert_eq! ( pool . read_n_with_strategy ( 6 , OrderingStrategy ::ByTransactionScore ) , expected ) ;
assert_eq! ( pool . read_n_with_strategy ( 6 , OrderingStrategy ::ByPackageScore ) , expected ) ;
2016-10-14 14:37:32 -07:00
}
2016-12-02 03:45:14 -08:00
#[ test ]
fn test_memory_pool_spent_transaction_output ( ) {
let chain = & mut ChainBuilder ::new ( ) ;
// all transactions of same size (=> 3 inputs)
// construct level0
TransactionBuilder ::with_output ( 10 ) . store ( chain ) // transaction0
. set_output ( 20 ) . store ( chain ) // transaction1
. set_input ( & chain . at ( 0 ) , 0 ) . add_output ( 30 ) . store ( chain ) ; // transaction0 -> transaction2
let mut pool = MemoryPool ::new ( ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 0 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 1 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 2 ) , index : 0 , } ) ) ;
pool . insert_verified ( chain . at ( 0 ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 0 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 1 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 2 ) , index : 0 , } ) ) ;
pool . insert_verified ( chain . at ( 1 ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 0 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 1 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 2 ) , index : 0 , } ) ) ;
pool . insert_verified ( chain . at ( 2 ) ) ;
assert! ( pool . is_spent ( & OutPoint { hash : chain . hash ( 0 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 1 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 2 ) , index : 0 , } ) ) ;
pool . remove_by_hash ( & chain . at ( 2 ) . hash ( ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 0 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 1 ) , index : 0 , } ) ) ;
assert! ( ! pool . is_spent ( & OutPoint { hash : chain . hash ( 2 ) , index : 0 , } ) ) ;
}
#[ test ]
fn test_memory_pool_remove_by_prevout ( ) {
let chain = & mut ChainBuilder ::new ( ) ;
// all transactions of same size (=> 3 inputs)
// construct level0
TransactionBuilder ::with_output ( 10 ) . store ( chain ) // transaction0
. into_input ( 0 ) . add_output ( 20 ) . store ( chain ) // transaction0 -> transaction1
. into_input ( 0 ) . add_output ( 30 ) . store ( chain ) // transaction0 -> transaction1 -> transaction2
. reset ( ) . add_output ( 40 ) . store ( chain ) ; // transaction3
let mut pool = MemoryPool ::new ( ) ;
2016-12-02 03:51:32 -08:00
2016-12-02 03:45:14 -08:00
pool . insert_verified ( chain . at ( 0 ) ) ;
pool . insert_verified ( chain . at ( 1 ) ) ;
pool . insert_verified ( chain . at ( 2 ) ) ;
pool . insert_verified ( chain . at ( 3 ) ) ;
assert_eq! ( pool . information ( ) . transactions_count , 4 ) ;
assert_eq! ( pool . remove_by_prevout ( & OutPoint { hash : chain . hash ( 0 ) , index : 0 } ) , Some ( vec! [ chain . at ( 1 ) , chain . at ( 2 ) ] ) ) ;
assert_eq! ( pool . information ( ) . transactions_count , 2 ) ;
}
2016-10-14 14:37:32 -07:00
}