Skip to main content

zebra_chain/
value_balance.rs

1//! Balances in chain value pools and transaction value pools.
2
3use crate::amount::{self, Amount, Constraint, NegativeAllowed, NonNegative};
4
5use core::fmt;
6
7#[cfg(any(test, feature = "proptest-impl"))]
8use std::{borrow::Borrow, collections::HashMap};
9
10#[cfg(any(test, feature = "proptest-impl"))]
11use crate::{amount::MAX_MONEY, transaction::Transaction, transparent};
12
13#[cfg(any(test, feature = "proptest-impl"))]
14mod arbitrary;
15
16#[cfg(test)]
17mod tests;
18
19use ValueBalanceError::*;
20
21/// A balance in each chain value pool or transaction value pool.
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
23pub struct ValueBalance<C> {
24    transparent: Amount<C>,
25    sprout: Amount<C>,
26    sapling: Amount<C>,
27    orchard: Amount<C>,
28    deferred: Amount<C>,
29    ironwood: Amount<C>,
30}
31
32impl<C> ValueBalance<C>
33where
34    C: Constraint + Copy,
35{
36    /// Creates a [`ValueBalance`] from the given transparent amount.
37    pub fn from_transparent_amount(transparent_amount: Amount<C>) -> Self {
38        ValueBalance {
39            transparent: transparent_amount,
40            ..ValueBalance::zero()
41        }
42    }
43
44    /// Creates a [`ValueBalance`] from the given sprout amount.
45    pub fn from_sprout_amount(sprout_amount: Amount<C>) -> Self {
46        ValueBalance {
47            sprout: sprout_amount,
48            ..ValueBalance::zero()
49        }
50    }
51
52    /// Creates a [`ValueBalance`] from the given sapling amount.
53    pub fn from_sapling_amount(sapling_amount: Amount<C>) -> Self {
54        ValueBalance {
55            sapling: sapling_amount,
56            ..ValueBalance::zero()
57        }
58    }
59
60    /// Creates a [`ValueBalance`] from the given orchard amount.
61    pub fn from_orchard_amount(orchard_amount: Amount<C>) -> Self {
62        ValueBalance {
63            orchard: orchard_amount,
64            ..ValueBalance::zero()
65        }
66    }
67
68    /// Creates a [`ValueBalance`] from the given ironwood amount.
69    pub fn from_ironwood_amount(ironwood_amount: Amount<C>) -> Self {
70        ValueBalance {
71            ironwood: ironwood_amount,
72            ..ValueBalance::zero()
73        }
74    }
75
76    /// Get the transparent amount from the [`ValueBalance`].
77    pub fn transparent_amount(&self) -> Amount<C> {
78        self.transparent
79    }
80
81    /// Insert a transparent value balance into a given [`ValueBalance`]
82    /// leaving the other values untouched.
83    pub fn set_transparent_value_balance(
84        &mut self,
85        transparent_value_balance: ValueBalance<C>,
86    ) -> &Self {
87        self.transparent = transparent_value_balance.transparent;
88        self
89    }
90
91    /// Get the sprout amount from the [`ValueBalance`].
92    pub fn sprout_amount(&self) -> Amount<C> {
93        self.sprout
94    }
95
96    /// Insert a sprout value balance into a given [`ValueBalance`]
97    /// leaving the other values untouched.
98    pub fn set_sprout_value_balance(&mut self, sprout_value_balance: ValueBalance<C>) -> &Self {
99        self.sprout = sprout_value_balance.sprout;
100        self
101    }
102
103    /// Get the sapling amount from the [`ValueBalance`].
104    pub fn sapling_amount(&self) -> Amount<C> {
105        self.sapling
106    }
107
108    /// Insert a sapling value balance into a given [`ValueBalance`]
109    /// leaving the other values untouched.
110    pub fn set_sapling_value_balance(&mut self, sapling_value_balance: ValueBalance<C>) -> &Self {
111        self.sapling = sapling_value_balance.sapling;
112        self
113    }
114
115    /// Get the orchard amount from the [`ValueBalance`].
116    pub fn orchard_amount(&self) -> Amount<C> {
117        self.orchard
118    }
119
120    /// Insert an orchard value balance into a given [`ValueBalance`]
121    /// leaving the other values untouched.
122    pub fn set_orchard_value_balance(&mut self, orchard_value_balance: ValueBalance<C>) -> &Self {
123        self.orchard = orchard_value_balance.orchard;
124        self
125    }
126
127    /// Returns the deferred amount.
128    pub fn deferred_amount(&self) -> Amount<C> {
129        self.deferred
130    }
131
132    /// Sets the deferred amount without affecting other amounts.
133    pub fn set_deferred_amount(&mut self, deferred_amount: Amount<C>) -> &Self {
134        self.deferred = deferred_amount;
135        self
136    }
137
138    /// Get the ironwood amount from the [`ValueBalance`].
139    pub fn ironwood_amount(&self) -> Amount<C> {
140        self.ironwood
141    }
142
143    /// Insert an ironwood value balance into a given [`ValueBalance`]
144    /// leaving the other values untouched.
145    pub fn set_ironwood_value_balance(&mut self, ironwood_value_balance: ValueBalance<C>) -> &Self {
146        self.ironwood = ironwood_value_balance.ironwood;
147        self
148    }
149
150    /// Creates a [`ValueBalance`] where all the pools are zero.
151    pub fn zero() -> Self {
152        let zero = Amount::zero();
153        Self {
154            transparent: zero,
155            sprout: zero,
156            sapling: zero,
157            orchard: zero,
158            deferred: zero,
159            ironwood: zero,
160        }
161    }
162
163    /// Returns the sum of all value pool balances.
164    pub fn total(self) -> Result<Amount<C>, amount::Error> {
165        let total: i128 = [
166            self.transparent,
167            self.sprout,
168            self.sapling,
169            self.orchard,
170            self.deferred,
171            self.ironwood,
172        ]
173        .into_iter()
174        .map(|amount| i128::from(amount.zatoshis()))
175        .sum();
176
177        Amount::try_from(total)
178    }
179
180    /// Convert this value balance to a different ValueBalance type,
181    /// if it satisfies the new constraint
182    pub fn constrain<C2>(self) -> Result<ValueBalance<C2>, ValueBalanceError>
183    where
184        C2: Constraint,
185    {
186        Ok(ValueBalance::<C2> {
187            transparent: self.transparent.constrain().map_err(Transparent)?,
188            sprout: self.sprout.constrain().map_err(Sprout)?,
189            sapling: self.sapling.constrain().map_err(Sapling)?,
190            orchard: self.orchard.constrain().map_err(Orchard)?,
191            deferred: self.deferred.constrain().map_err(Deferred)?,
192            ironwood: self.ironwood.constrain().map_err(Ironwood)?,
193        })
194    }
195}
196
197impl ValueBalance<NegativeAllowed> {
198    /// Assumes that this value balance is a non-coinbase transaction value balance,
199    /// and returns the remaining value in the transaction value pool.
200    ///
201    /// # Consensus
202    ///
203    /// > The remaining value in the transparent transaction value pool MUST be nonnegative.
204    ///
205    /// <https://zips.z.cash/protocol/protocol.pdf#transactions>
206    ///
207    /// This rule applies to Block and Mempool transactions.
208    ///
209    /// Design: <https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/rfcs/0012-value-pools.md#definitions>
210    pub fn remaining_transaction_value(&self) -> Result<Amount<NonNegative>, amount::Error> {
211        // Calculated by summing the transparent, sprout, sapling, orchard, and ironwood value
212        // balances, as specified in:
213        // https://zebra.zfnd.org/dev/rfcs/0012-value-pools.html#definitions
214        //
215        // The ironwood bundle (NU6.3 onward) contributes to the transaction value pool exactly
216        // like the orchard bundle; it is zero for transactions without an ironwood bundle.
217        //
218        // This will error if the remaining value in the transaction value pool is negative.
219        (self.transparent + self.sprout + self.sapling + self.orchard + self.ironwood)?
220            .constrain::<NonNegative>()
221    }
222}
223
224impl ValueBalance<NonNegative> {
225    /// Returns the sum of this value balance, and the chain value pool changes in `transaction`.
226    ///
227    /// `outputs` must contain the [`transparent::Output`]s of every input in this transaction,
228    /// including UTXOs created by earlier transactions in its block.
229    ///
230    /// Note: the chain value pool has the opposite sign to the transaction
231    /// value pool.
232    ///
233    /// # Consensus
234    ///
235    /// > If any of the "Sprout chain value pool balance", "Sapling chain value pool balance", or
236    /// > "Orchard chain value pool balance" would become negative in the block chain created
237    /// > as a result of accepting a block, then all nodes MUST reject the block as invalid.
238    /// >
239    /// > Nodes MAY relay transactions even if one or more of them cannot be mined due to the
240    /// > aforementioned restriction.
241    ///
242    /// <https://zips.z.cash/zip-0209#specification>
243    ///
244    /// Since this consensus rule is optional for mempool transactions,
245    /// Zebra does not check it in the mempool transaction verifier.
246    #[cfg(any(test, feature = "proptest-impl"))]
247    pub fn add_transaction(
248        self,
249        transaction: impl Borrow<Transaction>,
250        utxos: &HashMap<transparent::OutPoint, transparent::Output>,
251    ) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
252        use std::ops::Neg;
253
254        // the chain pool (unspent outputs) has the opposite sign to
255        // transaction value balances (inputs - outputs)
256        let chain_value_pool_change = transaction
257            .borrow()
258            .value_balance_from_outputs(utxos)?
259            .neg();
260
261        self.add_chain_value_pool_change(chain_value_pool_change)
262    }
263
264    /// Returns the sum of this value balance, and the chain value pool change in `input`.
265    ///
266    /// `outputs` must contain the [`transparent::Output`] spent by `input`,
267    /// (including UTXOs created by earlier transactions in its block).
268    ///
269    /// Note: the chain value pool has the opposite sign to the transaction
270    /// value pool. Inputs remove value from the chain value pool.
271    #[cfg(any(test, feature = "proptest-impl"))]
272    pub fn add_transparent_input(
273        self,
274        input: impl Borrow<transparent::Input>,
275        utxos: &HashMap<transparent::OutPoint, transparent::Output>,
276    ) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
277        use std::ops::Neg;
278
279        // the chain pool (unspent outputs) has the opposite sign to
280        // transaction value balances (inputs - outputs)
281        let transparent_value_pool_change = input.borrow().value_from_outputs(utxos).neg();
282        let transparent_value_pool_change =
283            ValueBalance::from_transparent_amount(transparent_value_pool_change);
284
285        self.add_chain_value_pool_change(transparent_value_pool_change)
286    }
287
288    /// Returns the sum of this value balance, and the given `chain_value_pool_change`.
289    ///
290    /// Note that the chain value pool has the opposite sign to the transaction value pool.
291    ///
292    /// # Consensus
293    ///
294    /// > If the Sprout chain value pool balance would become negative in the block chain
295    /// > created as a result of accepting a block, then all nodes MUST reject the block as invalid.
296    ///
297    /// <https://zips.z.cash/protocol/protocol.pdf#joinsplitbalance>
298    ///
299    /// > If the Sapling chain value pool balance would become negative in the block chain
300    /// > created as a result of accepting a block, then all nodes MUST reject the block as invalid.
301    ///
302    /// <https://zips.z.cash/protocol/protocol.pdf#saplingbalance>
303    ///
304    /// > If the Orchard chain value pool balance would become negative in the block chain
305    /// > created as a result of accepting a block , then all nodes MUST reject the block as invalid.
306    ///
307    /// <https://zips.z.cash/protocol/protocol.pdf#orchardbalance>
308    ///
309    /// > If any of the "Sprout chain value pool balance", "Sapling chain value pool balance", or
310    /// > "Orchard chain value pool balance" would become negative in the block chain created
311    /// > as a result of accepting a block, then all nodes MUST reject the block as invalid.
312    ///
313    /// <https://zips.z.cash/zip-0209#specification>
314    ///
315    /// Zebra also checks that the transparent value pool is non-negative.
316    /// In Zebra, we define this pool as the sum of all unspent transaction outputs.
317    /// (Despite their encoding as an `int64`, transparent output values must be non-negative.)
318    ///
319    /// This is a consensus rule derived from Bitcoin:
320    ///
321    /// > because a UTXO can only be spent once,
322    /// > the full value of the included UTXOs must be spent or given to a miner as a transaction fee.
323    ///
324    /// <https://developer.bitcoin.org/devguide/transactions.html#transaction-fees-and-change>
325    ///
326    /// We implement the consensus rules above by constraining the returned value balance to
327    /// [`ValueBalance<NonNegative>`].
328    #[allow(clippy::unwrap_in_result)]
329    pub fn add_chain_value_pool_change(
330        self,
331        chain_value_pool_change: ValueBalance<NegativeAllowed>,
332    ) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
333        let mut chain_value_pool = self
334            .constrain::<NegativeAllowed>()
335            .expect("conversion from NonNegative to NegativeAllowed is always valid");
336        chain_value_pool = (chain_value_pool + chain_value_pool_change)?;
337
338        let chain_value_pool = chain_value_pool.constrain::<NonNegative>()?;
339
340        // The sum of all chain value pools is the total monetary base, which consensus caps at
341        // `MAX_MONEY`. Reject any change that would push the chain value pool total over that cap.
342        chain_value_pool.total().map_err(ValueBalanceError::Total)?;
343
344        Ok(chain_value_pool)
345    }
346
347    /// Create a fake value pool for testing purposes.
348    ///
349    /// The resulting [`ValueBalance`] has `MAX_MONEY / 8` on the transparent, Sprout, Sapling,
350    /// Orchard, and Ironwood pools; the deferred pool is zero. This keeps the total within the
351    /// valid `Amount` range (see [`ValueBalance::total`]), while leaving headroom for value pool
352    /// changes that tests commit on top of it.
353    #[cfg(any(test, feature = "proptest-impl"))]
354    pub fn fake_populated_pool() -> ValueBalance<NonNegative> {
355        let mut fake_value_pool = ValueBalance::zero();
356
357        let fake_transparent_value_balance =
358            ValueBalance::from_transparent_amount(Amount::try_from(MAX_MONEY / 8).unwrap());
359        let fake_sprout_value_balance =
360            ValueBalance::from_sprout_amount(Amount::try_from(MAX_MONEY / 8).unwrap());
361        let fake_sapling_value_balance =
362            ValueBalance::from_sapling_amount(Amount::try_from(MAX_MONEY / 8).unwrap());
363        let fake_orchard_value_balance =
364            ValueBalance::from_orchard_amount(Amount::try_from(MAX_MONEY / 8).unwrap());
365        let fake_ironwood_value_balance =
366            ValueBalance::from_ironwood_amount(Amount::try_from(MAX_MONEY / 8).unwrap());
367
368        fake_value_pool.set_transparent_value_balance(fake_transparent_value_balance);
369        fake_value_pool.set_sprout_value_balance(fake_sprout_value_balance);
370        fake_value_pool.set_sapling_value_balance(fake_sapling_value_balance);
371        fake_value_pool.set_orchard_value_balance(fake_orchard_value_balance);
372        fake_value_pool.set_ironwood_value_balance(fake_ironwood_value_balance);
373
374        fake_value_pool
375    }
376
377    /// To byte array
378    ///
379    /// The `ironwood` pool (NU6.3 onward) is appended after `deferred`, so that records written by
380    /// earlier Zebra versions (32 bytes without `deferred`, or 40 bytes with it) remain parsable by
381    /// [`Self::from_bytes`].
382    pub fn to_bytes(self) -> [u8; 48] {
383        match [
384            self.transparent.to_bytes(),
385            self.sprout.to_bytes(),
386            self.sapling.to_bytes(),
387            self.orchard.to_bytes(),
388            self.deferred.to_bytes(),
389            self.ironwood.to_bytes(),
390        ]
391        .concat()
392        .try_into()
393        {
394            Ok(bytes) => bytes,
395            _ => unreachable!(
396                "six [u8; 8] should always concat with no error into a single [u8; 48]"
397            ),
398        }
399    }
400
401    /// From byte array
402    ///
403    /// Accepts 32-byte (pre-`deferred`), 40-byte (pre-`ironwood`), and 48-byte records; missing
404    /// trailing pools default to zero.
405    #[allow(clippy::unwrap_in_result)]
406    pub fn from_bytes(bytes: &[u8]) -> Result<ValueBalance<NonNegative>, ValueBalanceError> {
407        let bytes_length = bytes.len();
408
409        // Return an error early if bytes don't have the right length instead of panicking later.
410        match bytes_length {
411            32 | 40 | 48 => {}
412            _ => return Err(Unparsable),
413        };
414
415        let transparent = Amount::from_bytes(
416            bytes[0..8]
417                .try_into()
418                .expect("transparent amount should be parsable"),
419        )
420        .map_err(Transparent)?;
421
422        let sprout = Amount::from_bytes(
423            bytes[8..16]
424                .try_into()
425                .expect("sprout amount should be parsable"),
426        )
427        .map_err(Sprout)?;
428
429        let sapling = Amount::from_bytes(
430            bytes[16..24]
431                .try_into()
432                .expect("sapling amount should be parsable"),
433        )
434        .map_err(Sapling)?;
435
436        let orchard = Amount::from_bytes(
437            bytes[24..32]
438                .try_into()
439                .expect("orchard amount should be parsable"),
440        )
441        .map_err(Orchard)?;
442
443        let deferred = match bytes_length {
444            32 => Amount::zero(),
445            40 | 48 => Amount::from_bytes(
446                bytes[32..40]
447                    .try_into()
448                    .expect("deferred amount should be parsable"),
449            )
450            .map_err(Deferred)?,
451            _ => return Err(Unparsable),
452        };
453
454        let ironwood = match bytes_length {
455            32 | 40 => Amount::zero(),
456            48 => Amount::from_bytes(
457                bytes[40..48]
458                    .try_into()
459                    .expect("ironwood amount should be parsable"),
460            )
461            .map_err(Ironwood)?,
462            _ => return Err(Unparsable),
463        };
464
465        Ok(ValueBalance {
466            transparent,
467            sprout,
468            sapling,
469            orchard,
470            deferred,
471            ironwood,
472        })
473    }
474}
475
476#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
477/// Errors that can be returned when validating a [`ValueBalance`]
478pub enum ValueBalanceError {
479    /// transparent amount error {0}
480    Transparent(amount::Error),
481
482    /// sprout amount error {0}
483    Sprout(amount::Error),
484
485    /// sapling amount error {0}
486    Sapling(amount::Error),
487
488    /// orchard amount error {0}
489    Orchard(amount::Error),
490
491    /// deferred amount error {0}
492    Deferred(amount::Error),
493
494    /// ironwood amount error {0}
495    Ironwood(amount::Error),
496
497    /// total amount error {0}
498    Total(amount::Error),
499
500    /// ValueBalance is unparsable
501    Unparsable,
502}
503
504impl fmt::Display for ValueBalanceError {
505    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506        f.write_str(&match self {
507            Transparent(e) => format!("transparent amount err: {e}"),
508            Sprout(e) => format!("sprout amount err: {e}"),
509            Sapling(e) => format!("sapling amount err: {e}"),
510            Orchard(e) => format!("orchard amount err: {e}"),
511            Deferred(e) => format!("deferred amount err: {e}"),
512            Ironwood(e) => format!("ironwood amount err: {e}"),
513            Total(e) => format!("total amount err: {e}"),
514            Unparsable => "value balance is unparsable".to_string(),
515        })
516    }
517}
518
519impl<C> std::ops::Add for ValueBalance<C>
520where
521    C: Constraint,
522{
523    type Output = Result<ValueBalance<C>, ValueBalanceError>;
524    fn add(self, rhs: ValueBalance<C>) -> Self::Output {
525        Ok(ValueBalance::<C> {
526            transparent: (self.transparent + rhs.transparent).map_err(Transparent)?,
527            sprout: (self.sprout + rhs.sprout).map_err(Sprout)?,
528            sapling: (self.sapling + rhs.sapling).map_err(Sapling)?,
529            orchard: (self.orchard + rhs.orchard).map_err(Orchard)?,
530            deferred: (self.deferred + rhs.deferred).map_err(Deferred)?,
531            ironwood: (self.ironwood + rhs.ironwood).map_err(Ironwood)?,
532        })
533    }
534}
535
536impl<C> std::ops::Add<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
537where
538    C: Constraint,
539{
540    type Output = Result<ValueBalance<C>, ValueBalanceError>;
541    fn add(self, rhs: ValueBalance<C>) -> Self::Output {
542        self? + rhs
543    }
544}
545
546impl<C> std::ops::Add<Result<ValueBalance<C>, ValueBalanceError>> for ValueBalance<C>
547where
548    C: Constraint,
549{
550    type Output = Result<ValueBalance<C>, ValueBalanceError>;
551
552    fn add(self, rhs: Result<ValueBalance<C>, ValueBalanceError>) -> Self::Output {
553        self + rhs?
554    }
555}
556
557impl<C> std::ops::AddAssign<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
558where
559    ValueBalance<C>: Copy,
560    C: Constraint,
561{
562    fn add_assign(&mut self, rhs: ValueBalance<C>) {
563        if let Ok(lhs) = *self {
564            *self = lhs + rhs;
565        }
566    }
567}
568
569impl<C> std::ops::Sub for ValueBalance<C>
570where
571    C: Constraint,
572{
573    type Output = Result<ValueBalance<C>, ValueBalanceError>;
574    fn sub(self, rhs: ValueBalance<C>) -> Self::Output {
575        Ok(ValueBalance::<C> {
576            transparent: (self.transparent - rhs.transparent).map_err(Transparent)?,
577            sprout: (self.sprout - rhs.sprout).map_err(Sprout)?,
578            sapling: (self.sapling - rhs.sapling).map_err(Sapling)?,
579            orchard: (self.orchard - rhs.orchard).map_err(Orchard)?,
580            deferred: (self.deferred - rhs.deferred).map_err(Deferred)?,
581            ironwood: (self.ironwood - rhs.ironwood).map_err(Ironwood)?,
582        })
583    }
584}
585impl<C> std::ops::Sub<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
586where
587    C: Constraint,
588{
589    type Output = Result<ValueBalance<C>, ValueBalanceError>;
590    fn sub(self, rhs: ValueBalance<C>) -> Self::Output {
591        self? - rhs
592    }
593}
594
595impl<C> std::ops::Sub<Result<ValueBalance<C>, ValueBalanceError>> for ValueBalance<C>
596where
597    C: Constraint,
598{
599    type Output = Result<ValueBalance<C>, ValueBalanceError>;
600
601    fn sub(self, rhs: Result<ValueBalance<C>, ValueBalanceError>) -> Self::Output {
602        self - rhs?
603    }
604}
605
606impl<C> std::ops::SubAssign<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
607where
608    ValueBalance<C>: Copy,
609    C: Constraint,
610{
611    fn sub_assign(&mut self, rhs: ValueBalance<C>) {
612        if let Ok(lhs) = *self {
613            *self = lhs - rhs;
614        }
615    }
616}
617
618impl<C> std::iter::Sum<ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
619where
620    C: Constraint + Copy,
621{
622    fn sum<I: Iterator<Item = ValueBalance<C>>>(mut iter: I) -> Self {
623        iter.try_fold(ValueBalance::zero(), |acc, value_balance| {
624            acc + value_balance
625        })
626    }
627}
628
629impl<'amt, C> std::iter::Sum<&'amt ValueBalance<C>> for Result<ValueBalance<C>, ValueBalanceError>
630where
631    C: Constraint + std::marker::Copy + 'amt,
632{
633    fn sum<I: Iterator<Item = &'amt ValueBalance<C>>>(iter: I) -> Self {
634        iter.copied().sum()
635    }
636}
637
638impl<C> std::ops::Neg for ValueBalance<C>
639where
640    C: Constraint,
641{
642    type Output = ValueBalance<NegativeAllowed>;
643
644    fn neg(self) -> Self::Output {
645        ValueBalance::<NegativeAllowed> {
646            transparent: self.transparent.neg(),
647            sprout: self.sprout.neg(),
648            sapling: self.sapling.neg(),
649            orchard: self.orchard.neg(),
650            deferred: self.deferred.neg(),
651            ironwood: self.ironwood.neg(),
652        }
653    }
654}