1use std::collections::HashMap;
4
5use crate::{
6 block::{self, Block, Height},
7 transaction::{self, Transaction},
8 transparent,
9};
10
11#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13#[cfg_attr(
14 any(test, feature = "proptest-impl"),
15 derive(proptest_derive::Arbitrary, serde::Serialize)
16)]
17pub struct Utxo {
18 pub output: transparent::Output,
20
21 pub height: block::Height,
26 pub from_coinbase: bool,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
38#[cfg_attr(
39 any(test, feature = "proptest-impl"),
40 derive(proptest_derive::Arbitrary)
41)]
42pub struct OrderedUtxo {
45 pub utxo: Utxo,
47 pub tx_index_in_block: usize,
55}
56
57impl AsRef<Utxo> for OrderedUtxo {
58 fn as_ref(&self) -> &Utxo {
59 &self.utxo
60 }
61}
62
63impl Utxo {
64 pub fn new(output: transparent::Output, height: block::Height, from_coinbase: bool) -> Utxo {
66 Utxo {
67 output,
68 height,
69 from_coinbase,
70 }
71 }
72
73 pub fn from_location(
75 output: transparent::Output,
76 height: block::Height,
77 tx_index_in_block: usize,
78 ) -> Utxo {
79 let from_coinbase = tx_index_in_block == 0;
82
83 Utxo {
84 output,
85 height,
86 from_coinbase,
87 }
88 }
89}
90
91impl OrderedUtxo {
92 pub fn new(
94 output: transparent::Output,
95 height: block::Height,
96 tx_index_in_block: usize,
97 ) -> OrderedUtxo {
98 let from_coinbase = tx_index_in_block == 0;
101
102 OrderedUtxo {
103 utxo: Utxo::new(output, height, from_coinbase),
104 tx_index_in_block,
105 }
106 }
107
108 pub fn from_utxo(utxo: Utxo, tx_index_in_block: usize) -> OrderedUtxo {
110 OrderedUtxo {
111 utxo,
112 tx_index_in_block,
113 }
114 }
115}
116
117#[derive(Copy, Clone, Debug, PartialEq, Eq)]
123#[cfg_attr(
124 any(test, feature = "proptest-impl"),
125 derive(proptest_derive::Arbitrary)
126)]
127pub enum CoinbaseSpendRestriction {
128 DisallowCoinbaseSpend,
132
133 CheckCoinbaseMaturity {
137 spend_height: block::Height,
139 },
140}
141
142pub fn utxos_from_ordered_utxos(
144 ordered_utxos: HashMap<transparent::OutPoint, OrderedUtxo>,
145) -> HashMap<transparent::OutPoint, Utxo> {
146 ordered_utxos
147 .into_iter()
148 .map(|(outpoint, ordered_utxo)| (outpoint, ordered_utxo.utxo))
149 .collect()
150}
151
152pub fn outputs_from_utxos(
154 utxos: HashMap<transparent::OutPoint, Utxo>,
155) -> HashMap<transparent::OutPoint, transparent::Output> {
156 utxos
157 .into_iter()
158 .map(|(outpoint, utxo)| (outpoint, utxo.output))
159 .collect()
160}
161
162pub fn new_outputs(
165 block: &Block,
166 transaction_hashes: &[transaction::Hash],
167) -> HashMap<transparent::OutPoint, Utxo> {
168 utxos_from_ordered_utxos(new_ordered_outputs(block, transaction_hashes))
169}
170
171#[cfg(any(test, feature = "proptest-impl"))]
176pub fn new_outputs_with_height(
177 block: &Block,
178 height: Height,
179 transaction_hashes: &[transaction::Hash],
180) -> HashMap<transparent::OutPoint, Utxo> {
181 utxos_from_ordered_utxos(new_ordered_outputs_with_height(
182 block,
183 height,
184 transaction_hashes,
185 ))
186}
187
188pub fn new_ordered_outputs(
191 block: &Block,
192 transaction_hashes: &[transaction::Hash],
193) -> HashMap<transparent::OutPoint, OrderedUtxo> {
194 let height = block.coinbase_height().expect("block has coinbase height");
195
196 new_ordered_outputs_with_height(block, height, transaction_hashes)
197}
198
199pub fn new_ordered_outputs_with_height(
205 block: &Block,
206 height: Height,
207 transaction_hashes: &[transaction::Hash],
208) -> HashMap<transparent::OutPoint, OrderedUtxo> {
209 let mut new_ordered_outputs = HashMap::new();
210
211 for (tx_index_in_block, (transaction, hash)) in block
212 .transactions
213 .iter()
214 .zip(transaction_hashes.iter().cloned())
215 .enumerate()
216 {
217 new_ordered_outputs.extend(new_transaction_ordered_outputs(
218 transaction,
219 hash,
220 tx_index_in_block,
221 height,
222 ));
223 }
224
225 new_ordered_outputs
226}
227
228pub fn new_transaction_ordered_outputs(
234 transaction: &Transaction,
235 hash: transaction::Hash,
236 tx_index_in_block: usize,
237 height: block::Height,
238) -> HashMap<transparent::OutPoint, OrderedUtxo> {
239 let mut new_ordered_outputs = HashMap::new();
240
241 for (output_index_in_transaction, output) in transaction.outputs().iter().cloned().enumerate() {
242 let output_index_in_transaction = output_index_in_transaction
243 .try_into()
244 .expect("unexpectedly large number of outputs");
245
246 new_ordered_outputs.insert(
247 transparent::OutPoint {
248 hash,
249 index: output_index_in_transaction,
250 },
251 OrderedUtxo::new(output, height, tx_index_in_block),
252 );
253 }
254
255 new_ordered_outputs
256}