@@ -18,6 +18,7 @@ package apitypes
18
18
19
19
import (
20
20
"bytes"
21
+ "crypto/sha256"
21
22
"encoding/json"
22
23
"errors"
23
24
"fmt"
@@ -34,6 +35,8 @@ import (
34
35
"github.com/ethereum/go-ethereum/common/math"
35
36
"github.com/ethereum/go-ethereum/core/types"
36
37
"github.com/ethereum/go-ethereum/crypto"
38
+ "github.com/ethereum/go-ethereum/crypto/kzg4844"
39
+ "github.com/holiman/uint256"
37
40
)
38
41
39
42
var typedDataReferenceTypeRegexp = regexp .MustCompile (`^[A-Za-z](\w*)(\[\])?$` )
@@ -92,12 +95,21 @@ type SendTxArgs struct {
92
95
// We accept "data" and "input" for backwards-compatibility reasons.
93
96
// "input" is the newer name and should be preferred by clients.
94
97
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
95
- Data * hexutil.Bytes `json:"data"`
98
+ Data * hexutil.Bytes `json:"data,omitempty "`
96
99
Input * hexutil.Bytes `json:"input,omitempty"`
97
100
98
101
// For non-legacy transactions
99
102
AccessList * types.AccessList `json:"accessList,omitempty"`
100
103
ChainID * hexutil.Big `json:"chainId,omitempty"`
104
+
105
+ // For BlobTxType
106
+ BlobFeeCap * hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
107
+ BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
108
+
109
+ // For BlobTxType transactions with blob sidecar
110
+ Blobs []kzg4844.Blob `json:"blobs,omitempty"`
111
+ Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
112
+ Proofs []kzg4844.Proof `json:"proofs,omitempty"`
101
113
}
102
114
103
115
func (args SendTxArgs ) String () string {
@@ -108,24 +120,56 @@ func (args SendTxArgs) String() string {
108
120
return err .Error ()
109
121
}
110
122
123
+ // data retrieves the transaction calldata. Input field is preferred.
124
+ func (args * SendTxArgs ) data () []byte {
125
+ if args .Input != nil {
126
+ return * args .Input
127
+ }
128
+ if args .Data != nil {
129
+ return * args .Data
130
+ }
131
+ return nil
132
+ }
133
+
111
134
// ToTransaction converts the arguments to a transaction.
112
- func (args * SendTxArgs ) ToTransaction () * types.Transaction {
135
+ func (args * SendTxArgs ) ToTransaction () ( * types.Transaction , error ) {
113
136
// Add the To-field, if specified
114
137
var to * common.Address
115
138
if args .To != nil {
116
139
dstAddr := args .To .Address ()
117
140
to = & dstAddr
118
141
}
119
-
120
- var input []byte
121
- if args .Input != nil {
122
- input = * args .Input
123
- } else if args .Data != nil {
124
- input = * args .Data
142
+ if err := args .validateTxSidecar (); err != nil {
143
+ return nil , err
125
144
}
126
-
127
145
var data types.TxData
128
146
switch {
147
+ case args .BlobHashes != nil :
148
+ al := types.AccessList {}
149
+ if args .AccessList != nil {
150
+ al = * args .AccessList
151
+ }
152
+ data = & types.BlobTx {
153
+ To : * to ,
154
+ ChainID : uint256 .MustFromBig ((* big .Int )(args .ChainID )),
155
+ Nonce : uint64 (args .Nonce ),
156
+ Gas : uint64 (args .Gas ),
157
+ GasFeeCap : uint256 .MustFromBig ((* big .Int )(args .MaxFeePerGas )),
158
+ GasTipCap : uint256 .MustFromBig ((* big .Int )(args .MaxPriorityFeePerGas )),
159
+ Value : uint256 .MustFromBig ((* big .Int )(& args .Value )),
160
+ Data : args .data (),
161
+ AccessList : al ,
162
+ BlobHashes : args .BlobHashes ,
163
+ BlobFeeCap : uint256 .MustFromBig ((* big .Int )(args .BlobFeeCap )),
164
+ }
165
+ if args .Blobs != nil {
166
+ data .(* types.BlobTx ).Sidecar = & types.BlobTxSidecar {
167
+ Blobs : args .Blobs ,
168
+ Commitments : args .Commitments ,
169
+ Proofs : args .Proofs ,
170
+ }
171
+ }
172
+
129
173
case args .MaxFeePerGas != nil :
130
174
al := types.AccessList {}
131
175
if args .AccessList != nil {
@@ -139,7 +183,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
139
183
GasFeeCap : (* big .Int )(args .MaxFeePerGas ),
140
184
GasTipCap : (* big .Int )(args .MaxPriorityFeePerGas ),
141
185
Value : (* big .Int )(& args .Value ),
142
- Data : input ,
186
+ Data : args . data () ,
143
187
AccessList : al ,
144
188
}
145
189
case args .AccessList != nil :
@@ -150,7 +194,7 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
150
194
Gas : uint64 (args .Gas ),
151
195
GasPrice : (* big .Int )(args .GasPrice ),
152
196
Value : (* big .Int )(& args .Value ),
153
- Data : input ,
197
+ Data : args . data () ,
154
198
AccessList : * args .AccessList ,
155
199
}
156
200
default :
@@ -160,10 +204,81 @@ func (args *SendTxArgs) ToTransaction() *types.Transaction {
160
204
Gas : uint64 (args .Gas ),
161
205
GasPrice : (* big .Int )(args .GasPrice ),
162
206
Value : (* big .Int )(& args .Value ),
163
- Data : input ,
207
+ Data : args . data () ,
164
208
}
165
209
}
166
- return types .NewTx (data )
210
+
211
+ return types .NewTx (data ), nil
212
+ }
213
+
214
+ // validateTxSidecar validates blob data, if present
215
+ func (args * SendTxArgs ) validateTxSidecar () error {
216
+ // No blobs, we're done.
217
+ if args .Blobs == nil {
218
+ return nil
219
+ }
220
+
221
+ n := len (args .Blobs )
222
+ // Assume user provides either only blobs (w/o hashes), or
223
+ // blobs together with commitments and proofs.
224
+ if args .Commitments == nil && args .Proofs != nil {
225
+ return errors .New (`blob proofs provided while commitments were not` )
226
+ } else if args .Commitments != nil && args .Proofs == nil {
227
+ return errors .New (`blob commitments provided while proofs were not` )
228
+ }
229
+
230
+ // len(blobs) == len(commitments) == len(proofs) == len(hashes)
231
+ if args .Commitments != nil && len (args .Commitments ) != n {
232
+ return fmt .Errorf ("number of blobs and commitments mismatch (have=%d, want=%d)" , len (args .Commitments ), n )
233
+ }
234
+ if args .Proofs != nil && len (args .Proofs ) != n {
235
+ return fmt .Errorf ("number of blobs and proofs mismatch (have=%d, want=%d)" , len (args .Proofs ), n )
236
+ }
237
+ if args .BlobHashes != nil && len (args .BlobHashes ) != n {
238
+ return fmt .Errorf ("number of blobs and hashes mismatch (have=%d, want=%d)" , len (args .BlobHashes ), n )
239
+ }
240
+
241
+ if args .Commitments == nil {
242
+ // Generate commitment and proof.
243
+ commitments := make ([]kzg4844.Commitment , n )
244
+ proofs := make ([]kzg4844.Proof , n )
245
+ for i , b := range args .Blobs {
246
+ c , err := kzg4844 .BlobToCommitment (b )
247
+ if err != nil {
248
+ return fmt .Errorf ("blobs[%d]: error computing commitment: %v" , i , err )
249
+ }
250
+ commitments [i ] = c
251
+ p , err := kzg4844 .ComputeBlobProof (b , c )
252
+ if err != nil {
253
+ return fmt .Errorf ("blobs[%d]: error computing proof: %v" , i , err )
254
+ }
255
+ proofs [i ] = p
256
+ }
257
+ args .Commitments = commitments
258
+ args .Proofs = proofs
259
+ } else {
260
+ for i , b := range args .Blobs {
261
+ if err := kzg4844 .VerifyBlobProof (b , args .Commitments [i ], args .Proofs [i ]); err != nil {
262
+ return fmt .Errorf ("failed to verify blob proof: %v" , err )
263
+ }
264
+ }
265
+ }
266
+
267
+ hashes := make ([]common.Hash , n )
268
+ hasher := sha256 .New ()
269
+ for i , c := range args .Commitments {
270
+ hashes [i ] = kzg4844 .CalcBlobHashV1 (hasher , & c )
271
+ }
272
+ if args .BlobHashes != nil {
273
+ for i , h := range hashes {
274
+ if h != args .BlobHashes [i ] {
275
+ return fmt .Errorf ("blob hash verification failed (have=%s, want=%s)" , args .BlobHashes [i ], h )
276
+ }
277
+ }
278
+ } else {
279
+ args .BlobHashes = hashes
280
+ }
281
+ return nil
167
282
}
168
283
169
284
type SigFormat struct {
0 commit comments