Cargo fmt all the things
This commit is contained in:
+123
-103
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+320
-278
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user