Cargo fmt all the things

This commit is contained in:
Ignotus Peverell
2017-09-29 18:44:25 +00:00
parent 3b51180359
commit 8504efb796
57 changed files with 3678 additions and 2600 deletions
+123 -103
View File
@@ -22,142 +22,162 @@ use types::{BlockChain, PoolError};
#[derive(Debug)]
pub struct DummyBlockHeaderIndex {
block_headers: HashMap<Commitment, block::BlockHeader>
block_headers: HashMap<Commitment, block::BlockHeader>,
}
impl DummyBlockHeaderIndex {
pub fn insert(&mut self, commit: Commitment, block_header: block::BlockHeader) {
self.block_headers.insert(commit, block_header);
}
pub fn insert(&mut self, commit: Commitment, block_header: block::BlockHeader) {
self.block_headers.insert(commit, block_header);
}
pub fn get_block_header_by_output_commit(&self, commit: Commitment) -> Result<&block::BlockHeader, PoolError> {
match self.block_headers.get(&commit) {
Some(h) => Ok(h),
None => Err(PoolError::GenericPoolError)
}
}
pub fn get_block_header_by_output_commit(
&self,
commit: Commitment,
) -> Result<&block::BlockHeader, PoolError> {
match self.block_headers.get(&commit) {
Some(h) => Ok(h),
None => Err(PoolError::GenericPoolError),
}
}
}
/// A DummyUtxoSet for mocking up the chain
pub struct DummyUtxoSet {
outputs : HashMap<Commitment, transaction::Output>
outputs: HashMap<Commitment, transaction::Output>,
}
#[allow(dead_code)]
impl DummyUtxoSet {
pub fn empty() -> DummyUtxoSet{
DummyUtxoSet{outputs: HashMap::new()}
}
pub fn root(&self) -> hash::Hash {
hash::ZERO_HASH
}
pub fn apply(&self, b: &block::Block) -> DummyUtxoSet {
let mut new_hashmap = self.outputs.clone();
for input in &b.inputs {
new_hashmap.remove(&input.commitment());
}
for output in &b.outputs {
new_hashmap.insert(output.commitment(), output.clone());
}
DummyUtxoSet{outputs: new_hashmap}
}
pub fn with_block(&mut self, b: &block::Block) {
for input in &b.inputs {
self.outputs.remove(&input.commitment());
}
for output in &b.outputs {
self.outputs.insert(output.commitment(), output.clone());
}
}
pub fn rewind(&self, _: &block::Block) -> DummyUtxoSet {
DummyUtxoSet{outputs: HashMap::new()}
}
pub fn get_output(&self, output_ref: &Commitment) -> Option<&transaction::Output> {
self.outputs.get(output_ref)
}
pub fn empty() -> DummyUtxoSet {
DummyUtxoSet { outputs: HashMap::new() }
}
pub fn root(&self) -> hash::Hash {
hash::ZERO_HASH
}
pub fn apply(&self, b: &block::Block) -> DummyUtxoSet {
let mut new_hashmap = self.outputs.clone();
for input in &b.inputs {
new_hashmap.remove(&input.commitment());
}
for output in &b.outputs {
new_hashmap.insert(output.commitment(), output.clone());
}
DummyUtxoSet { outputs: new_hashmap }
}
pub fn with_block(&mut self, b: &block::Block) {
for input in &b.inputs {
self.outputs.remove(&input.commitment());
}
for output in &b.outputs {
self.outputs.insert(output.commitment(), output.clone());
}
}
pub fn rewind(&self, _: &block::Block) -> DummyUtxoSet {
DummyUtxoSet { outputs: HashMap::new() }
}
pub fn get_output(&self, output_ref: &Commitment) -> Option<&transaction::Output> {
self.outputs.get(output_ref)
}
fn clone(&self) -> DummyUtxoSet {
DummyUtxoSet{outputs: self.outputs.clone()}
}
fn clone(&self) -> DummyUtxoSet {
DummyUtxoSet { outputs: self.outputs.clone() }
}
// only for testing: add an output to the map
pub fn add_output(&mut self, output: transaction::Output) {
self.outputs.insert(output.commitment(), output);
}
// like above, but doesn't modify in-place so no mut ref needed
pub fn with_output(&self, output: transaction::Output) -> DummyUtxoSet {
let mut new_map = self.outputs.clone();
new_map.insert(output.commitment(), output);
DummyUtxoSet{outputs: new_map}
}
// only for testing: add an output to the map
pub fn add_output(&mut self, output: transaction::Output) {
self.outputs.insert(output.commitment(), output);
}
// like above, but doesn't modify in-place so no mut ref needed
pub fn with_output(&self, output: transaction::Output) -> DummyUtxoSet {
let mut new_map = self.outputs.clone();
new_map.insert(output.commitment(), output);
DummyUtxoSet { outputs: new_map }
}
}
/// A DummyChain is the mocked chain for playing with what methods we would
/// need
#[allow(dead_code)]
pub struct DummyChainImpl {
utxo: RwLock<DummyUtxoSet>,
block_headers: RwLock<DummyBlockHeaderIndex>,
head_header: RwLock<Vec<block::BlockHeader>>,
utxo: RwLock<DummyUtxoSet>,
block_headers: RwLock<DummyBlockHeaderIndex>,
head_header: RwLock<Vec<block::BlockHeader>>,
}
#[allow(dead_code)]
impl DummyChainImpl {
pub fn new() -> DummyChainImpl {
DummyChainImpl{
utxo: RwLock::new(DummyUtxoSet{outputs: HashMap::new()}),
block_headers: RwLock::new(DummyBlockHeaderIndex{block_headers: HashMap::new()}),
head_header: RwLock::new(vec![]),
}
}
pub fn new() -> DummyChainImpl {
DummyChainImpl {
utxo: RwLock::new(DummyUtxoSet { outputs: HashMap::new() }),
block_headers: RwLock::new(DummyBlockHeaderIndex { block_headers: HashMap::new() }),
head_header: RwLock::new(vec![]),
}
}
}
impl BlockChain for DummyChainImpl {
fn get_unspent(&self, commitment: &Commitment) -> Result<transaction::Output, PoolError> {
let output = self.utxo.read().unwrap().get_output(commitment).cloned();
match output {
Some(o) => Ok(o),
None => Err(PoolError::GenericPoolError),
}
}
fn get_unspent(&self, commitment: &Commitment) -> Result<transaction::Output, PoolError> {
let output = self.utxo.read().unwrap().get_output(commitment).cloned();
match output {
Some(o) => Ok(o),
None => Err(PoolError::GenericPoolError),
}
}
fn get_block_header_by_output_commit(&self, commit: &Commitment) -> Result<block::BlockHeader, PoolError> {
match self.block_headers.read().unwrap().get_block_header_by_output_commit(*commit) {
Ok(h) => Ok(h.clone()),
Err(e) => Err(e),
}
}
fn get_block_header_by_output_commit(
&self,
commit: &Commitment,
) -> Result<block::BlockHeader, PoolError> {
match self.block_headers
.read()
.unwrap()
.get_block_header_by_output_commit(*commit) {
Ok(h) => Ok(h.clone()),
Err(e) => Err(e),
}
}
fn head_header(&self) -> Result<block::BlockHeader, PoolError> {
let headers = self.head_header.read().unwrap();
if headers.len() > 0 {
Ok(headers[0].clone())
} else {
Err(PoolError::GenericPoolError)
}
}
fn head_header(&self) -> Result<block::BlockHeader, PoolError> {
let headers = self.head_header.read().unwrap();
if headers.len() > 0 {
Ok(headers[0].clone())
} else {
Err(PoolError::GenericPoolError)
}
}
}
impl DummyChain for DummyChainImpl {
fn update_utxo_set(&mut self, new_utxo: DummyUtxoSet) {
self.utxo = RwLock::new(new_utxo);
}
fn apply_block(&self, b: &block::Block) {
self.utxo.write().unwrap().with_block(b);
}
fn store_header_by_output_commitment(&self, commitment: Commitment, block_header: &block::BlockHeader) {
self.block_headers.write().unwrap().insert(commitment, block_header.clone());
}
fn store_head_header(&self, block_header: &block::BlockHeader) {
let mut h = self.head_header.write().unwrap();
h.clear();
h.insert(0, block_header.clone());
}
fn update_utxo_set(&mut self, new_utxo: DummyUtxoSet) {
self.utxo = RwLock::new(new_utxo);
}
fn apply_block(&self, b: &block::Block) {
self.utxo.write().unwrap().with_block(b);
}
fn store_header_by_output_commitment(
&self,
commitment: Commitment,
block_header: &block::BlockHeader,
) {
self.block_headers.write().unwrap().insert(
commitment,
block_header.clone(),
);
}
fn store_head_header(&self, block_header: &block::BlockHeader) {
let mut h = self.head_header.write().unwrap();
h.clear();
h.insert(0, block_header.clone());
}
}
pub trait DummyChain: BlockChain {
fn update_utxo_set(&mut self, new_utxo: DummyUtxoSet);
fn apply_block(&self, b: &block::Block);
fn store_header_by_output_commitment(&self, commitment: Commitment, block_header: &block::BlockHeader);
fn store_head_header(&self, block_header: &block::BlockHeader);
fn update_utxo_set(&mut self, new_utxo: DummyUtxoSet);
fn apply_block(&self, b: &block::Block);
fn store_header_by_output_commitment(
&self,
commitment: Commitment,
block_header: &block::BlockHeader,
);
fn store_head_header(&self, block_header: &block::BlockHeader);
}
+195 -162
View File
@@ -30,184 +30,210 @@ use core::core::hash::Hashed;
/// 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,
// Core data
/// Unique identifier of this pool entry and the corresponding transaction
pub transaction_hash: core::hash::Hash,
// Metadata
/// Size estimate
pub size_estimate: u64,
/// Receive timestamp
pub receive_ts: time::Tm,
// Metadata
/// Size estimate
pub size_estimate: u64,
/// Receive timestamp
pub receive_ts: time::Tm,
}
impl PoolEntry {
/// Create new transaction pool entry
pub fn new(tx: &core::transaction::Transaction) -> PoolEntry {
PoolEntry{
transaction_hash: transaction_identifier(tx),
size_estimate : estimate_transaction_size(tx),
receive_ts: time::now_utc()}
}
/// Create new transaction pool entry
pub fn new(tx: &core::transaction::Transaction) -> PoolEntry {
PoolEntry {
transaction_hash: transaction_identifier(tx),
size_estimate: estimate_transaction_size(tx),
receive_ts: time::now_utc(),
}
}
}
/// TODO guessing this needs implementing
fn estimate_transaction_size(_tx: &core::transaction::Transaction) -> u64 {
0
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<core::hash::Hash>,
destination: Option<core::hash::Hash>,
// Source and Destination are the vertex id's, the transaction (kernel)
// hash.
source: Option<core::hash::Hash>,
destination: Option<core::hash::Hash>,
// Output is the output hash which this input/output pairing corresponds
// to.
output: Commitment,
// Output is the output hash which this input/output pairing corresponds
// to.
output: Commitment,
}
impl Edge{
/// Create new edge
pub fn new(source: Option<core::hash::Hash>, destination: Option<core::hash::Hash>, output: Commitment) -> Edge {
Edge{source: source, destination: destination, output: output}
}
impl Edge {
/// Create new edge
pub fn new(
source: Option<core::hash::Hash>,
destination: Option<core::hash::Hash>,
output: Commitment,
) -> Edge {
Edge {
source: source,
destination: destination,
output: output,
}
}
/// Create new edge with a source
pub fn with_source(&self, src: Option<core::hash::Hash>) -> Edge {
Edge{source: src, destination: self.destination, output: self.output}
}
/// Create new edge with a source
pub fn with_source(&self, src: Option<core::hash::Hash>) -> Edge {
Edge {
source: src,
destination: self.destination,
output: self.output,
}
}
/// Create new edge with destination
pub fn with_destination(&self, dst: Option<core::hash::Hash>) -> Edge {
Edge{source: self.source, destination: dst, output: self.output}
}
/// Create new edge with destination
pub fn with_destination(&self, dst: Option<core::hash::Hash>) -> Edge {
Edge {
source: self.source,
destination: dst,
output: self.output,
}
}
/// The output commitment of the edge
pub fn output_commitment(&self) -> Commitment {
self.output
}
/// The output commitment of the edge
pub fn output_commitment(&self) -> Commitment {
self.output
}
/// The destination hash of the edge
pub fn destination_hash(&self) -> Option<core::hash::Hash> {
self.destination
}
/// The destination hash of the edge
pub fn destination_hash(&self) -> Option<core::hash::Hash> {
self.destination
}
/// The source hash of the edge
pub fn source_hash(&self) -> Option<core::hash::Hash> {
self.source
}
/// The source hash of the edge
pub fn source_hash(&self) -> Option<core::hash::Hash> {
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)
}
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<Commitment, Edge>,
vertices: Vec<PoolEntry>,
edges: HashMap<Commitment, Edge>,
vertices: Vec<PoolEntry>,
// 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<PoolEntry>,
// 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<PoolEntry>,
}
impl DirectedGraph {
/// Create an empty directed graph
pub fn empty() -> DirectedGraph {
DirectedGraph{
edges: HashMap::new(),
vertices: Vec::new(),
roots: Vec::new(),
}
}
/// Create an empty directed graph
pub fn empty() -> DirectedGraph {
DirectedGraph {
edges: HashMap::new(),
vertices: Vec::new(),
roots: Vec::new(),
}
}
/// Get an edge by its commitment
pub fn get_edge_by_commitment(&self, output_commitment: &Commitment) -> Option<&Edge> {
self.edges.get(output_commitment)
}
/// Get an edge by its commitment
pub fn get_edge_by_commitment(&self, output_commitment: &Commitment) -> Option<&Edge> {
self.edges.get(output_commitment)
}
/// Remove an edge by its commitment
pub fn remove_edge_by_commitment(&mut self, output_commitment: &Commitment) -> Option<Edge> {
self.edges.remove(output_commitment)
}
/// Remove an edge by its commitment
pub fn remove_edge_by_commitment(&mut self, output_commitment: &Commitment) -> Option<Edge> {
self.edges.remove(output_commitment)
}
/// Remove a vertex by its hash
pub fn remove_vertex(&mut self, tx_hash: core::hash::Hash) -> Option<PoolEntry> {
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,
}
}
}
}
/// Remove a vertex by its hash
pub fn remove_vertex(&mut self, tx_hash: core::hash::Hash) -> Option<PoolEntry> {
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<Edge>) {
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);
}
}
}
/// 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<Edge>) {
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);
}
}
/// 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);
}
}
/// add_edge_only adds an edge
pub fn add_edge_only(&mut self, edge: Edge) {
self.edges.insert(edge.output_commitment(), edge);
}
/// add_edge_only adds an edge
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 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 root vertices only
pub fn len_roots(&self) -> usize {
self.roots.len()
}
/// Number of edges
pub fn len_edges(&self) -> usize {
self.edges.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<core::hash::Hash> {
self.roots.iter().map(|x| x.transaction_hash).collect()
}
/// Get the current list of roots
pub fn get_roots(&self) -> Vec<core::hash::Hash> {
self.roots.iter().map(|x| x.transaction_hash).collect()
}
}
/// Using transaction merkle_inputs_outputs to calculate a deterministic hash;
@@ -215,50 +241,57 @@ impl DirectedGraph {
/// 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)
tx.hash()
// core::transaction::merkle_inputs_outputs(&tx.inputs, &tx.outputs)
tx.hash()
}
#[cfg(test)]
mod tests {
use super::*;
use secp::{Secp256k1, ContextFlag};
use secp::key;
use super::*;
use secp::{Secp256k1, ContextFlag};
use secp::key;
#[test]
fn test_add_entry() {
let ec = Secp256k1::with_caps(ContextFlag::Commit);
#[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, ec.nonce())}];
let test_transaction = core::transaction::Transaction::new(inputs,
outputs, 5);
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, ec.nonce()),
},
];
let test_transaction = core::transaction::Transaction::new(inputs, outputs, 5);
let test_pool_entry = PoolEntry::new(&test_transaction);
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 incoming_edge_1 = Edge::new(
Some(random_hash()),
Some(core::hash::ZERO_HASH),
output_commit,
);
let mut test_graph = DirectedGraph::empty();
let mut test_graph = DirectedGraph::empty();
test_graph.add_entry(test_pool_entry, vec![incoming_edge_1]);
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);
}
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
pub fn random_hash() -> core::hash::Hash {
let hash_bytes: [u8;32]= rand::random();
core::hash::Hash(hash_bytes)
let hash_bytes: [u8; 32] = rand::random();
core::hash::Hash(hash_bytes)
}
+915 -795
View File
File diff suppressed because it is too large Load Diff
+320 -278
View File
@@ -37,90 +37,93 @@ use core::core::hash;
/// 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,
/// 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{output: transaction::Output},
PoolTransaction{tx_ref: hash::Hash},
AlreadySpent{other_tx: hash::Hash},
Unknown,
BlockTransaction { output: transaction::Output },
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{output: _} => 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),
}
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Parent::Unknown => write!(f, "Parent: Unknown"),
&Parent::BlockTransaction { output: _ } => 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),
}
}
}
// TODO document this enum more accurately
/// Enum of errors
#[derive(Debug)]
pub enum PoolError {
/// An invalid pool entry
Invalid,
/// An entry already in the pool
AlreadyInPool,
/// A duplicate output
DuplicateOutput{
/// The other transaction
other_tx: Option<hash::Hash>,
/// Is in chain?
in_chain: bool,
/// The output
output: Commitment
},
/// A double spend
DoubleSpend{
/// The other transaction
other_tx: hash::Hash,
/// The spent output
spent_output: Commitment
},
/// Attempt to spend a coinbase output before it matures (1000 blocks?)
ImmatureCoinbase{
/// The block header of the block containing the coinbase output
header: block::BlockHeader,
/// The unspent coinbase output
output: Commitment,
},
/// An orphan successfully added to the orphans set
OrphanTransaction,
/// TODO - wip, just getting imports working, remove this and use more specific errors
GenericPoolError,
/// TODO - is this the right level of abstraction for pool errors?
OutputNotFound,
/// TODO - is this the right level of abstraction for pool errors?
OutputSpent,
/// An invalid pool entry
Invalid,
/// An entry already in the pool
AlreadyInPool,
/// A duplicate output
DuplicateOutput {
/// The other transaction
other_tx: Option<hash::Hash>,
/// Is in chain?
in_chain: bool,
/// The output
output: Commitment,
},
/// A double spend
DoubleSpend {
/// The other transaction
other_tx: hash::Hash,
/// The spent output
spent_output: Commitment,
},
/// Attempt to spend a coinbase output before it matures (1000 blocks?)
ImmatureCoinbase {
/// The block header of the block containing the coinbase output
header: block::BlockHeader,
/// The unspent coinbase output
output: Commitment,
},
/// An orphan successfully added to the orphans set
OrphanTransaction,
/// TODO - wip, just getting imports working, remove this and use more
/// specific errors
GenericPoolError,
/// TODO - is this the right level of abstraction for pool errors?
OutputNotFound,
/// TODO - is this the right level of abstraction for pool errors?
OutputSpent,
}
/// Interface that the pool requires from a blockchain implementation.
pub trait BlockChain {
/// Get an unspent output by its commitment. Will return None if the output
/// is spent or if it doesn't exist. The blockchain is expected to produce
/// a result with its current view of the most worked chain, ignoring
/// orphans, etc.
fn get_unspent(&self, output_ref: &Commitment)
-> Result<transaction::Output, PoolError>;
/// Get an unspent output by its commitment. Will return None if the output
/// is spent or if it doesn't exist. The blockchain is expected to produce
/// a result with its current view of the most worked chain, ignoring
/// orphans, etc.
fn get_unspent(&self, output_ref: &Commitment) -> Result<transaction::Output, PoolError>;
/// Get the block header by output commitment (needed for spending coinbase after n blocks)
fn get_block_header_by_output_commit(&self, commit: &Commitment)
-> Result<block::BlockHeader, PoolError>;
/// Get the block header by output commitment (needed for spending coinbase
/// after n blocks)
fn get_block_header_by_output_commit(
&self,
commit: &Commitment,
) -> Result<block::BlockHeader, PoolError>;
/// Get the block header at the head
fn head_header(&self) -> Result<block::BlockHeader, PoolError>;
/// Get the block header at the head
fn head_header(&self) -> Result<block::BlockHeader, PoolError>;
}
/// Pool contains the elements of the graph that are connected, in full, to
@@ -135,230 +138,270 @@ pub trait BlockChain {
/// connections are in the pool edge set, while unspent (dangling) references
/// exist in the available_outputs set.
pub struct Pool {
graph : graph::DirectedGraph,
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<Commitment, graph::Edge>,
// 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<Commitment, graph::Edge>,
// Consumed blockchain utxo's are kept in a separate map.
consumed_blockchain_outputs: HashMap<Commitment, graph::Edge>
// Consumed blockchain utxo's are kept in a separate map.
consumed_blockchain_outputs: HashMap<Commitment, graph::Edge>,
}
impl Pool {
pub fn empty() -> Pool {
Pool{
graph: graph::DirectedGraph::empty(),
available_outputs: HashMap::new(),
consumed_blockchain_outputs: HashMap::new(),
}
}
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<hash::Hash> {
self.graph.get_edge_by_commitment(&o.commitment()).or(self.consumed_blockchain_outputs.get(&o.commitment())).map(|x| x.destination_hash().unwrap())
}
/// 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<hash::Hash> {
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 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<graph::Edge>, pool_refs: Vec<graph::Edge>,
mut new_unspents: Vec<graph::Edge>) {
pub fn add_pool_transaction(
&mut self,
pool_entry: graph::PoolEntry,
mut blockchain_refs: Vec<graph::Edge>,
pool_refs: Vec<graph::Edge>,
mut new_unspents: Vec<graph::Edge>,
) {
// 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());
}
// 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);
}
// 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 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);
}
}
// 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<hash::Hash, ()>) {
pub fn remove_pool_transaction(
&mut self,
tx: &transaction::Transaction,
marked_txs: &HashMap<hash::Hash, ()>,
) {
self.graph.remove_vertex(graph::transaction_identifier(tx));
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 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()) {
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);
}
};
}
}
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<hash::Hash> {
let mut roots = self.graph.get_roots();
roots.truncate(num_to_fetch as usize);
roots
}
/// Simplest possible implementation: just return the roots
pub fn get_mineable_transactions(&self, num_to_fetch: u32) -> Vec<hash::Hash> {
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)
}
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,
graph: graph::DirectedGraph,
// available_outputs are unspent outputs of the current orphan set,
// maintained as edges with empty destinations.
available_outputs: HashMap<Commitment, graph::Edge>,
// available_outputs are unspent outputs of the current orphan set,
// maintained as edges with empty destinations.
available_outputs: HashMap<Commitment, graph::Edge>,
// missing_outputs are spending references (inputs) with missing
// corresponding outputs, maintained as edges with empty sources.
missing_outputs: HashMap<Commitment, graph::Edge>,
// missing_outputs are spending references (inputs) with missing
// corresponding outputs, maintained as edges with empty sources.
missing_outputs: HashMap<Commitment, graph::Edge>,
// 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<Commitment, graph::Edge>,
// 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<Commitment, graph::Edge>,
}
impl Orphans {
pub fn empty() -> Orphans {
Orphans{
graph: graph::DirectedGraph::empty(),
available_outputs : HashMap::new(),
missing_outputs: HashMap::new(),
pool_connections: HashMap::new(),
}
}
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.
pub fn check_double_spend(&self, o: transaction::Output) -> Option<hash::Hash> {
self.graph.get_edge_by_commitment(&o.commitment()).or(self.pool_connections.get(&o.commitment())).map(|x| x.destination_hash().unwrap())
}
/// 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.
pub fn check_double_spend(&self, o: transaction::Output) -> Option<hash::Hash> {
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)
}
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<graph::Edge>, mut orphan_refs: Vec<graph::Edge>,
is_missing: HashMap<usize, ()>, mut new_unspents: Vec<graph::Edge>) {
/// 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<graph::Edge>,
mut orphan_refs: Vec<graph::Edge>,
is_missing: HashMap<usize, ()>,
mut new_unspents: Vec<graph::Edge>,
) {
// 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);
}
}
// 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);
}
// 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());
// 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);
}
}
// 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)
}
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.
@@ -382,44 +425,43 @@ impl TransactionGraphContainer for Orphans {
/// 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>;
/// 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 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<hash::Hash> {
self.get_available_output(c).
or(self.get_internal_spent_output(c)).
map(|x| x.source_hash().unwrap())
}
/// Checks if the pool has anything by this output already, between
/// available outputs and internal ones.
fn find_output(&self, c: &Commitment) -> Option<hash::Hash> {
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)
}
/// 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_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()
}
fn num_transactions(&self) -> usize {
self.get_graph().len_vertices()
}
fn num_output_edges(&self) -> usize {
self.get_graph().len_edges()
}
}