simplify the encode and decode logic about sidecar

This commit is contained in:
maskpp 2025-08-17 16:37:49 +08:00
parent a9a19c4202
commit 22e0abd3a5
3 changed files with 47 additions and 148 deletions

View file

@ -946,8 +946,8 @@ func (pool *LegacyPool) addRemoteSync(tx *types.Transaction) error {
func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error {
// Filter out known ones without obtaining the pool lock or recovering signatures
var (
errs = make([]error, len(txs))
news = make([]*types.Transaction, 0, len(txs))
errs = make([]error, len(txs))
verifiedTxs int
)
for i, tx := range txs {
// If the transaction is known, pre-set the error slot
@ -965,26 +965,18 @@ func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error {
invalidTxMeter.Mark(1)
continue
}
// Accumulate all unknown transactions for deeper processing
news = append(news, tx)
verifiedTxs++
}
if len(news) == 0 {
if verifiedTxs == 0 {
return errs
}
// Process all the new transaction and merge any errors into the original slice
var dirtyAddrs *accountSet
pool.mu.Lock()
newErrs, dirtyAddrs := pool.addTxsLocked(news)
errs, dirtyAddrs = pool.addTxsLocked(txs, errs)
pool.mu.Unlock()
var nilSlot = 0
for _, err := range newErrs {
for errs[nilSlot] != nil {
nilSlot++
}
errs[nilSlot] = err
nilSlot++
}
// Reorg the pool internals if needed and return
done := pool.requestPromoteExecutables(dirtyAddrs)
if sync {
@ -995,10 +987,15 @@ func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error {
// addTxsLocked attempts to queue a batch of transactions if they are valid.
// The transaction pool lock must be held.
func (pool *LegacyPool) addTxsLocked(txs []*types.Transaction) ([]error, *accountSet) {
func (pool *LegacyPool) addTxsLocked(txs []*types.Transaction, errs []error) ([]error, *accountSet) {
dirty := newAccountSet(pool.signer)
errs := make([]error, len(txs))
if errs == nil {
errs = make([]error, len(txs))
}
for i, tx := range txs {
if errs[i] != nil {
continue
}
replaced, err := pool.add(tx)
errs[i] = err
if err == nil && !replaced {
@ -1439,7 +1436,7 @@ func (pool *LegacyPool) reset(oldHead, newHead *types.Header) {
// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
core.SenderCacher().Recover(pool.signer, reinject)
pool.addTxsLocked(reinject)
pool.addTxsLocked(reinject, nil)
}
// promoteExecutables moves transactions that have become processable from the

View file

@ -19,7 +19,6 @@ package types
import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
"math/big"
"slices"
@ -113,10 +112,7 @@ func (sc *BlobTxSidecar) CellProofsAt(idx int) ([]kzg4844.Proof, error) {
// ToV1 converts the BlobSidecar to version 1, attaching the cell proofs.
func (sc *BlobTxSidecar) ToV1() error {
if sc.Version == BlobSidecarVersion1 {
return nil
}
if sc.Version == BlobSidecarVersion0 {
if sc.Version != BlobSidecarVersion1 {
proofs := make([]kzg4844.Proof, 0, len(sc.Blobs)*kzg4844.CellProofsPerBlob)
for _, blob := range sc.Blobs {
cellProofs, err := kzg4844.ComputeCellProofs(&blob)
@ -183,48 +179,6 @@ type blobTxWithBlobs interface {
assign(*BlobTxSidecar) error
}
type blobTxWithBlobsV0 struct {
BlobTx *BlobTx
Blobs []kzg4844.Blob
Commitments []kzg4844.Commitment
Proofs []kzg4844.Proof
}
type blobTxWithBlobsV1 struct {
BlobTx *BlobTx
Version byte
Blobs []kzg4844.Blob
Commitments []kzg4844.Commitment
Proofs []kzg4844.Proof
}
func (btx *blobTxWithBlobsV0) tx() *BlobTx {
return btx.BlobTx
}
func (btx *blobTxWithBlobsV0) assign(sc *BlobTxSidecar) error {
sc.Version = BlobSidecarVersion0
sc.Blobs = btx.Blobs
sc.Commitments = btx.Commitments
sc.Proofs = btx.Proofs
return nil
}
func (btx *blobTxWithBlobsV1) tx() *BlobTx {
return btx.BlobTx
}
func (btx *blobTxWithBlobsV1) assign(sc *BlobTxSidecar) error {
if btx.Version != BlobSidecarVersion1 {
return fmt.Errorf("unsupported blob tx version %d", btx.Version)
}
sc.Version = BlobSidecarVersion1
sc.Blobs = btx.Blobs
sc.Commitments = btx.Commitments
sc.Proofs = btx.Proofs
return nil
}
// copy creates a deep copy of the transaction data and initializes all fields.
func (tx *BlobTx) copy() TxData {
cpy := &BlobTx{
@ -326,98 +280,29 @@ func (tx *BlobTx) withSidecar(sideCar *BlobTxSidecar) *BlobTx {
}
func (tx *BlobTx) encode(b *bytes.Buffer) error {
switch {
case tx.Sidecar == nil:
return rlp.Encode(b, tx)
case tx.Sidecar.Version == BlobSidecarVersion0:
return rlp.Encode(b, &blobTxWithBlobsV0{
BlobTx: tx,
Blobs: tx.Sidecar.Blobs,
Commitments: tx.Sidecar.Commitments,
Proofs: tx.Sidecar.Proofs,
})
case tx.Sidecar.Version == BlobSidecarVersion1:
return rlp.Encode(b, &blobTxWithBlobsV1{
BlobTx: tx,
Version: tx.Sidecar.Version,
Blobs: tx.Sidecar.Blobs,
Commitments: tx.Sidecar.Commitments,
Proofs: tx.Sidecar.Proofs,
})
default:
return errors.New("unsupported sidecar version")
if err := rlp.Encode(b, tx); err != nil {
return err
}
if tx.Sidecar != nil {
return rlp.Encode(b, tx.Sidecar)
}
return nil
}
func (tx *BlobTx) decode(input []byte) error {
// Here we need to support two outer formats: the network protocol encoding of the tx
// (with blobs) or the canonical encoding without blobs.
//
// The canonical encoding is just a list of fields:
//
// [chainID, nonce, ...]
//
// The network encoding is a list where the first element is the tx in the canonical encoding,
// and the remaining elements are the 'sidecar':
//
// [[chainID, nonce, ...], ...]
//
// The two outer encodings can be distinguished by checking whether the first element
// of the input list is itself a list. If it's the canonical encoding, the first
// element is the chainID, which is a number.
firstElem, _, err := rlp.SplitList(input)
bLen, err := rlp.DecodeBytesV1(input, tx)
if err != nil {
return err
}
firstElemKind, _, secondElem, err := rlp.Split(firstElem)
if err != nil {
return err
// If the input is shorter than the RLP-encoded transaction, it means
// that the transaction does not have a sidecar.
if len(input) > bLen {
var sidecar *BlobTxSidecar
if err = rlp.DecodeBytes(input[bLen:], &sidecar); err != nil {
return err
}
tx.Sidecar = sidecar
}
if firstElemKind != rlp.List {
// Blob tx without blobs.
return rlp.DecodeBytes(input, tx)
}
// Now we know it's the network encoding with the blob sidecar. Here we again need to
// support multiple encodings: legacy sidecars (v0) with a blob proof, and versioned
// sidecars.
//
// The legacy encoding is:
//
// [tx, blobs, commitments, proofs]
//
// The versioned encoding is:
//
// [tx, version, blobs, ...]
//
// We can tell the two apart by checking whether the second element is the version byte.
// For legacy sidecar the second element is a list of blobs.
secondElemKind, _, _, err := rlp.Split(secondElem)
if err != nil {
return err
}
var payload blobTxWithBlobs
if secondElemKind == rlp.List {
// No version byte: blob sidecar v0.
payload = new(blobTxWithBlobsV0)
} else {
// It has a version byte. Decode as v1, version is checked by assign()
payload = new(blobTxWithBlobsV1)
}
if err := rlp.DecodeBytes(input, payload); err != nil {
return err
}
sc := new(BlobTxSidecar)
if err := payload.assign(sc); err != nil {
return err
}
*tx = *payload.tx()
tx.Sidecar = sc
return nil
}

View file

@ -105,6 +105,23 @@ func DecodeBytes(b []byte, val interface{}) error {
return nil
}
// DecodeBytesV1 parses RLP data from b into val. Please see package-level documentation for
// the decoding rules. The input must contain exactly one value and no trailing data.
// DecodeBytesV1 is similar to DecodeBytes, but it returns the number of bytes read from b.
func DecodeBytesV1(b []byte, val interface{}) (int, error) {
r := (*sliceReader)(&b)
stream := streamPool.Get().(*Stream)
defer streamPool.Put(stream)
bLen := len(b)
stream.Reset(r, uint64(bLen))
if err := stream.Decode(val); err != nil {
return 0, err
}
return bLen - len(b), nil
}
type decodeError struct {
msg string
typ reflect.Type