Skip to main content

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