2021-07-22 05:49:18 -07:00
|
|
|
//! A type that can hold the four types of Zcash value pools.
|
|
|
|
|
2021-08-09 10:22:26 -07:00
|
|
|
use crate::{
|
|
|
|
amount::{self, Amount, Constraint, NegativeAllowed, NonNegative},
|
|
|
|
block::Block,
|
|
|
|
transparent,
|
|
|
|
};
|
2021-07-22 05:49:18 -07:00
|
|
|
|
2021-08-09 10:22:26 -07:00
|
|
|
use std::{borrow::Borrow, collections::HashMap, convert::TryInto};
|
|
|
|
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
2021-08-20 06:30:38 -07:00
|
|
|
use crate::{amount::MAX_MONEY, transaction::Transaction};
|
2021-08-08 18:22:27 -07:00
|
|
|
|
2021-07-22 05:49:18 -07:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
mod arbitrary;
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2021-08-09 06:13:27 -07:00
|
|
|
use ValueBalanceError::*;
|
|
|
|
|
2021-07-22 05:49:18 -07:00
|
|
|
/// An amount spread between different Zcash pools.
|
2023-10-12 13:00:43 -07:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
2021-07-22 05:49:18 -07:00
|
|
|
pub struct ValueBalance<C> {
|
|
|
|
transparent: Amount<C>,
|
|
|
|
sprout: Amount<C>,
|
|
|
|
sapling: Amount<C>,
|
|
|
|
orchard: Amount<C>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C> ValueBalance<C>
|
|
|
|
where
|
|
|
|
C: Constraint + Copy,
|
|
|
|
{
|
2021-08-20 06:30:38 -07:00
|
|
|
/// Creates a [`ValueBalance`] from the given transparent amount.
|
|
|
|
pub fn from_transparent_amount(transparent_amount: Amount<C>) -> Self {
|
|
|
|
ValueBalance {
|
|
|
|
transparent: transparent_amount,
|
|
|
|
..ValueBalance::zero()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a [`ValueBalance`] from the given sprout amount.
|
|
|
|
pub fn from_sprout_amount(sprout_amount: Amount<C>) -> Self {
|
|
|
|
ValueBalance {
|
|
|
|
sprout: sprout_amount,
|
|
|
|
..ValueBalance::zero()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a [`ValueBalance`] from the given sapling amount.
|
|
|
|
pub fn from_sapling_amount(sapling_amount: Amount<C>) -> Self {
|
|
|
|
ValueBalance {
|
|
|
|
sapling: sapling_amount,
|
|
|
|
..ValueBalance::zero()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a [`ValueBalance`] from the given orchard amount.
|
|
|
|
pub fn from_orchard_amount(orchard_amount: Amount<C>) -> Self {
|
|
|
|
ValueBalance {
|
|
|
|
orchard: orchard_amount,
|
|
|
|
..ValueBalance::zero()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the transparent amount from the [`ValueBalance`].
|
|
|
|
pub fn transparent_amount(&self) -> Amount<C> {
|
|
|
|
self.transparent
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a transparent value balance into a given [`ValueBalance`]
|
|
|
|
/// leaving the other values untouched.
|
|
|
|
pub fn set_transparent_value_balance(
|
|
|
|
&mut self,
|
|
|
|
transparent_value_balance: ValueBalance<C>,
|
|
|
|
) -> &Self {
|
|
|
|
self.transparent = transparent_value_balance.transparent;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the sprout amount from the [`ValueBalance`].
|
|
|
|
pub fn sprout_amount(&self) -> Amount<C> {
|
|
|
|
self.sprout
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a sprout value balance into a given [`ValueBalance`]
|
|
|
|
/// leaving the other values untouched.
|
|
|
|
pub fn set_sprout_value_balance(&mut self, sprout_value_balance: ValueBalance<C>) -> &Self {
|
|
|
|
self.sprout = sprout_value_balance.sprout;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the sapling amount from the [`ValueBalance`].
|
|
|
|
pub fn sapling_amount(&self) -> Amount<C> {
|
|
|
|
self.sapling
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a sapling value balance into a given [`ValueBalance`]
|
|
|
|
/// leaving the other values untouched.
|
|
|
|
pub fn set_sapling_value_balance(&mut self, sapling_value_balance: ValueBalance<C>) -> &Self {
|
|
|
|
self.sapling = sapling_value_balance.sapling;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the orchard amount from the [`ValueBalance`].
|
|
|
|
pub fn orchard_amount(&self) -> Amount<C> {
|
|
|
|
self.orchard
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert an orchard value balance into a given [`ValueBalance`]
|
|
|
|
/// leaving the other values untouched.
|
|
|
|
pub fn set_orchard_value_balance(&mut self, orchard_value_balance: ValueBalance<C>) -> &Self {
|
|
|
|
self.orchard = orchard_value_balance.orchard;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a [`ValueBalance`] where all the pools are zero.
|
|
|
|
pub fn zero() -> Self {
|
|
|
|
let zero = Amount::zero();
|
|
|
|
Self {
|
|
|
|
transparent: zero,
|
|
|
|
sprout: zero,
|
|
|
|
sapling: zero,
|
|
|
|
orchard: zero,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert this value balance to a different ValueBalance type,
|
|
|
|
/// if it satisfies the new constraint
|
|
|
|
pub fn constrain<C2>(self) -> Result<ValueBalance<C2>, ValueBalanceError>
|
|
|
|
where
|
|
|
|
C2: Constraint,
|
|
|
|
{
|
|
|
|
Ok(ValueBalance::<C2> {
|
|
|
|
transparent: self.transparent.constrain().map_err(Transparent)?,
|
|
|
|
sprout: self.sprout.constrain().map_err(Sprout)?,
|
|
|
|
sapling: self.sapling.constrain().map_err(Sapling)?,
|
|
|
|
orchard: self.orchard.constrain().map_err(Orchard)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ValueBalance<NegativeAllowed> {
|
2022-11-07 09:37:50 -08:00
|
|
|
/// Assumes that this value balance is a non-coinbase transaction value balance,
|
2021-12-30 15:50:05 -08:00
|
|
|
/// and returns the remaining value in the transaction value pool.
|
|
|
|
///
|
|
|
|
/// # Consensus
|
|
|
|
///
|
|
|
|
/// > The remaining value in the transparent transaction value pool MUST be nonnegative.
|
|
|
|
///
|
|
|
|
/// <https://zips.z.cash/protocol/protocol.pdf#transactions>
|
2021-07-22 05:49:18 -07:00
|
|
|
///
|
|
|
|
/// This rule applies to Block and Mempool transactions.
|
|
|
|
///
|
2021-12-30 15:50:05 -08:00
|
|
|
/// Design: <https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/rfcs/0012-value-pools.md#definitions>
|
2021-08-09 10:22:26 -07:00
|
|
|
pub fn remaining_transaction_value(&self) -> Result<Amount<NonNegative>, amount::Error> {
|
|
|
|
// Calculated by summing the transparent, sprout, sapling, and orchard value balances,
|
|
|
|
// as specified in:
|
2021-08-08 15:41:34 -07:00
|
|
|
// https://zebra.zfnd.org/dev/rfcs/0012-value-pools.html#definitions
|
2021-12-30 15:50:05 -08:00
|
|
|
//
|
|
|
|
// This will error if the remaining value in the transaction value pool is negative.
|
2021-08-09 10:22:26 -07:00
|
|
|
(self.transparent + self.sprout + self.sapling + self.orchard)?.constrain::<NonNegative>()
|
|
|
|
}
|
2021-08-20 06:30:38 -07:00
|
|
|
}
|
2021-08-09 10:22:26 -07:00
|
|
|
|
2021-08-20 06:30:38 -07:00
|
|
|
impl ValueBalance<NonNegative> {
|
2021-12-30 15:50:05 -08:00
|
|
|
/// Returns the sum of this value balance, and the chain value pool changes in `block`.
|
2021-08-09 10:22:26 -07:00
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// `utxos` must contain the [`transparent::Utxo`]s of every input in this block,
|
2021-08-09 10:22:26 -07:00
|
|
|
/// including UTXOs created by earlier transactions in this block.
|
|
|
|
///
|
2021-12-30 15:50:05 -08:00
|
|
|
/// Note: the chain value pool has the opposite sign to the transaction
|
|
|
|
/// value pool.
|
|
|
|
///
|
|
|
|
/// See [`Block::chain_value_pool_change`] for details.
|
2021-08-10 07:42:02 -07:00
|
|
|
///
|
2021-12-30 15:50:05 -08:00
|
|
|
/// # Consensus
|
2021-08-10 07:42:02 -07:00
|
|
|
///
|
2021-12-30 15:50:05 -08:00
|
|
|
/// > If the Sprout chain value pool balance would become negative in the block chain
|
|
|
|
/// > created as a result of accepting a block, then all nodes MUST reject the block as invalid.
|
2021-08-10 07:42:02 -07:00
|
|
|
///
|
2021-12-30 15:50:05 -08:00
|
|
|
/// <https://zips.z.cash/protocol/protocol.pdf#joinsplitbalance>
|
2021-08-10 07:42:02 -07:00
|
|
|
///
|
2021-12-30 15:50:05 -08:00
|
|
|
/// > If the Sapling chain value pool balance would become negative in the block chain
|
|
|
|
/// > created as a result of accepting a block, then all nodes MUST reject the block as invalid.
|
2021-08-09 10:22:26 -07:00
|
|
|
///
|
2021-12-30 15:50:05 -08:00
|
|
|
/// <https://zips.z.cash/protocol/protocol.pdf#saplingbalance>
|
|
|
|
///
|
|
|
|
/// > If the Orchard chain value pool balance would become negative in the block chain
|
|
|
|
/// > created as a result of accepting a block , then all nodes MUST reject the block as invalid.
|
|
|
|
///
|
|
|
|
/// <https://zips.z.cash/protocol/protocol.pdf#orchardbalance>
|
|
|
|
///
|
|
|
|
/// > If any of the "Sprout chain value pool balance", "Sapling chain value pool balance", or
|
|
|
|
/// > "Orchard chain value pool balance" would become negative in the block chain created
|
|
|
|
/// > as a result of accepting a block, then all nodes MUST reject the block as invalid.
|
|
|
|
///
|
|
|
|
/// <https://zips.z.cash/zip-0209#specification>
|
|
|
|
///
|
|
|
|
/// Zebra also checks that the transparent value pool is non-negative.
|
|
|
|
/// In Zebra, we define this pool as the sum of all unspent transaction outputs.
|
|
|
|
/// (Despite their encoding as an `int64`, transparent output values must be non-negative.)
|
|
|
|
///
|
|
|
|
/// This is a consensus rule derived from Bitcoin:
|
|
|
|
///
|
|
|
|
/// > because a UTXO can only be spent once,
|
|
|
|
/// > the full value of the included UTXOs must be spent or given to a miner as a transaction fee.
|
|
|
|
///
|
|
|
|
/// <https://developer.bitcoin.org/devguide/transactions.html#transaction-fees-and-change>
|
2021-08-25 06:57:07 -07:00
|
|
|
pub fn add_block(
|
2021-08-10 07:42:02 -07:00
|
|
|
self,
|
2021-08-09 10:22:26 -07:00
|
|
|
block: impl Borrow<Block>,
|
|
|
|
utxos: &HashMap<transparent::OutPoint, transparent::Utxo>,
|
2021-08-20 06:30:38 -07:00
|
|
|
) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
|
2021-08-09 10:22:26 -07:00
|
|
|
let chain_value_pool_change = block.borrow().chain_value_pool_change(utxos)?;
|
|
|
|
|
2021-12-30 15:50:05 -08:00
|
|
|
// This will error if the chain value pool balance gets negative with the change.
|
2021-08-25 06:57:07 -07:00
|
|
|
self.add_chain_value_pool_change(chain_value_pool_change)
|
2021-08-09 10:22:26 -07:00
|
|
|
}
|
|
|
|
|
2021-12-30 15:50:05 -08:00
|
|
|
/// Returns the sum of this value balance, and the chain value pool changes in `transaction`.
|
2021-08-09 10:22:26 -07:00
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// `outputs` must contain the [`transparent::Output`]s of every input in this transaction,
|
2021-08-09 10:22:26 -07:00
|
|
|
/// including UTXOs created by earlier transactions in its block.
|
|
|
|
///
|
|
|
|
/// Note: the chain value pool has the opposite sign to the transaction
|
|
|
|
/// value pool.
|
|
|
|
///
|
|
|
|
/// See [`Block::chain_value_pool_change`] and [`Transaction::value_balance`]
|
|
|
|
/// for details.
|
2021-12-30 15:50:05 -08:00
|
|
|
///
|
|
|
|
/// # Consensus
|
|
|
|
///
|
|
|
|
/// > If any of the "Sprout chain value pool balance", "Sapling chain value pool balance", or
|
|
|
|
/// > "Orchard chain value pool balance" would become negative in the block chain created
|
|
|
|
/// > as a result of accepting a block, then all nodes MUST reject the block as invalid.
|
|
|
|
/// >
|
|
|
|
/// > Nodes MAY relay transactions even if one or more of them cannot be mined due to the
|
|
|
|
/// > aforementioned restriction.
|
|
|
|
///
|
|
|
|
/// <https://zips.z.cash/zip-0209#specification>
|
|
|
|
///
|
|
|
|
/// Since this consensus rule is optional for mempool transactions,
|
|
|
|
/// Zebra does not check it in the mempool transaction verifier.
|
2021-08-09 10:22:26 -07:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
2021-08-25 06:57:07 -07:00
|
|
|
pub fn add_transaction(
|
2021-08-10 07:42:02 -07:00
|
|
|
self,
|
2021-08-09 10:22:26 -07:00
|
|
|
transaction: impl Borrow<Transaction>,
|
|
|
|
utxos: &HashMap<transparent::OutPoint, transparent::Output>,
|
2021-08-20 06:30:38 -07:00
|
|
|
) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
|
2021-08-09 10:22:26 -07:00
|
|
|
use std::ops::Neg;
|
|
|
|
|
|
|
|
// the chain pool (unspent outputs) has the opposite sign to
|
|
|
|
// transaction value balances (inputs - outputs)
|
|
|
|
let chain_value_pool_change = transaction
|
|
|
|
.borrow()
|
|
|
|
.value_balance_from_outputs(utxos)?
|
|
|
|
.neg();
|
|
|
|
|
2021-08-25 06:57:07 -07:00
|
|
|
self.add_chain_value_pool_change(chain_value_pool_change)
|
2021-08-09 10:22:26 -07:00
|
|
|
}
|
|
|
|
|
2021-12-30 15:50:05 -08:00
|
|
|
/// Returns the sum of this value balance, and the chain value pool change in `input`.
|
2021-08-10 07:42:02 -07:00
|
|
|
///
|
2022-06-02 08:07:35 -07:00
|
|
|
/// `outputs` must contain the [`transparent::Output`] spent by `input`,
|
2021-08-10 07:42:02 -07:00
|
|
|
/// (including UTXOs created by earlier transactions in its block).
|
|
|
|
///
|
|
|
|
/// Note: the chain value pool has the opposite sign to the transaction
|
|
|
|
/// value pool. Inputs remove value from the chain value pool.
|
|
|
|
///
|
|
|
|
/// See [`Block::chain_value_pool_change`] and [`Transaction::value_balance`]
|
|
|
|
/// for details.
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
2021-08-25 06:57:07 -07:00
|
|
|
pub fn add_transparent_input(
|
2021-08-10 07:42:02 -07:00
|
|
|
self,
|
|
|
|
input: impl Borrow<transparent::Input>,
|
|
|
|
utxos: &HashMap<transparent::OutPoint, transparent::Output>,
|
2021-08-20 06:30:38 -07:00
|
|
|
) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
|
2021-08-10 07:42:02 -07:00
|
|
|
use std::ops::Neg;
|
|
|
|
|
|
|
|
// the chain pool (unspent outputs) has the opposite sign to
|
|
|
|
// transaction value balances (inputs - outputs)
|
|
|
|
let transparent_value_pool_change = input.borrow().value_from_outputs(utxos).neg();
|
|
|
|
let transparent_value_pool_change =
|
|
|
|
ValueBalance::from_transparent_amount(transparent_value_pool_change);
|
|
|
|
|
2021-08-25 06:57:07 -07:00
|
|
|
self.add_chain_value_pool_change(transparent_value_pool_change)
|
2021-08-10 07:42:02 -07:00
|
|
|
}
|
|
|
|
|
2021-12-30 15:50:05 -08:00
|
|
|
/// Returns the sum of this value balance, and the `chain_value_pool_change`.
|
2021-08-09 10:22:26 -07:00
|
|
|
///
|
|
|
|
/// Note: the chain value pool has the opposite sign to the transaction
|
|
|
|
/// value pool.
|
|
|
|
///
|
2021-08-25 06:57:07 -07:00
|
|
|
/// See `add_block` for details.
|
2022-06-27 23:22:07 -07:00
|
|
|
#[allow(clippy::unwrap_in_result)]
|
2021-08-25 06:57:07 -07:00
|
|
|
pub fn add_chain_value_pool_change(
|
2021-08-10 07:42:02 -07:00
|
|
|
self,
|
2021-08-09 10:22:26 -07:00
|
|
|
chain_value_pool_change: ValueBalance<NegativeAllowed>,
|
2021-08-20 06:30:38 -07:00
|
|
|
) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
|
2021-08-10 07:42:02 -07:00
|
|
|
let mut chain_value_pool = self
|
2021-08-09 10:22:26 -07:00
|
|
|
.constrain::<NegativeAllowed>()
|
|
|
|
.expect("conversion from NonNegative to NegativeAllowed is always valid");
|
2021-08-10 07:42:02 -07:00
|
|
|
chain_value_pool = (chain_value_pool + chain_value_pool_change)?;
|
|
|
|
|
|
|
|
chain_value_pool.constrain()
|
2021-07-22 05:49:18 -07:00
|
|
|
}
|
|
|
|
|
2021-08-20 06:30:38 -07:00
|
|
|
/// Create a fake value pool for testing purposes.
|
|
|
|
///
|
|
|
|
/// The resulting [`ValueBalance`] will have half of the MAX_MONEY amount on each pool.
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
pub fn fake_populated_pool() -> ValueBalance<NonNegative> {
|
|
|
|
let mut fake_value_pool = ValueBalance::zero();
|
|
|
|
|
|
|
|
let fake_transparent_value_balance =
|
|
|
|
ValueBalance::from_transparent_amount(Amount::try_from(MAX_MONEY / 2).unwrap());
|
|
|
|
let fake_sprout_value_balance =
|
|
|
|
ValueBalance::from_sprout_amount(Amount::try_from(MAX_MONEY / 2).unwrap());
|
|
|
|
let fake_sapling_value_balance =
|
|
|
|
ValueBalance::from_sapling_amount(Amount::try_from(MAX_MONEY / 2).unwrap());
|
|
|
|
let fake_orchard_value_balance =
|
|
|
|
ValueBalance::from_orchard_amount(Amount::try_from(MAX_MONEY / 2).unwrap());
|
|
|
|
|
|
|
|
fake_value_pool.set_transparent_value_balance(fake_transparent_value_balance);
|
|
|
|
fake_value_pool.set_sprout_value_balance(fake_sprout_value_balance);
|
|
|
|
fake_value_pool.set_sapling_value_balance(fake_sapling_value_balance);
|
|
|
|
fake_value_pool.set_orchard_value_balance(fake_orchard_value_balance);
|
|
|
|
|
|
|
|
fake_value_pool
|
2021-08-09 06:13:27 -07:00
|
|
|
}
|
|
|
|
|
2021-08-08 18:22:27 -07:00
|
|
|
/// To byte array
|
|
|
|
pub fn to_bytes(self) -> [u8; 32] {
|
|
|
|
let transparent = self.transparent.to_bytes();
|
|
|
|
let sprout = self.sprout.to_bytes();
|
|
|
|
let sapling = self.sapling.to_bytes();
|
|
|
|
let orchard = self.orchard.to_bytes();
|
|
|
|
match [transparent, sprout, sapling, orchard].concat().try_into() {
|
|
|
|
Ok(bytes) => bytes,
|
|
|
|
_ => unreachable!(
|
|
|
|
"Four [u8; 8] should always concat with no error into a single [u8; 32]"
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// From byte array
|
2022-06-27 23:22:07 -07:00
|
|
|
#[allow(clippy::unwrap_in_result)]
|
2021-08-20 06:30:38 -07:00
|
|
|
pub fn from_bytes(bytes: [u8; 32]) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
|
2021-08-08 18:22:27 -07:00
|
|
|
let transparent = Amount::from_bytes(
|
|
|
|
bytes[0..8]
|
|
|
|
.try_into()
|
|
|
|
.expect("Extracting the first quarter of a [u8; 32] should always succeed"),
|
2021-08-09 06:13:27 -07:00
|
|
|
)
|
|
|
|
.map_err(Transparent)?;
|
|
|
|
|
2021-08-08 18:22:27 -07:00
|
|
|
let sprout = Amount::from_bytes(
|
|
|
|
bytes[8..16]
|
|
|
|
.try_into()
|
|
|
|
.expect("Extracting the second quarter of a [u8; 32] should always succeed"),
|
2021-08-09 06:13:27 -07:00
|
|
|
)
|
|
|
|
.map_err(Sprout)?;
|
|
|
|
|
2021-08-08 18:22:27 -07:00
|
|
|
let sapling = Amount::from_bytes(
|
|
|
|
bytes[16..24]
|
|
|
|
.try_into()
|
|
|
|
.expect("Extracting the third quarter of a [u8; 32] should always succeed"),
|
2021-08-09 06:13:27 -07:00
|
|
|
)
|
|
|
|
.map_err(Sapling)?;
|
|
|
|
|
2021-08-08 18:22:27 -07:00
|
|
|
let orchard = Amount::from_bytes(
|
|
|
|
bytes[24..32]
|
|
|
|
.try_into()
|
|
|
|
.expect("Extracting the last quarter of a [u8; 32] should always succeed"),
|
2021-08-09 06:13:27 -07:00
|
|
|
)
|
|
|
|
.map_err(Orchard)?;
|
2021-08-08 18:22:27 -07:00
|
|
|
|
|
|
|
Ok(ValueBalance {
|
|
|
|
transparent,
|
|
|
|
sprout,
|
|
|
|
sapling,
|
|
|
|
orchard,
|
|
|
|
})
|
|
|
|
}
|
2021-07-22 05:49:18 -07:00
|
|
|
}
|
|
|
|
|
2021-08-09 06:13:27 -07:00
|
|
|
#[derive(thiserror::Error, Debug, displaydoc::Display, Clone, PartialEq, Eq)]
|
|
|
|
/// Errors that can be returned when validating a [`ValueBalance`]
|
2021-07-22 05:49:18 -07:00
|
|
|
pub enum ValueBalanceError {
|
2021-08-09 06:13:27 -07:00
|
|
|
/// transparent amount error {0}
|
2021-08-09 10:22:26 -07:00
|
|
|
Transparent(amount::Error),
|
2021-08-09 06:13:27 -07:00
|
|
|
|
|
|
|
/// sprout amount error {0}
|
2021-08-09 10:22:26 -07:00
|
|
|
Sprout(amount::Error),
|
2021-08-09 06:13:27 -07:00
|
|
|
|
|
|
|
/// sapling amount error {0}
|
2021-08-09 10:22:26 -07:00
|
|
|
Sapling(amount::Error),
|
2021-08-09 06:13:27 -07:00
|
|
|
|
|
|
|
/// orchard amount error {0}
|
2021-08-09 10:22:26 -07:00
|
|
|
Orchard(amount::Error),
|
2021-07-22 05:49:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<C> std::ops::Add for ValueBalance<C>
|
|
|
|
where
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
type Output = Result<ValueBalance<C>, ValueBalanceError>;
|
|
|
|
fn add(self, rhs: ValueBalance<C>) -> Self::Output {
|
|
|
|
Ok(ValueBalance::<C> {
|
2021-08-09 06:13:27 -07:00
|
|
|
transparent: (self.transparent + rhs.transparent).map_err(Transparent)?,
|
|
|
|
sprout: (self.sprout + rhs.sprout).map_err(Sprout)?,
|
|
|
|
sapling: (self.sapling + rhs.sapling).map_err(Sapling)?,
|
|
|
|
orchard: (self.orchard + rhs.orchard).map_err(Orchard)?,
|
2021-07-22 05:49:18 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-08-09 10:22:26 -07:00
|
|
|
|
2021-07-22 05:49:18 -07:00
|
|
|
impl<C> std::ops::Add<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
|
|
|
|
where
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
type Output = Result<ValueBalance<C>, ValueBalanceError>;
|
|
|
|
fn add(self, rhs: ValueBalance<C>) -> Self::Output {
|
|
|
|
self? + rhs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-09 10:22:26 -07:00
|
|
|
impl<C> std::ops::Add<Result<ValueBalance<C>, ValueBalanceError>> for ValueBalance<C>
|
|
|
|
where
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
type Output = Result<ValueBalance<C>, ValueBalanceError>;
|
|
|
|
|
|
|
|
fn add(self, rhs: Result<ValueBalance<C>, ValueBalanceError>) -> Self::Output {
|
|
|
|
self + rhs?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C> std::ops::AddAssign<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
|
|
|
|
where
|
|
|
|
ValueBalance<C>: Copy,
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
fn add_assign(&mut self, rhs: ValueBalance<C>) {
|
|
|
|
if let Ok(lhs) = *self {
|
|
|
|
*self = lhs + rhs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 05:49:18 -07:00
|
|
|
impl<C> std::ops::Sub for ValueBalance<C>
|
|
|
|
where
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
type Output = Result<ValueBalance<C>, ValueBalanceError>;
|
|
|
|
fn sub(self, rhs: ValueBalance<C>) -> Self::Output {
|
|
|
|
Ok(ValueBalance::<C> {
|
2021-08-09 06:13:27 -07:00
|
|
|
transparent: (self.transparent - rhs.transparent).map_err(Transparent)?,
|
|
|
|
sprout: (self.sprout - rhs.sprout).map_err(Sprout)?,
|
|
|
|
sapling: (self.sapling - rhs.sapling).map_err(Sapling)?,
|
|
|
|
orchard: (self.orchard - rhs.orchard).map_err(Orchard)?,
|
2021-07-22 05:49:18 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<C> std::ops::Sub<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
|
|
|
|
where
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
type Output = Result<ValueBalance<C>, ValueBalanceError>;
|
|
|
|
fn sub(self, rhs: ValueBalance<C>) -> Self::Output {
|
|
|
|
self? - rhs
|
|
|
|
}
|
|
|
|
}
|
2021-07-29 16:46:52 -07:00
|
|
|
|
2021-08-09 10:22:26 -07:00
|
|
|
impl<C> std::ops::Sub<Result<ValueBalance<C>, ValueBalanceError>> for ValueBalance<C>
|
|
|
|
where
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
type Output = Result<ValueBalance<C>, ValueBalanceError>;
|
|
|
|
|
|
|
|
fn sub(self, rhs: Result<ValueBalance<C>, ValueBalanceError>) -> Self::Output {
|
|
|
|
self - rhs?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<C> std::ops::SubAssign<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
|
|
|
|
where
|
|
|
|
ValueBalance<C>: Copy,
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
fn sub_assign(&mut self, rhs: ValueBalance<C>) {
|
|
|
|
if let Ok(lhs) = *self {
|
|
|
|
*self = lhs - rhs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-29 16:46:52 -07:00
|
|
|
impl<C> std::iter::Sum<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
|
|
|
|
where
|
|
|
|
C: Constraint + Copy,
|
|
|
|
{
|
|
|
|
fn sum<I: Iterator<Item = ValueBalance<C>>>(mut iter: I) -> Self {
|
|
|
|
iter.try_fold(ValueBalance::zero(), |acc, value_balance| {
|
2021-08-09 06:13:27 -07:00
|
|
|
acc + value_balance
|
2021-07-29 16:46:52 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'amt, C> std::iter::Sum<&'amt ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
|
|
|
|
where
|
|
|
|
C: Constraint + std::marker::Copy + 'amt,
|
|
|
|
{
|
|
|
|
fn sum<I: Iterator<Item = &'amt ValueBalance<C>>>(iter: I) -> Self {
|
|
|
|
iter.copied().sum()
|
|
|
|
}
|
|
|
|
}
|
2021-08-09 06:13:27 -07:00
|
|
|
|
|
|
|
impl<C> std::ops::Neg for ValueBalance<C>
|
|
|
|
where
|
|
|
|
C: Constraint,
|
|
|
|
{
|
|
|
|
type Output = ValueBalance<NegativeAllowed>;
|
|
|
|
|
|
|
|
fn neg(self) -> Self::Output {
|
|
|
|
ValueBalance::<NegativeAllowed> {
|
|
|
|
transparent: self.transparent.neg(),
|
|
|
|
sprout: self.sprout.neg(),
|
|
|
|
sapling: self.sapling.neg(),
|
|
|
|
orchard: self.orchard.neg(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|