use std::collections::HashMap;
use crate::{
block::{self, Block, Height},
transaction::{self, Transaction},
transparent,
};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary, serde::Serialize)
)]
pub struct Utxo {
pub output: transparent::Output,
pub height: block::Height,
pub from_coinbase: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub struct OrderedUtxo {
pub utxo: Utxo,
pub tx_index_in_block: usize,
}
impl AsRef<Utxo> for OrderedUtxo {
fn as_ref(&self) -> &Utxo {
&self.utxo
}
}
impl Utxo {
pub fn new(output: transparent::Output, height: block::Height, from_coinbase: bool) -> Utxo {
Utxo {
output,
height,
from_coinbase,
}
}
pub fn from_location(
output: transparent::Output,
height: block::Height,
tx_index_in_block: usize,
) -> Utxo {
let from_coinbase = tx_index_in_block == 0;
Utxo {
output,
height,
from_coinbase,
}
}
}
impl OrderedUtxo {
pub fn new(
output: transparent::Output,
height: block::Height,
tx_index_in_block: usize,
) -> OrderedUtxo {
let from_coinbase = tx_index_in_block == 0;
OrderedUtxo {
utxo: Utxo::new(output, height, from_coinbase),
tx_index_in_block,
}
}
pub fn from_utxo(utxo: Utxo, tx_index_in_block: usize) -> OrderedUtxo {
OrderedUtxo {
utxo,
tx_index_in_block,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub enum CoinbaseSpendRestriction {
DisallowCoinbaseSpend,
CheckCoinbaseMaturity {
spend_height: block::Height,
},
}
pub fn utxos_from_ordered_utxos(
ordered_utxos: HashMap<transparent::OutPoint, OrderedUtxo>,
) -> HashMap<transparent::OutPoint, Utxo> {
ordered_utxos
.into_iter()
.map(|(outpoint, ordered_utxo)| (outpoint, ordered_utxo.utxo))
.collect()
}
pub fn outputs_from_utxos(
utxos: HashMap<transparent::OutPoint, Utxo>,
) -> HashMap<transparent::OutPoint, transparent::Output> {
utxos
.into_iter()
.map(|(outpoint, utxo)| (outpoint, utxo.output))
.collect()
}
pub fn new_outputs(
block: &Block,
transaction_hashes: &[transaction::Hash],
) -> HashMap<transparent::OutPoint, Utxo> {
utxos_from_ordered_utxos(new_ordered_outputs(block, transaction_hashes))
}
#[cfg(any(test, feature = "proptest-impl"))]
pub fn new_outputs_with_height(
block: &Block,
height: Height,
transaction_hashes: &[transaction::Hash],
) -> HashMap<transparent::OutPoint, Utxo> {
utxos_from_ordered_utxos(new_ordered_outputs_with_height(
block,
height,
transaction_hashes,
))
}
pub fn new_ordered_outputs(
block: &Block,
transaction_hashes: &[transaction::Hash],
) -> HashMap<transparent::OutPoint, OrderedUtxo> {
let height = block.coinbase_height().expect("block has coinbase height");
new_ordered_outputs_with_height(block, height, transaction_hashes)
}
pub fn new_ordered_outputs_with_height(
block: &Block,
height: Height,
transaction_hashes: &[transaction::Hash],
) -> HashMap<transparent::OutPoint, OrderedUtxo> {
let mut new_ordered_outputs = HashMap::new();
for (tx_index_in_block, (transaction, hash)) in block
.transactions
.iter()
.zip(transaction_hashes.iter().cloned())
.enumerate()
{
new_ordered_outputs.extend(new_transaction_ordered_outputs(
transaction,
hash,
tx_index_in_block,
height,
));
}
new_ordered_outputs
}
pub fn new_transaction_ordered_outputs(
transaction: &Transaction,
hash: transaction::Hash,
tx_index_in_block: usize,
height: block::Height,
) -> HashMap<transparent::OutPoint, OrderedUtxo> {
let mut new_ordered_outputs = HashMap::new();
for (output_index_in_transaction, output) in transaction.outputs().iter().cloned().enumerate() {
let output_index_in_transaction = output_index_in_transaction
.try_into()
.expect("unexpectedly large number of outputs");
new_ordered_outputs.insert(
transparent::OutPoint {
hash,
index: output_index_in_transaction,
},
OrderedUtxo::new(output, height, tx_index_in_block),
);
}
new_ordered_outputs
}