// 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. //! Base types for the transaction pool's Directed Acyclic Graphs 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 secp::pedersen::Commitment; use secp::{Secp256k1, ContextFlag}; use secp::key; use time; use rand; use std::fmt; use core::core; /// An entry in the transaction pool. /// These are the vertices of both of the graph structures pub struct PoolEntry { // Core data // Unique identifier of this pool entry and the corresponding transaction pub transaction_hash: core::hash::Hash, // Metadata size_estimate: u64, pub receive_ts: time::Tm, } impl PoolEntry { pub fn new(tx: &core::transaction::Transaction) -> PoolEntry { PoolEntry{ transaction_hash: transaction_identifier(tx), size_estimate : estimate_transaction_size(tx), receive_ts: time::now()} } } fn estimate_transaction_size(tx: &core::transaction::Transaction) -> u64 { 0 } /// An edge connecting graph vertices. /// For various use cases, one of either the source or destination may be /// unpopulated pub struct Edge { // Source and Destination are the vertex id's, the transaction (kernel) // hash. source: Option, destination: Option, // Output is the output hash which this input/output pairing corresponds // to. output: Commitment, } impl Edge{ pub fn new(source: Option, destination: Option, output: Commitment) -> Edge { Edge{source: source, destination: destination, output: output} } pub fn with_source(&self, src: Option) -> Edge { Edge{source: src, destination: self.destination, output: self.output} } pub fn with_destination(&self, dst: Option) -> Edge { Edge{source: self.source, destination: dst, output: self.output} } pub fn output_commitment(&self) -> Commitment { self.output } pub fn destination_hash(&self) -> Option { self.destination } pub fn source_hash(&self) -> Option { self.source } } impl fmt::Debug for Edge { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Edge {{source: {:?}, destination: {:?}, commitment: {:?}}}", self.source, self.destination, self.output) } } /// The generic graph container. Both graphs, the pool and orphans, embed this /// structure and add additional capability on top of it. pub struct DirectedGraph { edges: HashMap, vertices: Vec, // A small optimization: keeping roots (vertices with in-degree 0) in a // separate list makes topological sort a bit faster. (This is true for // Kahn's, not sure about other implementations) roots: Vec, } impl DirectedGraph { pub fn empty() -> DirectedGraph { DirectedGraph{ edges: HashMap::new(), vertices: Vec::new(), roots: Vec::new(), } } pub fn get_edge_by_commitment(&self, output_commitment: &Commitment) -> Option<&Edge> { self.edges.get(output_commitment) } pub fn remove_edge_by_commitment(&mut self, output_commitment: &Commitment) -> Option { self.edges.remove(output_commitment) } pub fn remove_vertex(&mut self, tx_hash: core::hash::Hash) -> Option { match self.roots.iter().position(|x| x.transaction_hash == tx_hash) { Some(i) => Some(self.roots.swap_remove(i)), None => { match self.vertices.iter().position(|x| x.transaction_hash == tx_hash) { Some(i) => Some(self.vertices.swap_remove(i)), None => None, } } } } /// Adds a vertex and a set of incoming edges to the graph. /// /// The PoolEntry at vertex is added to the graph; depending on the /// number of incoming edges, the vertex is either added to the vertices /// or to the roots. /// /// Outgoing edges must not be included in edges; this method is designed /// for adding vertices one at a time and only accepts incoming edges as /// internal edges. pub fn add_entry(&mut self, vertex: PoolEntry, mut edges: Vec) { if edges.len() == 0 { self.roots.push(vertex); } else { self.vertices.push(vertex); for edge in edges.drain(..) { self.edges.insert(edge.output_commitment(), edge); } } } // add_vertex_only adds a vertex, meant to be complemented by add_edge_only // in cases where delivering a vector of edges is not feasible or efficient pub fn add_vertex_only(&mut self, vertex: PoolEntry, is_root: bool) { if is_root { self.roots.push(vertex); } else { self.vertices.push(vertex); } } pub fn add_edge_only(&mut self, edge: Edge) { self.edges.insert(edge.output_commitment(), edge); } /// Number of vertices (root + internal) pub fn len_vertices(&self) -> usize { self.vertices.len() + self.roots.len() } /// Number of root vertices only pub fn len_roots(&self) -> usize { self.roots.len() } /// Number of edges pub fn len_edges(&self) -> usize { self.edges.len() } /// Get the current list of roots pub fn get_roots(&self) -> Vec { self.roots.iter().map(|x| x.transaction_hash).collect() } } /// Using transaction merkle_inputs_outputs to calculate a deterministic hash; /// this hashing mechanism has some ambiguity issues especially around range /// proofs and any extra data the kernel may cover, but it is used initially /// for testing purposes. pub fn transaction_identifier(tx: &core::transaction::Transaction) -> core::hash::Hash { core::transaction::merkle_inputs_outputs(&tx.inputs, &tx.outputs) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_entry() { let ec = Secp256k1::with_caps(ContextFlag::Commit); let output_commit = ec.commit_value(70).unwrap(); let inputs = vec![core::transaction::Input(ec.commit_value(50).unwrap()), core::transaction::Input(ec.commit_value(25).unwrap())]; let outputs = vec![core::transaction::Output{ features: core::transaction::DEFAULT_OUTPUT, commit: output_commit, proof: ec.range_proof(0, 100, key::ZERO_KEY, output_commit)}]; let test_transaction = core::transaction::Transaction::new(inputs, outputs, 5); let test_pool_entry = PoolEntry::new(&test_transaction); let incoming_edge_1 = Edge::new(Some(random_hash()), Some(core::hash::ZERO_HASH), output_commit); let mut test_graph = DirectedGraph::empty(); test_graph.add_entry(test_pool_entry, vec![incoming_edge_1]); assert_eq!(test_graph.vertices.len(), 1); assert_eq!(test_graph.roots.len(), 0); assert_eq!(test_graph.edges.len(), 1); } } /// For testing/debugging: a random tx hash fn random_hash() -> core::hash::Hash { let hash_bytes: [u8;32]= rand::random(); core::hash::Hash(hash_bytes) }