Skip to content

Commit 61cc5d0

Browse files
committed
core/types: improve encoding/decoding
1 parent 41634e6 commit 61cc5d0

File tree

1 file changed

+105
-42
lines changed

1 file changed

+105
-42
lines changed

core/types/tx_blob.go

Lines changed: 105 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package types
1919
import (
2020
"bytes"
2121
"crypto/sha256"
22+
"errors"
2223
"fmt"
2324
"math/big"
2425

@@ -119,22 +120,55 @@ func (sc *BlobTxSidecar) ValidateBlobCommitmentHashes(hashes []common.Hash) erro
119120
return nil
120121
}
121122

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 {
124131
BlobTx *BlobTx
125132
Blobs []kzg4844.Blob
126133
Commitments []kzg4844.Commitment
127134
Proofs []kzg4844.Proof
128135
}
129136

130-
type versionedBlobTxWithBlobs struct {
137+
type blobTxWithBlobsV1 struct {
131138
BlobTx *BlobTx
132139
Version byte
133140
Blobs []kzg4844.Blob
134141
Commitments []kzg4844.Commitment
135142
Proofs []kzg4844.Proof
136143
}
137144

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+
138172
// copy creates a deep copy of the transaction data and initializes all fields.
139173
func (tx *BlobTx) copy() TxData {
140174
cpy := &BlobTx{
@@ -240,69 +274,98 @@ func (tx *BlobTx) withSidecar(sideCar *BlobTxSidecar) *BlobTx {
240274
}
241275

242276
func (tx *BlobTx) encode(b *bytes.Buffer) error {
243-
if tx.Sidecar == nil {
277+
switch {
278+
case tx.Sidecar == nil:
244279
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{
249291
BlobTx: tx,
250292
Version: tx.Sidecar.Version,
251293
Blobs: tx.Sidecar.Blobs,
252294
Commitments: tx.Sidecar.Commitments,
253295
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")
262300
}
263-
return rlp.Encode(b, inner)
264301
}
265302

266303
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':
269313
//
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.
272319

273-
outerList, _, err := rlp.SplitList(input)
320+
firstElem, _, err := rlp.SplitList(input)
274321
if err != nil {
275322
return err
276323
}
277-
firstElemKind, _, _, err := rlp.Split(outerList)
324+
firstElemKind, _, secondElem, err := rlp.Split(firstElem)
278325
if err != nil {
279326
return err
280327
}
281-
282328
if firstElemKind != rlp.List {
329+
// Blob tx without blobs.
283330
return rlp.DecodeBytes(input, tx)
284331
}
285332

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)
298359
}
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
305366
}
367+
*tx = *payload.tx()
368+
tx.Sidecar = sc
306369
return nil
307370
}
308371

0 commit comments

Comments
 (0)