zebra_chain/transaction/
builder.rs

1//! Methods for building transactions.
2
3use crate::{
4    amount::{Amount, NonNegative},
5    block::Height,
6    parameters::{Network, NetworkUpgrade},
7    transaction::{LockTime, Transaction},
8    transparent,
9};
10
11impl Transaction {
12    /// Returns a new version 5 coinbase transaction for `network` and `height`,
13    /// which contains the specified `outputs`.
14    pub fn new_v5_coinbase(
15        network: &Network,
16        height: Height,
17        outputs: impl IntoIterator<Item = (Amount<NonNegative>, transparent::Script)>,
18        extra_coinbase_data: Vec<u8>,
19    ) -> Transaction {
20        // # Consensus
21        //
22        // These consensus rules apply to v5 coinbase transactions after NU5 activation:
23        //
24        // > If effectiveVersion ≥ 5 then this condition MUST hold:
25        // > tx_in_count > 0 or nSpendsSapling > 0 or
26        // > (nActionsOrchard > 0 and enableSpendsOrchard = 1).
27        //
28        // > A coinbase transaction for a block at block height greater than 0 MUST have
29        // > a script that, as its first item, encodes the block height as follows. ...
30        // > let heightBytes be the signed little-endian representation of height,
31        // > using the minimum nonzero number of bytes such that the most significant byte
32        // > is < 0x80. The length of heightBytes MUST be in the range {1 .. 5}.
33        // > Then the encoding is the length of heightBytes encoded as one byte,
34        // > followed by heightBytes itself. This matches the encoding used by Bitcoin
35        // > in the implementation of [BIP-34]
36        // > (but the description here is to be considered normative).
37        //
38        // > A coinbase transaction script MUST have length in {2 .. 100} bytes.
39        //
40        // Zebra adds extra coinbase data if configured to do so.
41        //
42        // Since we're not using a lock time, any sequence number is valid here.
43        // See `Transaction::lock_time()` for the relevant consensus rules.
44        //
45        // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
46        let inputs = vec![transparent::Input::new_coinbase(
47            height,
48            Some(extra_coinbase_data),
49            None,
50        )];
51
52        // > The block subsidy is composed of a miner subsidy and a series of funding streams.
53        //
54        // <https://zips.z.cash/protocol/protocol.pdf#subsidyconcepts>
55        //
56        // > The total value in zatoshi of transparent outputs from a coinbase transaction,
57        // > minus vbalanceSapling, minus vbalanceOrchard, MUST NOT be greater than
58        // > the value in zatoshi of block subsidy plus the transaction fees
59        // > paid by transactions in this block.
60        //
61        // > If effectiveVersion ≥ 5 then this condition MUST hold:
62        // > tx_out_count > 0 or nOutputsSapling > 0 or
63        // > (nActionsOrchard > 0 and enableOutputsOrchard = 1).
64        //
65        // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
66        let outputs: Vec<_> = outputs
67            .into_iter()
68            .map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
69            .collect();
70        assert!(
71            !outputs.is_empty(),
72            "invalid coinbase transaction: must have at least one output"
73        );
74
75        Transaction::V5 {
76            // > The transaction version number MUST be 4 or 5. ...
77            // > If the transaction version number is 5 then the version group ID
78            // > MUST be 0x26A7270A.
79            // > If effectiveVersion ≥ 5, the nConsensusBranchId field MUST match the consensus
80            // > branch ID used for SIGHASH transaction hashes, as specified in [ZIP-244].
81            network_upgrade: NetworkUpgrade::current(network, height),
82
83            // There is no documented consensus rule for the lock time field in coinbase
84            // transactions, so we just leave it unlocked. (We could also set it to `height`.)
85            lock_time: LockTime::unlocked(),
86
87            // > The nExpiryHeight field of a coinbase transaction MUST be equal to its
88            // > block height.
89            expiry_height: height,
90
91            inputs,
92            outputs,
93
94            // Zebra does not support shielded coinbase yet.
95            //
96            // > In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0.
97            // > In a version 5 transaction, the reserved bits 2 .. 7 of the flagsOrchard field
98            // > MUST be zero.
99            //
100            // See the Zcash spec for additional shielded coinbase consensus rules.
101            sapling_shielded_data: None,
102            orchard_shielded_data: None,
103        }
104    }
105
106    /// Returns a new version 4 coinbase transaction for `network` and `height`,
107    /// which contains the specified `outputs`.
108    ///
109    /// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
110    /// in the `getblocktemplate` RPC.
111    pub fn new_v4_coinbase(
112        _network: &Network,
113        height: Height,
114        outputs: impl IntoIterator<Item = (Amount<NonNegative>, transparent::Script)>,
115        like_zcashd: bool,
116        extra_coinbase_data: Vec<u8>,
117    ) -> Transaction {
118        let mut extra_data = None;
119        let mut sequence = None;
120
121        // `zcashd` includes an extra byte after the coinbase height in the coinbase data,
122        // and a sequence number of u32::MAX.
123        if like_zcashd {
124            extra_data = Some(vec![0x00]);
125            sequence = Some(u32::MAX);
126        }
127
128        // Override like_zcashd if extra_coinbase_data was supplied
129        if !extra_coinbase_data.is_empty() {
130            extra_data = Some(extra_coinbase_data);
131        }
132
133        // # Consensus
134        //
135        // See the other consensus rules above in new_v5_coinbase().
136        //
137        // > If effectiveVersion < 5, then at least one of tx_in_count, nSpendsSapling,
138        // > and nJoinSplit MUST be nonzero.
139        let inputs = vec![transparent::Input::new_coinbase(
140            height, extra_data, sequence,
141        )];
142
143        // > If effectiveVersion < 5, then at least one of tx_out_count, nOutputsSapling,
144        // > and nJoinSplit MUST be nonzero.
145        let outputs: Vec<_> = outputs
146            .into_iter()
147            .map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
148            .collect();
149        assert!(
150            !outputs.is_empty(),
151            "invalid coinbase transaction: must have at least one output"
152        );
153
154        // > The transaction version number MUST be 4 or 5. ...
155        // > If the transaction version number is 4 then the version group ID MUST be 0x892F2085.
156        Transaction::V4 {
157            lock_time: LockTime::unlocked(),
158
159            expiry_height: height,
160
161            inputs,
162            outputs,
163
164            // Zebra does not support shielded coinbase yet.
165            joinsplit_data: None,
166            sapling_shielded_data: None,
167        }
168    }
169}