@@ -19,6 +19,7 @@ package types
19
19
import (
20
20
"bytes"
21
21
"crypto/sha256"
22
+ "errors"
22
23
"fmt"
23
24
"math/big"
24
25
@@ -119,22 +120,55 @@ func (sc *BlobTxSidecar) ValidateBlobCommitmentHashes(hashes []common.Hash) erro
119
120
return nil
120
121
}
121
122
122
- // blobTxWithBlobs is used for encoding of transactions when blobs are present.
123
- type blobTxWithBlobs struct {
123
+ // blobTxWithBlobs represents blob tx with its corresponding sidecar.
124
+ // This is an interface because sidecars are versioned.
125
+ type blobTxWithBlobs interface {
126
+ tx () * BlobTx
127
+ assign (* BlobTxSidecar ) error
128
+ }
129
+
130
+ type blobTxWithBlobsV0 struct {
124
131
BlobTx * BlobTx
125
132
Blobs []kzg4844.Blob
126
133
Commitments []kzg4844.Commitment
127
134
Proofs []kzg4844.Proof
128
135
}
129
136
130
- type versionedBlobTxWithBlobs struct {
137
+ type blobTxWithBlobsV1 struct {
131
138
BlobTx * BlobTx
132
139
Version byte
133
140
Blobs []kzg4844.Blob
134
141
Commitments []kzg4844.Commitment
135
142
Proofs []kzg4844.Proof
136
143
}
137
144
145
+ func (btx * blobTxWithBlobsV0 ) tx () * BlobTx {
146
+ return btx .BlobTx
147
+ }
148
+
149
+ func (btx * blobTxWithBlobsV0 ) assign (sc * BlobTxSidecar ) error {
150
+ sc .Version = 0
151
+ sc .Blobs = btx .Blobs
152
+ sc .Commitments = btx .Commitments
153
+ sc .Proofs = btx .Proofs
154
+ return nil
155
+ }
156
+
157
+ func (btx * blobTxWithBlobsV1 ) tx () * BlobTx {
158
+ return btx .BlobTx
159
+ }
160
+
161
+ func (btx * blobTxWithBlobsV1 ) assign (sc * BlobTxSidecar ) error {
162
+ if btx .Version != 1 {
163
+ return fmt .Errorf ("unsupported blob tx version %d" , btx .Version )
164
+ }
165
+ sc .Version = 1
166
+ sc .Blobs = btx .Blobs
167
+ sc .Commitments = btx .Commitments
168
+ sc .Proofs = btx .Proofs
169
+ return nil
170
+ }
171
+
138
172
// copy creates a deep copy of the transaction data and initializes all fields.
139
173
func (tx * BlobTx ) copy () TxData {
140
174
cpy := & BlobTx {
@@ -240,69 +274,98 @@ func (tx *BlobTx) withSidecar(sideCar *BlobTxSidecar) *BlobTx {
240
274
}
241
275
242
276
func (tx * BlobTx ) encode (b * bytes.Buffer ) error {
243
- if tx .Sidecar == nil {
277
+ switch {
278
+ case tx .Sidecar == nil :
244
279
return rlp .Encode (b , tx )
245
- }
246
- // Encode a cell proof transaction
247
- if tx .Sidecar .Version != 0 {
248
- inner := & versionedBlobTxWithBlobs {
280
+
281
+ case tx .Sidecar .Version == 0 :
282
+ return rlp .Encode (b , & blobTxWithBlobsV0 {
283
+ BlobTx : tx ,
284
+ Blobs : tx .Sidecar .Blobs ,
285
+ Commitments : tx .Sidecar .Commitments ,
286
+ Proofs : tx .Sidecar .Proofs ,
287
+ })
288
+
289
+ case tx .Sidecar .Version == 1 :
290
+ return rlp .Encode (b , & blobTxWithBlobsV1 {
249
291
BlobTx : tx ,
250
292
Version : tx .Sidecar .Version ,
251
293
Blobs : tx .Sidecar .Blobs ,
252
294
Commitments : tx .Sidecar .Commitments ,
253
295
Proofs : tx .Sidecar .Proofs ,
254
- }
255
- return rlp .Encode (b , inner )
256
- }
257
- inner := & blobTxWithBlobs {
258
- BlobTx : tx ,
259
- Blobs : tx .Sidecar .Blobs ,
260
- Commitments : tx .Sidecar .Commitments ,
261
- Proofs : tx .Sidecar .Proofs ,
296
+ })
297
+
298
+ default :
299
+ return errors .New ("unsupported sidecar version" )
262
300
}
263
- return rlp .Encode (b , inner )
264
301
}
265
302
266
303
func (tx * BlobTx ) decode (input []byte ) error {
267
- // Here we need to support two formats: the network protocol encoding of the tx (with
268
- // blobs) or the canonical encoding without blobs.
304
+ // Here we need to support two outer formats: the network protocol encoding of the tx
305
+ // (with blobs) or the canonical encoding without blobs.
306
+ //
307
+ // The canonical encoding is just a list of fields:
308
+ //
309
+ // [chainID, nonce, ...]
310
+ //
311
+ // The network encoding is a list where the first element is the tx in the canonical encoding,
312
+ // and the remaining elements are the 'sidecar':
269
313
//
270
- // The two encodings can be distinguished by checking whether the first element of the
271
- // input list is itself a list.
314
+ // [[chainID, nonce, ...], ...]
315
+ //
316
+ // The two outer encodings can be distinguished by checking whether the first element
317
+ // of the input list is itself a list. If it's the canonical encoding, the first
318
+ // element is the chainID, which is a number.
272
319
273
- outerList , _ , err := rlp .SplitList (input )
320
+ firstElem , _ , err := rlp .SplitList (input )
274
321
if err != nil {
275
322
return err
276
323
}
277
- firstElemKind , _ , _ , err := rlp .Split (outerList )
324
+ firstElemKind , _ , secondElem , err := rlp .Split (firstElem )
278
325
if err != nil {
279
326
return err
280
327
}
281
-
282
328
if firstElemKind != rlp .List {
329
+ // Blob tx without blobs.
283
330
return rlp .DecodeBytes (input , tx )
284
331
}
285
332
286
- // It's a tx with blobs. Try to decode it as version 0.
287
- var inner versionedBlobTxWithBlobs
288
- if err := rlp .DecodeBytes (input , & inner ); err != nil {
289
- var innerV0 blobTxWithBlobs
290
- if err := rlp .DecodeBytes (input , & innerV0 ); err != nil {
291
- return err
292
- }
293
- inner .BlobTx = innerV0 .BlobTx
294
- inner .Version = 0
295
- inner .Blobs = innerV0 .Blobs
296
- inner .Commitments = innerV0 .Commitments
297
- inner .Proofs = innerV0 .Proofs
333
+ // Now we know it's the network encoding with the blob sidecar. Here we again need to
334
+ // support multiple encodings: legacy sidecars (v0) with a blob proof, and versioned
335
+ // sidecars.
336
+ //
337
+ // The legacy encoding is:
338
+ //
339
+ // [tx, blobs, commitments, proofs]
340
+ //
341
+ // The versioned encoding is:
342
+ //
343
+ // [tx, version, blobs, ...]
344
+ //
345
+ // We can tell the two apart by checking whether the second element is the version byte.
346
+ // For legacy sidecar the second element is a list of blobs.
347
+
348
+ secondElemKind , _ , _ , err := rlp .Split (secondElem )
349
+ if err != nil {
350
+ return err
351
+ }
352
+ var payload blobTxWithBlobs
353
+ if secondElemKind == rlp .List {
354
+ // No version byte: blob sidecar v0.
355
+ payload = new (blobTxWithBlobsV0 )
356
+ } else {
357
+ // It has a version byte. Decode as v1, version is checked by assign()
358
+ payload = new (blobTxWithBlobsV1 )
298
359
}
299
- * tx = * inner . BlobTx
300
- tx . Sidecar = & BlobTxSidecar {
301
- Version : inner . Version ,
302
- Blobs : inner . Blobs ,
303
- Commitments : inner . Commitments ,
304
- Proofs : inner . Proofs ,
360
+ if err := rlp . DecodeBytes ( input , payload ); err != nil {
361
+ return err
362
+ }
363
+ sc := new ( BlobTxSidecar )
364
+ if err := payload . assign ( sc ); err != nil {
365
+ return err
305
366
}
367
+ * tx = * payload .tx ()
368
+ tx .Sidecar = sc
306
369
return nil
307
370
}
308
371
0 commit comments