2020-08-15 21:55:28 -07:00
|
|
|
//! Transparent-related (Bitcoin-inherited) functionality.
|
2020-08-15 18:02:07 -07:00
|
|
|
|
2023-02-22 16:10:11 -08:00
|
|
|
use std::{collections::HashMap, fmt, iter};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
amount::{Amount, NonNegative},
|
|
|
|
block,
|
|
|
|
parameters::Network,
|
|
|
|
primitives::zcash_primitives,
|
|
|
|
transaction,
|
|
|
|
};
|
|
|
|
|
2020-08-15 20:51:03 -07:00
|
|
|
mod address;
|
|
|
|
mod keys;
|
2022-12-08 21:17:55 -08:00
|
|
|
mod opcodes;
|
2020-08-17 01:24:33 -07:00
|
|
|
mod script;
|
|
|
|
mod serialize;
|
2021-07-11 19:49:33 -07:00
|
|
|
mod utxo;
|
2020-08-15 20:51:03 -07:00
|
|
|
|
2020-08-17 02:28:55 -07:00
|
|
|
pub use address::Address;
|
2020-08-17 01:24:33 -07:00
|
|
|
pub use script::Script;
|
2023-02-22 16:10:11 -08:00
|
|
|
pub use serialize::{GENESIS_COINBASE_DATA, MAX_COINBASE_DATA_LEN, MAX_COINBASE_HEIGHT_DATA_LEN};
|
2021-07-28 21:23:50 -07:00
|
|
|
pub use utxo::{
|
2022-03-09 15:34:50 -08:00
|
|
|
new_ordered_outputs, new_outputs, outputs_from_utxos, utxos_from_ordered_utxos,
|
|
|
|
CoinbaseSpendRestriction, OrderedUtxo, Utxo,
|
2021-07-28 21:23:50 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
2022-03-09 15:34:50 -08:00
|
|
|
pub use utxo::{
|
|
|
|
new_ordered_outputs_with_height, new_outputs_with_height, new_transaction_ordered_outputs,
|
|
|
|
};
|
2020-08-17 01:24:33 -07:00
|
|
|
|
2020-10-02 15:51:51 -07:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
mod arbitrary;
|
2022-03-09 15:34:50 -08:00
|
|
|
|
2020-10-02 15:51:51 -07:00
|
|
|
#[cfg(test)]
|
2021-12-05 14:38:02 -08:00
|
|
|
mod tests;
|
2020-10-02 15:51:51 -07:00
|
|
|
|
2023-02-22 16:10:11 -08:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
use proptest_derive::Arbitrary;
|
2021-07-28 20:49:36 -07:00
|
|
|
|
2021-08-12 05:38:16 -07:00
|
|
|
/// The maturity threshold for transparent coinbase outputs.
|
|
|
|
///
|
|
|
|
/// "A transaction MUST NOT spend a transparent output of a coinbase transaction
|
|
|
|
/// from a block less than 100 blocks prior to the spend. Note that transparent
|
|
|
|
/// outputs of coinbase transactions include Founders' Reward outputs and
|
|
|
|
/// transparent Funding Stream outputs."
|
|
|
|
/// [7.1](https://zips.z.cash/protocol/nu5.pdf#txnencodingandconsensus)
|
2023-04-26 16:35:53 -07:00
|
|
|
//
|
|
|
|
// TODO: change type to HeightDiff
|
2021-08-12 05:38:16 -07:00
|
|
|
pub const MIN_TRANSPARENT_COINBASE_MATURITY: u32 = 100;
|
|
|
|
|
2023-02-22 16:10:11 -08:00
|
|
|
/// Extra coinbase data that identifies some coinbase transactions generated by Zebra.
|
|
|
|
/// <https://emojipedia.org/zebra/>
|
|
|
|
//
|
|
|
|
// # Note
|
|
|
|
//
|
|
|
|
// rust-analyzer will crash in some editors when moving over an actual Zebra emoji,
|
|
|
|
// so we encode it here. This is a known issue in emacs-lsp and other lsp implementations:
|
|
|
|
// - https://github.com/rust-lang/rust-analyzer/issues/9121
|
|
|
|
// - https://github.com/emacs-lsp/lsp-mode/issues/2080
|
|
|
|
// - https://github.com/rust-lang/rust-analyzer/issues/13709
|
|
|
|
pub const EXTRA_ZEBRA_COINBASE_DATA: &str = "z\u{1F993}";
|
|
|
|
|
2020-08-17 01:24:33 -07:00
|
|
|
/// Arbitrary data inserted by miners into a coinbase transaction.
|
2023-02-22 16:10:11 -08:00
|
|
|
//
|
|
|
|
// TODO: rename to ExtraCoinbaseData, because height is also part of the coinbase data?
|
2022-03-18 13:30:16 -07:00
|
|
|
#[derive(Clone, Eq, PartialEq)]
|
2023-06-14 12:01:39 -07:00
|
|
|
#[cfg_attr(
|
|
|
|
any(test, feature = "proptest-impl", feature = "elasticsearch"),
|
|
|
|
derive(Serialize)
|
|
|
|
)]
|
2020-08-17 01:24:33 -07:00
|
|
|
pub struct CoinbaseData(
|
|
|
|
/// Invariant: this vec, together with the coinbase height, must be less than
|
|
|
|
/// 100 bytes. We enforce this by only constructing CoinbaseData fields by
|
2023-02-22 16:10:11 -08:00
|
|
|
/// parsing blocks with 100-byte data fields, and checking newly created
|
|
|
|
/// CoinbaseData lengths in the transaction builder.
|
2020-08-17 01:24:33 -07:00
|
|
|
pub(super) Vec<u8>,
|
|
|
|
);
|
|
|
|
|
2021-06-28 17:49:40 -07:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
impl CoinbaseData {
|
|
|
|
/// Create a new `CoinbaseData` containing `data`.
|
|
|
|
///
|
|
|
|
/// Only for use in tests.
|
|
|
|
pub fn new(data: Vec<u8>) -> CoinbaseData {
|
|
|
|
CoinbaseData(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-17 01:24:33 -07:00
|
|
|
impl AsRef<[u8]> for CoinbaseData {
|
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
self.0.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-19 17:32:27 -08:00
|
|
|
impl std::fmt::Debug for CoinbaseData {
|
2022-06-27 23:22:07 -07:00
|
|
|
#[allow(clippy::unwrap_in_result)]
|
2020-11-19 17:32:27 -08:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
let escaped = String::from_utf8(
|
|
|
|
self.0
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.flat_map(std::ascii::escape_default)
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
.expect("ascii::escape_default produces utf8");
|
|
|
|
f.debug_tuple("CoinbaseData").field(&escaped).finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-17 01:24:33 -07:00
|
|
|
/// OutPoint
|
|
|
|
///
|
|
|
|
/// A particular transaction output reference.
|
2022-03-18 13:30:16 -07:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
2023-06-14 12:01:39 -07:00
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
|
|
|
#[cfg_attr(
|
|
|
|
any(test, feature = "proptest-impl", feature = "elasticsearch"),
|
|
|
|
derive(Serialize)
|
|
|
|
)]
|
2020-08-17 01:24:33 -07:00
|
|
|
pub struct OutPoint {
|
|
|
|
/// References the transaction that contains the UTXO being spent.
|
2022-03-18 13:30:16 -07:00
|
|
|
///
|
|
|
|
/// # Correctness
|
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// Consensus-critical serialization uses
|
|
|
|
/// [`ZcashSerialize`](crate::serialization::ZcashSerialize).
|
2022-03-18 13:30:16 -07:00
|
|
|
/// [`serde`]-based hex serialization must only be used for testing.
|
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), serde(with = "hex"))]
|
2020-08-17 02:26:33 -07:00
|
|
|
pub hash: transaction::Hash,
|
2020-08-17 01:24:33 -07:00
|
|
|
|
|
|
|
/// Identifies which UTXO from that transaction is referenced; the
|
|
|
|
/// first output is 0, etc.
|
|
|
|
pub index: u32,
|
|
|
|
}
|
|
|
|
|
2022-03-18 13:30:16 -07:00
|
|
|
impl OutPoint {
|
2022-06-02 08:07:35 -07:00
|
|
|
/// Returns a new [`OutPoint`] from an in-memory output `index`.
|
2022-03-18 13:30:16 -07:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// If `index` doesn't fit in a [`u32`].
|
|
|
|
pub fn from_usize(hash: transaction::Hash, index: usize) -> OutPoint {
|
|
|
|
OutPoint {
|
|
|
|
hash,
|
|
|
|
index: index
|
|
|
|
.try_into()
|
|
|
|
.expect("valid in-memory output indexes fit in a u32"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-17 01:24:33 -07:00
|
|
|
/// A transparent input to a transaction.
|
2022-03-18 13:30:16 -07:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
2023-06-14 12:01:39 -07:00
|
|
|
#[cfg_attr(
|
|
|
|
any(test, feature = "proptest-impl", feature = "elasticsearch"),
|
|
|
|
derive(Serialize)
|
|
|
|
)]
|
2020-08-17 01:24:33 -07:00
|
|
|
pub enum Input {
|
|
|
|
/// A reference to an output of a previous transaction.
|
|
|
|
PrevOut {
|
|
|
|
/// The previous output transaction reference.
|
|
|
|
outpoint: OutPoint,
|
|
|
|
/// The script that authorizes spending `outpoint`.
|
|
|
|
unlock_script: Script,
|
|
|
|
/// The sequence number for the output.
|
|
|
|
sequence: u32,
|
|
|
|
},
|
|
|
|
/// New coins created by the block reward.
|
|
|
|
Coinbase {
|
|
|
|
/// The height of this block.
|
|
|
|
height: block::Height,
|
|
|
|
/// Free data inserted by miners after the block height.
|
|
|
|
data: CoinbaseData,
|
|
|
|
/// The sequence number for the output.
|
|
|
|
sequence: u32,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
Refactor mempool spend conflict checks to increase performance (#2826)
* Add `HashSet`s to help spend conflict detection
Keep track of the spent transparent outpoints and the revealed
nullifiers.
Clippy complained that the `ActiveState` had variants with large size
differences, but that was expected, so I disabled that lint on that
`enum`.
* Clear the `HashSet`s when clearing the mempool
Clear them so that they remain consistent with the set of verified
transactions.
* Use `HashSet`s to check for spend conflicts
Store new outputs into its respective `HashSet`, and abort if a
duplicate output is found.
* Remove inserted outputs when aborting
Restore the `HashSet` to its previous state.
* Remove tracked outputs when removing a transaction
Keep the mempool storage in a consistent state when a transaction is
removed.
* Remove tracked outputs when evicting from mempool
Ensure eviction also keeps the tracked outputs consistent with the
verified transactions.
* Refactor to create a `VerifiedSet` helper type
Move the code to handle the output caches into the new type. Also move
the eviction code to make things a little simpler.
* Refactor to have a single `remove` method
Centralize the code that handles the removal of a transaction to avoid
mistakes.
* Move mempool size limiting back to `Storage`
Because the evicted transactions must be added to the rejected list.
* Remove leftover `dbg!` statement
Leftover from some temporary testing code.
Co-authored-by: teor <teor@riseup.net>
* Remove unnecessary `TODO`
It is more speculation than planning, so it doesn't add much value.
Co-authored-by: teor <teor@riseup.net>
* Fix typo in documentation
The verb should match the subject "transactions" which is plural.
Co-authored-by: teor <teor@riseup.net>
* Add a comment to warn about correctness
There's a subtle but important detail in the implementation that should
be made more visible to avoid mistakes in the future.
Co-authored-by: teor <teor@riseup.net>
* Remove outdated comment
Left-over from the attempt to move the eviction into the `VerifiedSet`.
* Improve comment explaining lint removal
Rewrite the comment explaining why the Clippy lint was ignored.
* Check for spend conflicts in `VerifiedSet`
Refactor to avoid API misuse.
* Test rejected transaction rollback
Using two transactions, perform the same test adding a conflict to both
of them to check if the second inserted transaction is properly
rejected. Then remove any conflicts from the second transaction and add
it again. That should work, because if it doesn't it means that when the
second transaction was rejected it left things it shouldn't in the
cache.
* Test removal of multiple transactions
When removing multiple transactions from the mempool storage, all of the
ones requested should be removed and any other transaction should be
still be there afterwards.
* Increase mempool size to 4, so that spend conflict tests work
If the mempool size is smaller than 4,
these tests don't fail on a trivial removal bug.
Because we need a minimum number of transactions in the mempool
to trigger the bug.
Also commit a proptest seed that fails on a trivial removal bug.
(This seed fails if we remove indexes in order,
because every index past the first removes the wrong transaction.)
* Summarise transaction data in proptest error output
* Summarise spend conflict field data in proptest error output
* Summarise multiple removal field data in proptest error output
And replace the very large proptest debug output with the new summary.
Co-authored-by: teor <teor@riseup.net>
2021-10-10 16:54:46 -07:00
|
|
|
impl fmt::Display for Input {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Input::PrevOut {
|
|
|
|
outpoint,
|
|
|
|
unlock_script,
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
let mut fmter = f.debug_struct("transparent::Input::PrevOut");
|
|
|
|
|
|
|
|
fmter.field("unlock_script_len", &unlock_script.as_raw_bytes().len());
|
|
|
|
fmter.field("outpoint", outpoint);
|
|
|
|
|
|
|
|
fmter.finish()
|
|
|
|
}
|
|
|
|
Input::Coinbase { height, data, .. } => {
|
|
|
|
let mut fmter = f.debug_struct("transparent::Input::Coinbase");
|
|
|
|
|
|
|
|
fmter.field("height", height);
|
|
|
|
fmter.field("data_len", &data.0.len());
|
|
|
|
|
|
|
|
fmter.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 16:40:15 -07:00
|
|
|
impl Input {
|
2022-11-09 16:12:27 -08:00
|
|
|
/// Returns a new coinbase input for `height` with optional `data` and `sequence`.
|
2023-02-22 16:10:11 -08:00
|
|
|
///
|
|
|
|
/// # Consensus
|
|
|
|
///
|
|
|
|
/// The combined serialized size of `height` and `data` can be at most 100 bytes.
|
|
|
|
///
|
|
|
|
/// > A coinbase transaction script MUST have length in {2 .. 100} bytes.
|
|
|
|
///
|
|
|
|
/// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// If the coinbase data is greater than [`MAX_COINBASE_DATA_LEN`].
|
2022-11-09 16:12:27 -08:00
|
|
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
|
|
|
pub fn new_coinbase(
|
|
|
|
height: block::Height,
|
|
|
|
data: Option<Vec<u8>>,
|
|
|
|
sequence: Option<u32>,
|
|
|
|
) -> Input {
|
2023-02-22 16:10:11 -08:00
|
|
|
// "No extra coinbase data" is the default.
|
|
|
|
let data = data.unwrap_or_default();
|
|
|
|
let height_size = height.coinbase_zcash_serialized_size();
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
data.len() + height_size <= MAX_COINBASE_DATA_LEN,
|
|
|
|
"invalid coinbase data: extra data {} bytes + height {height_size} bytes \
|
|
|
|
must be {} or less",
|
|
|
|
data.len(),
|
|
|
|
MAX_COINBASE_DATA_LEN,
|
|
|
|
);
|
|
|
|
|
2022-11-09 16:12:27 -08:00
|
|
|
Input::Coinbase {
|
|
|
|
height,
|
2023-02-22 16:10:11 -08:00
|
|
|
data: CoinbaseData(data),
|
2022-11-09 16:12:27 -08:00
|
|
|
|
|
|
|
// If the caller does not specify the sequence number,
|
|
|
|
// use a sequence number that activates the LockTime.
|
|
|
|
sequence: sequence.unwrap_or(0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:10:11 -08:00
|
|
|
/// Returns the extra coinbase data in this input, if it is an [`Input::Coinbase`].
|
|
|
|
pub fn extra_coinbase_data(&self) -> Option<&CoinbaseData> {
|
|
|
|
match self {
|
|
|
|
Input::PrevOut { .. } => None,
|
|
|
|
Input::Coinbase { data, .. } => Some(data),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-22 21:53:53 -08:00
|
|
|
/// Returns the input's sequence number.
|
|
|
|
pub fn sequence(&self) -> u32 {
|
|
|
|
match self {
|
|
|
|
Input::PrevOut { sequence, .. } | Input::Coinbase { sequence, .. } => *sequence,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-19 15:44:12 -08:00
|
|
|
/// Sets the input's sequence number.
|
|
|
|
///
|
|
|
|
/// Only for use in tests.
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
pub fn set_sequence(&mut self, new_sequence: u32) {
|
|
|
|
match self {
|
|
|
|
Input::PrevOut { sequence, .. } | Input::Coinbase { sequence, .. } => {
|
|
|
|
*sequence = new_sequence
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-02 08:07:35 -07:00
|
|
|
/// If this is a [`Input::PrevOut`] input, returns this input's
|
|
|
|
/// [`OutPoint`]. Otherwise, returns `None`.
|
2021-07-22 16:40:15 -07:00
|
|
|
pub fn outpoint(&self) -> Option<OutPoint> {
|
|
|
|
if let Input::PrevOut { outpoint, .. } = self {
|
|
|
|
Some(*outpoint)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-02 08:07:35 -07:00
|
|
|
/// Set this input's [`OutPoint`].
|
2021-07-22 16:40:15 -07:00
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// Should only be called on [`Input::PrevOut`] inputs.
|
2021-07-22 16:40:15 -07:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// If `self` is a coinbase input.
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
pub fn set_outpoint(&mut self, new_outpoint: OutPoint) {
|
|
|
|
if let Input::PrevOut {
|
|
|
|
ref mut outpoint, ..
|
|
|
|
} = self
|
|
|
|
{
|
|
|
|
*outpoint = new_outpoint;
|
|
|
|
} else {
|
|
|
|
unreachable!("unexpected variant: Coinbase Inputs do not have OutPoints");
|
|
|
|
}
|
|
|
|
}
|
2021-07-28 20:49:36 -07:00
|
|
|
|
2022-06-02 08:07:35 -07:00
|
|
|
/// Get the value spent by this input, by looking up its [`OutPoint`] in `outputs`.
|
|
|
|
/// See [`Self::value`] for details.
|
2021-07-28 20:49:36 -07:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// If the provided [`Output`]s don't have this input's [`OutPoint`].
|
2021-08-09 10:22:26 -07:00
|
|
|
pub(crate) fn value_from_outputs(
|
|
|
|
&self,
|
|
|
|
outputs: &HashMap<OutPoint, Output>,
|
|
|
|
) -> Amount<NonNegative> {
|
2021-07-28 20:49:36 -07:00
|
|
|
match self {
|
2021-08-09 10:22:26 -07:00
|
|
|
Input::PrevOut { outpoint, .. } => {
|
|
|
|
outputs
|
|
|
|
.get(outpoint)
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
panic!(
|
|
|
|
"provided Outputs (length {:?}) don't have spent {:?}",
|
|
|
|
outputs.len(),
|
|
|
|
outpoint
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.value
|
|
|
|
}
|
2021-07-28 20:49:36 -07:00
|
|
|
Input::Coinbase { .. } => Amount::zero(),
|
|
|
|
}
|
|
|
|
}
|
2021-08-09 10:22:26 -07:00
|
|
|
|
2022-06-02 08:07:35 -07:00
|
|
|
/// Get the value spent by this input, by looking up its [`OutPoint`] in
|
|
|
|
/// [`Utxo`]s.
|
2021-08-09 10:22:26 -07:00
|
|
|
///
|
|
|
|
/// This amount is added to the transaction value pool by this input.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// If the provided [`Utxo`]s don't have this input's [`OutPoint`].
|
2021-08-09 10:22:26 -07:00
|
|
|
pub fn value(&self, utxos: &HashMap<OutPoint, utxo::Utxo>) -> Amount<NonNegative> {
|
|
|
|
if let Some(outpoint) = self.outpoint() {
|
|
|
|
// look up the specific Output and convert it to the expected format
|
|
|
|
let output = utxos
|
|
|
|
.get(&outpoint)
|
|
|
|
.expect("provided Utxos don't have spent OutPoint")
|
|
|
|
.output
|
|
|
|
.clone();
|
|
|
|
self.value_from_outputs(&iter::once((outpoint, output)).collect())
|
|
|
|
} else {
|
|
|
|
// coinbase inputs don't need any UTXOs
|
|
|
|
self.value_from_outputs(&HashMap::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-02 08:07:35 -07:00
|
|
|
/// Get the value spent by this input, by looking up its [`OutPoint`] in
|
|
|
|
/// [`OrderedUtxo`]s.
|
|
|
|
///
|
|
|
|
/// See [`Self::value`] for details.
|
2021-08-09 10:22:26 -07:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// If the provided [`OrderedUtxo`]s don't have this input's [`OutPoint`].
|
2021-08-09 10:22:26 -07:00
|
|
|
pub fn value_from_ordered_utxos(
|
|
|
|
&self,
|
|
|
|
ordered_utxos: &HashMap<OutPoint, utxo::OrderedUtxo>,
|
|
|
|
) -> Amount<NonNegative> {
|
|
|
|
if let Some(outpoint) = self.outpoint() {
|
|
|
|
// look up the specific Output and convert it to the expected format
|
|
|
|
let output = ordered_utxos
|
|
|
|
.get(&outpoint)
|
|
|
|
.expect("provided Utxos don't have spent OutPoint")
|
|
|
|
.utxo
|
|
|
|
.output
|
|
|
|
.clone();
|
|
|
|
self.value_from_outputs(&iter::once((outpoint, output)).collect())
|
|
|
|
} else {
|
|
|
|
// coinbase inputs don't need any UTXOs
|
|
|
|
self.value_from_outputs(&HashMap::new())
|
|
|
|
}
|
|
|
|
}
|
2021-07-22 16:40:15 -07:00
|
|
|
}
|
|
|
|
|
2020-08-17 01:24:33 -07:00
|
|
|
/// A transparent output from a transaction.
|
|
|
|
///
|
|
|
|
/// The most fundamental building block of a transaction is a
|
|
|
|
/// transaction output -- the ZEC you own in your "wallet" is in
|
|
|
|
/// fact a subset of unspent transaction outputs (or "UTXO"s) of the
|
|
|
|
/// global UTXO set.
|
|
|
|
///
|
|
|
|
/// UTXOs are indivisible, discrete units of value which can only be
|
|
|
|
/// consumed in their entirety. Thus, if I want to send you 1 ZEC and
|
|
|
|
/// I only own one UTXO worth 2 ZEC, I would construct a transaction
|
|
|
|
/// that spends my UTXO and sends 1 ZEC to you and 1 ZEC back to me
|
|
|
|
/// (just like receiving change).
|
2022-03-18 13:30:16 -07:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
2023-06-14 12:01:39 -07:00
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Deserialize))]
|
2022-04-12 21:06:52 -07:00
|
|
|
#[cfg_attr(
|
2023-06-14 12:01:39 -07:00
|
|
|
any(test, feature = "proptest-impl", feature = "elasticsearch"),
|
|
|
|
derive(Serialize)
|
2022-04-12 21:06:52 -07:00
|
|
|
)]
|
2020-08-17 01:24:33 -07:00
|
|
|
pub struct Output {
|
|
|
|
/// Transaction value.
|
|
|
|
// At https://en.bitcoin.it/wiki/Protocol_documentation#tx, this is an i64.
|
|
|
|
pub value: Amount<NonNegative>,
|
|
|
|
|
|
|
|
/// The lock script defines the conditions under which this output can be spent.
|
|
|
|
pub lock_script: Script,
|
|
|
|
}
|
2021-07-28 20:49:36 -07:00
|
|
|
|
|
|
|
impl Output {
|
2022-11-09 16:12:27 -08:00
|
|
|
/// Returns a new coinbase output that pays `amount` using `lock_script`.
|
|
|
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
|
|
|
pub fn new_coinbase(amount: Amount<NonNegative>, lock_script: Script) -> Output {
|
|
|
|
Output {
|
|
|
|
value: amount,
|
|
|
|
lock_script,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-28 20:49:36 -07:00
|
|
|
/// Get the value contained in this output.
|
|
|
|
/// This amount is subtracted from the transaction value pool by this output.
|
2021-08-09 10:22:26 -07:00
|
|
|
pub fn value(&self) -> Amount<NonNegative> {
|
2021-07-28 20:49:36 -07:00
|
|
|
self.value
|
|
|
|
}
|
2022-03-10 19:23:04 -08:00
|
|
|
|
|
|
|
/// Return the destination address from a transparent output.
|
|
|
|
///
|
|
|
|
/// Returns None if the address type is not valid or unrecognized.
|
2024-03-19 13:45:27 -07:00
|
|
|
pub fn address(&self, network: &Network) -> Option<Address> {
|
2022-03-10 19:23:04 -08:00
|
|
|
zcash_primitives::transparent_output_address(self, network)
|
|
|
|
}
|
2021-07-28 20:49:36 -07:00
|
|
|
}
|