// Copyright 2017 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! The primary module containing the implementations of the transaction pool //! and its top-level members. use std::vec::Vec; use std::sync::Arc; use std::sync::RwLock; use std::sync::Weak; use std::cell::RefCell; use std::collections::HashMap; use std::iter::Iterator; use std::fmt; use secp::pedersen::Commitment; pub use graph; use time; use core::core::transaction; use core::core::block; use core::core::hash; /// Placeholder: the data representing where we heard about a tx from. /// /// Used to make decisions based on transaction acceptance priority from /// various sources. For example, a node may want to bypass pool size /// restrictions when accepting a transaction from a local wallet. /// /// Most likely this will evolve to contain some sort of network identifier, /// once we get a better sense of what transaction building might look like. pub struct TxSource { /// Human-readable name used for logging and errors. pub debug_name: String, /// Unique identifier used to distinguish this peer from others. pub identifier: String, } /// This enum describes the parent for a given input of a transaction. #[derive(Clone)] pub enum Parent { Unknown, BlockTransaction, PoolTransaction{tx_ref: hash::Hash}, AlreadySpent{other_tx: hash::Hash}, } impl fmt::Debug for Parent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &Parent::Unknown => write!(f, "Parent: Unknown"), &Parent::BlockTransaction => write!(f, "Parent: Block Transaction"), &Parent::PoolTransaction{tx_ref: x} => write!(f, "Parent: Pool Transaction ({:?})", x), &Parent::AlreadySpent{other_tx: x} => write!(f, "Parent: Already Spent By {:?}", x), } } } #[derive(Debug)] pub enum PoolError { Invalid, AlreadyInPool, DuplicateOutput{other_tx: Option, in_chain: bool, output: Commitment}, DoubleSpend{other_tx: hash::Hash, spent_output: Commitment}, // An orphan successfully added to the orphans set OrphanTransaction, } /// Pool contains the elements of the graph that are connected, in full, to /// the blockchain. /// Reservations of outputs by orphan transactions (not fully connected) are /// not respected. /// Spending references (input -> output) exist in two structures: internal /// graph references are contained in the pool edge sets, while references /// sourced from the blockchain's UTXO set are contained in the /// blockchain_connections set. /// Spent by references (output-> input) exist in two structures: pool-pool /// connections are in the pool edge set, while unspent (dangling) references /// exist in the available_outputs set. pub struct Pool { graph : graph::DirectedGraph, // available_outputs are unspent outputs of the current pool set, // maintained as edges with empty destinations, keyed by the // output's hash. available_outputs: HashMap, // Consumed blockchain utxo's are kept in a separate map. consumed_blockchain_outputs: HashMap } impl Pool { pub fn empty() -> Pool { Pool{ graph: graph::DirectedGraph::empty(), available_outputs: HashMap::new(), consumed_blockchain_outputs: HashMap::new(), } } /// Given an output, check if a spending reference (input -> output) /// already exists in the pool. /// Returns the transaction (kernel) hash corresponding to the conflicting /// transaction pub fn check_double_spend(&self, o: &transaction::Output) -> Option { self.graph.get_edge_by_commitment(&o.commitment()).or(self.consumed_blockchain_outputs.get(&o.commitment())).map(|x| x.destination_hash().unwrap()) } pub fn get_blockchain_spent(&self, c: &Commitment) -> Option<&graph::Edge> { self.consumed_blockchain_outputs.get(c) } pub fn add_pool_transaction(&mut self, pool_entry: graph::PoolEntry, mut blockchain_refs: Vec, pool_refs: Vec, mut new_unspents: Vec) { // Removing consumed available_outputs for new_edge in &pool_refs { // All of these should correspond to an existing unspent assert!(self.available_outputs.remove(&new_edge.output_commitment()).is_some()); } // Accounting for consumed blockchain outputs for new_blockchain_edge in blockchain_refs.drain(..) { self.consumed_blockchain_outputs.insert( new_blockchain_edge.output_commitment(), new_blockchain_edge); } // Adding the transaction to the vertices list along with internal // pool edges self.graph.add_entry(pool_entry, pool_refs); // Adding the new unspents to the unspent map for unspent_output in new_unspents.drain(..) { self.available_outputs.insert( unspent_output.output_commitment(), unspent_output); } } pub fn remove_pool_transaction(&mut self, tx: &transaction::Transaction, marked_txs: &HashMap) { self.graph.remove_vertex(graph::transaction_identifier(tx)); for input in tx.inputs.iter().map(|x| x.commitment()) { match self.graph.remove_edge_by_commitment(&input) { Some(x) => { if !marked_txs.contains_key(&x.source_hash().unwrap()) { self.available_outputs.insert(x.output_commitment(), x.with_destination(None)); } }, None => { self.consumed_blockchain_outputs.remove(&input); }, }; } for output in tx.outputs.iter().map(|x| x.commitment()) { match self.graph.remove_edge_by_commitment(&output) { Some(x) => { if !marked_txs.contains_key( &x.destination_hash().unwrap()) { self.consumed_blockchain_outputs.insert( x.output_commitment(), x.with_source(None)); } }, None => { self.available_outputs.remove(&output); } }; } } /// Simplest possible implementation: just return the roots pub fn get_mineable_transactions(&self, num_to_fetch: u32) -> Vec { let mut roots = self.graph.get_roots(); roots.truncate(num_to_fetch as usize); roots } } impl TransactionGraphContainer for Pool { fn get_graph(&self) -> &graph::DirectedGraph { &self.graph } fn get_available_output(&self, output: &Commitment) -> Option<&graph::Edge> { self.available_outputs.get(output) } fn get_external_spent_output(&self, output: &Commitment) -> Option<&graph::Edge> { self.consumed_blockchain_outputs.get(output) } fn get_internal_spent_output(&self, output: &Commitment) -> Option<&graph::Edge> { self.graph.get_edge_by_commitment(output) } } /// Orphans contains the elements of the transaction graph that have not been /// connected in full to the blockchain. pub struct Orphans { graph : graph::DirectedGraph, // available_outputs are unspent outputs of the current orphan set, // maintained as edges with empty destinations. available_outputs: HashMap, // missing_outputs are spending references (inputs) with missing // corresponding outputs, maintained as edges with empty sources. missing_outputs: HashMap, // pool_connections are bidirectional edges which connect to the pool // graph. They should map one-to-one to pool graph available_outputs. // pool_connections should not be viewed authoritatively, they are // merely informational until the transaction is officially connected to // the pool. pool_connections: HashMap, } impl Orphans { pub fn empty() -> Orphans { Orphans{ graph: graph::DirectedGraph::empty(), available_outputs : HashMap::new(), missing_outputs: HashMap::new(), pool_connections: HashMap::new(), } } /// Checks for a double spent output, given the hash of the output, /// ONLY in the data maintained by the orphans set. This includes links /// to the pool as well as links internal to orphan transactions. /// Returns the transaction hash corresponding to the conflicting /// transaction. fn check_double_spend(&self, o: transaction::Output) -> Option { self.graph.get_edge_by_commitment(&o.commitment()).or(self.pool_connections.get(&o.commitment())).map(|x| x.destination_hash().unwrap()) } pub fn get_unknown_output(&self, output: &Commitment) -> Option<&graph::Edge> { self.missing_outputs.get(output) } /// Add an orphan transaction to the orphans set. /// /// This method adds a given transaction (represented by the PoolEntry at /// orphan_entry) to the orphans set. /// /// This method has no failure modes. All checks should be passed before /// entry. /// /// Expects a HashMap at is_missing describing the indices of orphan_refs /// which correspond to missing (vs orphan-to-orphan) links. pub fn add_orphan_transaction(&mut self, orphan_entry: graph::PoolEntry, mut pool_refs: Vec, mut orphan_refs: Vec, is_missing: HashMap, mut new_unspents: Vec) { // Removing consumed available_outputs for (i, new_edge) in orphan_refs.drain(..).enumerate() { if is_missing.contains_key(&i) { self.missing_outputs.insert(new_edge.output_commitment(), new_edge); } else { assert!(self.available_outputs.remove(&new_edge.output_commitment()).is_some()); self.graph.add_edge_only(new_edge); } } // Accounting for consumed blockchain and pool outputs for external_edge in pool_refs.drain(..) { self.pool_connections.insert( external_edge.output_commitment(), external_edge); } // if missing_refs is the same length as orphan_refs, we have // no orphan-orphan links for this transaction and it is a // root transaction of the orphans set self.graph.add_vertex_only(orphan_entry, is_missing.len() == orphan_refs.len()); // Adding the new unspents to the unspent map for unspent_output in new_unspents.drain(..) { self.available_outputs.insert( unspent_output.output_commitment(), unspent_output); } } } impl TransactionGraphContainer for Orphans { fn get_graph(&self) -> &graph::DirectedGraph { &self.graph } fn get_available_output(&self, output: &Commitment) -> Option<&graph::Edge> { self.available_outputs.get(output) } fn get_external_spent_output(&self, output: &Commitment) -> Option<&graph::Edge> { self.pool_connections.get(output) } fn get_internal_spent_output(&self, output: &Commitment) -> Option<&graph::Edge> { self.graph.get_edge_by_commitment(output) } } /// Trait for types that embed a graph and connect to external state. /// /// The types implementing this trait consist of a graph with nodes and edges /// representing transactions and outputs, respectively. Outputs fall into one /// of three categories: /// 1) External spent: An output sourced externally consumed by a transaction /// in this graph, /// 2) Internal spent: An output produced by a transaction in this graph and /// consumed by another transaction in this graph, /// 3) [External] Unspent: An output produced by a transaction in this graph /// that is not yet spent. /// /// There is no concept of an external "spent by" reference (output produced by /// a transaction in the graph spent by a transaction in another source), as /// these references are expected to be maintained by descendent graph. Outputs /// follow a heirarchy (Blockchain -> Pool -> Orphans) where each descendent /// exists at a lower priority than their parent. An output consumed by a /// child graph is marked as unspent in the parent graph and an external spent /// in the child. This ensures that no descendent set must modify state in a /// set of higher priority. pub trait TransactionGraphContainer { /// Accessor for graph object fn get_graph(&self) -> &graph::DirectedGraph; /// Accessor for internal spents fn get_internal_spent_output(&self, output: &Commitment) -> Option<&graph::Edge>; /// Accessor for external unspents fn get_available_output(&self, output: &Commitment) -> Option<&graph::Edge>; /// Accessor for external spents fn get_external_spent_output(&self, output: &Commitment) -> Option<&graph::Edge>; /// Checks if the available_output set has the output at the given /// commitment fn has_available_output(&self, c: &Commitment) -> bool { self.get_available_output(c).is_some() } /// Checks if the pool has anything by this output already, between /// available outputs and internal ones. fn find_output(&self, c: &Commitment) -> Option { self.get_available_output(c). or(self.get_internal_spent_output(c)). map(|x| x.source_hash().unwrap()) } /// Search for a spent reference internal to the graph fn get_internal_spent(&self, c: &Commitment) -> Option<&graph::Edge> { self.get_internal_spent_output(c) } fn num_root_transactions(&self) -> usize { self.get_graph().len_roots() } fn num_transactions(&self) -> usize { self.get_graph().len_vertices() } fn num_output_edges(&self) -> usize { self.get_graph().len_edges() } }