diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 93a003c172..549aedf058 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -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 diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index bbfd3c98db..1396f64fec 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -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 } diff --git a/rlp/decode.go b/rlp/decode.go index 19074072fb..423a4b8d2b 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -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