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 6 coinbase transaction for `network` and `height`,
13    /// which contains the specified `outputs`.
14    #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
15    pub fn new_v6_coinbase(
16        network: &Network,
17        height: Height,
18        outputs: impl IntoIterator<Item = (Amount<NonNegative>, transparent::Script)>,
19        miner_data: Vec<u8>,
20        zip233_amount: Option<Amount<NonNegative>>,
21    ) -> Transaction {
22        // # Consensus
23        //
24        // These consensus rules apply to v5 coinbase transactions after NU5 activation:
25        //
26        // > If effectiveVersion ≥ 5 then this condition MUST hold:
27        // > tx_in_count > 0 or nSpendsSapling > 0 or
28        // > (nActionsOrchard > 0 and enableSpendsOrchard = 1).
29        //
30        // > A coinbase transaction for a block at block height greater than 0 MUST have
31        // > a script that, as its first item, encodes the block height as follows. ...
32        // > let heightBytes be the signed little-endian representation of height,
33        // > using the minimum nonzero number of bytes such that the most significant byte
34        // > is < 0x80. The length of heightBytes MUST be in the range {1 .. 5}.
35        // > Then the encoding is the length of heightBytes encoded as one byte,
36        // > followed by heightBytes itself. This matches the encoding used by Bitcoin
37        // > in the implementation of [BIP-34]
38        // > (but the description here is to be considered normative).
39        //
40        // > A coinbase transaction script MUST have length in {2 .. 100} bytes.
41        //
42        // Zebra adds extra coinbase data if configured to do so.
43        //
44        // Since we're not using a lock time, any sequence number is valid here.
45        // See `Transaction::lock_time()` for the relevant consensus rules.
46        //
47        // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
48        let inputs = vec![transparent::Input::new_coinbase(height, miner_data, None)];
49
50        // > The block subsidy is composed of a miner subsidy and a series of funding streams.
51        //
52        // <https://zips.z.cash/protocol/protocol.pdf#subsidyconcepts>
53        //
54        // > The total value in zatoshi of transparent outputs from a coinbase transaction,
55        // > minus vbalanceSapling, minus vbalanceOrchard, MUST NOT be greater than
56        // > the value in zatoshi of block subsidy plus the transaction fees
57        // > paid by transactions in this block.
58        //
59        // > If effectiveVersion ≥ 5 then this condition MUST hold:
60        // > tx_out_count > 0 or nOutputsSapling > 0 or
61        // > (nActionsOrchard > 0 and enableOutputsOrchard = 1).
62        //
63        // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
64        let outputs: Vec<_> = outputs
65            .into_iter()
66            .map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
67            .collect();
68        assert!(
69            !outputs.is_empty(),
70            "invalid coinbase transaction: must have at least one output"
71        );
72
73        Transaction::V6 {
74            // > The transaction version number MUST be 4 or 5. ...
75            // > If the transaction version number is 5 then the version group ID
76            // > MUST be 0x26A7270A.
77            // > If effectiveVersion ≥ 5, the nConsensusBranchId field MUST match the consensus
78            // > branch ID used for SIGHASH transaction hashes, as specified in [ZIP-244].
79            network_upgrade: NetworkUpgrade::current(network, height),
80
81            // There is no documented consensus rule for the lock time field in coinbase
82            // transactions, so we just leave it unlocked. (We could also set it to `height`.)
83            lock_time: LockTime::unlocked(),
84
85            // > The nExpiryHeight field of a coinbase transaction MUST be equal to its
86            // > block height.
87            expiry_height: height,
88
89            // > The NSM zip233_amount field [ZIP-233] must be set. It must be >= 0.
90            zip233_amount: zip233_amount.unwrap_or(Amount::zero()),
91
92            inputs,
93            outputs,
94
95            // Zebra does not support shielded coinbase yet.
96            //
97            // > In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0.
98            // > In a version 5 transaction, the reserved bits 2 .. 7 of the flagsOrchard field
99            // > MUST be zero.
100            //
101            // See the Zcash spec for additional shielded coinbase consensus rules.
102            sapling_shielded_data: None,
103            orchard_shielded_data: None,
104        }
105    }
106
107    /// Returns a new version 5 coinbase transaction for `network` and `height`,
108    /// which contains the specified `outputs`.
109    pub fn new_v5_coinbase(
110        network: &Network,
111        height: Height,
112        outputs: impl IntoIterator<Item = (Amount<NonNegative>, transparent::Script)>,
113        miner_data: Vec<u8>,
114    ) -> Transaction {
115        // # Consensus
116        //
117        // These consensus rules apply to v5 coinbase transactions after NU5 activation:
118        //
119        // > If effectiveVersion ≥ 5 then this condition MUST hold:
120        // > tx_in_count > 0 or nSpendsSapling > 0 or
121        // > (nActionsOrchard > 0 and enableSpendsOrchard = 1).
122        //
123        // > A coinbase transaction for a block at block height greater than 0 MUST have
124        // > a script that, as its first item, encodes the block height as follows. ...
125        // > let heightBytes be the signed little-endian representation of height,
126        // > using the minimum nonzero number of bytes such that the most significant byte
127        // > is < 0x80. The length of heightBytes MUST be in the range {1 .. 5}.
128        // > Then the encoding is the length of heightBytes encoded as one byte,
129        // > followed by heightBytes itself. This matches the encoding used by Bitcoin
130        // > in the implementation of [BIP-34]
131        // > (but the description here is to be considered normative).
132        //
133        // > A coinbase transaction script MUST have length in {2 .. 100} bytes.
134        //
135        // Zebra adds extra coinbase data if configured to do so.
136        //
137        // Since we're not using a lock time, any sequence number is valid here.
138        // See `Transaction::lock_time()` for the relevant consensus rules.
139        //
140        // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
141        let inputs = vec![transparent::Input::new_coinbase(height, miner_data, None)];
142
143        // > The block subsidy is composed of a miner subsidy and a series of funding streams.
144        //
145        // <https://zips.z.cash/protocol/protocol.pdf#subsidyconcepts>
146        //
147        // > The total value in zatoshi of transparent outputs from a coinbase transaction,
148        // > minus vbalanceSapling, minus vbalanceOrchard, MUST NOT be greater than
149        // > the value in zatoshi of block subsidy plus the transaction fees
150        // > paid by transactions in this block.
151        //
152        // > If effectiveVersion ≥ 5 then this condition MUST hold:
153        // > tx_out_count > 0 or nOutputsSapling > 0 or
154        // > (nActionsOrchard > 0 and enableOutputsOrchard = 1).
155        //
156        // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
157        let outputs: Vec<_> = outputs
158            .into_iter()
159            .map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
160            .collect();
161
162        assert!(
163            !outputs.is_empty(),
164            "invalid coinbase transaction: must have at least one output"
165        );
166
167        Transaction::V5 {
168            // > The transaction version number MUST be 4 or 5. ...
169            // > If the transaction version number is 5 then the version group ID
170            // > MUST be 0x26A7270A.
171            // > If effectiveVersion ≥ 5, the nConsensusBranchId field MUST match the consensus
172            // > branch ID used for SIGHASH transaction hashes, as specified in [ZIP-244].
173            network_upgrade: NetworkUpgrade::current(network, height),
174
175            // There is no documented consensus rule for the lock time field in coinbase
176            // transactions, so we just leave it unlocked. (We could also set it to `height`.)
177            lock_time: LockTime::unlocked(),
178
179            // > The nExpiryHeight field of a coinbase transaction MUST be equal to its
180            // > block height.
181            expiry_height: height,
182
183            inputs,
184            outputs,
185
186            // Zebra does not support shielded coinbase yet.
187            //
188            // > In a version 5 coinbase transaction, the enableSpendsOrchard flag MUST be 0.
189            // > In a version 5 transaction, the reserved bits 2 .. 7 of the flagsOrchard field
190            // > MUST be zero.
191            //
192            // See the Zcash spec for additional shielded coinbase consensus rules.
193            sapling_shielded_data: None,
194            orchard_shielded_data: None,
195        }
196    }
197
198    /// Returns a new version 4 coinbase transaction for `network` and `height`,
199    /// which contains the specified `outputs`.
200    ///
201    /// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd`
202    /// in the `getblocktemplate` RPC.
203    pub fn new_v4_coinbase(
204        height: Height,
205        outputs: impl IntoIterator<Item = (Amount<NonNegative>, transparent::Script)>,
206        miner_data: Vec<u8>,
207    ) -> Transaction {
208        // # Consensus
209        //
210        // See the other consensus rules above in new_v5_coinbase().
211        //
212        // > If effectiveVersion < 5, then at least one of tx_in_count, nSpendsSapling,
213        // > and nJoinSplit MUST be nonzero.
214        let inputs = vec![transparent::Input::new_coinbase(
215            height,
216            miner_data,
217            // zcashd uses a sequence number of u32::MAX.
218            Some(u32::MAX),
219        )];
220
221        // > If effectiveVersion < 5, then at least one of tx_out_count, nOutputsSapling,
222        // > and nJoinSplit MUST be nonzero.
223        let outputs: Vec<_> = outputs
224            .into_iter()
225            .map(|(amount, lock_script)| transparent::Output::new_coinbase(amount, lock_script))
226            .collect();
227
228        assert!(
229            !outputs.is_empty(),
230            "invalid coinbase transaction: must have at least one output"
231        );
232
233        // > The transaction version number MUST be 4 or 5. ...
234        // > If the transaction version number is 4 then the version group ID MUST be 0x892F2085.
235        Transaction::V4 {
236            lock_time: LockTime::unlocked(),
237            expiry_height: height,
238            inputs,
239            outputs,
240            joinsplit_data: None,
241            sapling_shielded_data: None,
242        }
243    }
244}