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}