core/types: support for optional blob sidecar in BlobTx (#27841)

This PR removes the newly added txpool.Transaction wrapper type, and instead adds a way
of keeping the blob sidecar within types.Transaction. It's better this way because most
code in go-ethereum does not care about blob transactions, and probably never will. This
will start mattering especially on the client side of RPC, where all APIs are based on
types.Transaction. Users need to be able to use the same signing flows they already
have.

However, since blobs are only allowed in some places but not others, we will now need to
add checks to avoid creating invalid blocks. I'm still trying to figure out the best place
to do some of these. The way I have it currently is as follows:

- In block validation (import), txs are verified not to have a blob sidecar.
- In miner, we strip off the sidecar when committing the transaction into the block.
- In TxPool validation, txs must have a sidecar to be added into the blobpool.
  - Note there is a special case here: when transactions are re-added because of a chain
    reorg, we cannot use the transactions gathered from the old chain blocks as-is,
    because they will be missing their blobs. This was previously handled by storing the
    blobs into the 'blobpool limbo'. The code has now changed to store the full
    transaction in the limbo instead, but it might be confusing for code readers why we're
    not simply adding the types.Transaction we already have.

Code changes summary:

- txpool.Transaction removed and all uses replaced by types.Transaction again
- blobpool now stores types.Transaction instead of defining its own blobTx format for storage
- the blobpool limbo now stores types.Transaction instead of storing only the blobs
- checks to validate the presence/absence of the blob sidecar added in certain critical places
This commit is contained in:
Felix Lange 2023-08-14 10:13:34 +02:00 committed by GitHub
parent 68860063fb
commit 2a6beb6a39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 627 additions and 374 deletions

View File

@ -68,6 +68,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash { if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch (header value %x, calculated %x)", header.TxHash, hash) return fmt.Errorf("transaction root hash mismatch (header value %x, calculated %x)", header.TxHash, hash)
} }
// Withdrawals are present after the Shanghai fork. // Withdrawals are present after the Shanghai fork.
if header.WithdrawalsHash != nil { if header.WithdrawalsHash != nil {
// Withdrawals list must be present in body after Shanghai. // Withdrawals list must be present in body after Shanghai.
@ -81,14 +82,23 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Withdrawals are not allowed prior to Shanghai fork // Withdrawals are not allowed prior to Shanghai fork
return errors.New("withdrawals present in block body") return errors.New("withdrawals present in block body")
} }
// Blob transactions may be present after the Cancun fork. // Blob transactions may be present after the Cancun fork.
var blobs int var blobs int
for _, tx := range block.Transactions() { for i, tx := range block.Transactions() {
// Count the number of blobs to validate against the header's blobGasUsed // Count the number of blobs to validate against the header's blobGasUsed
blobs += len(tx.BlobHashes()) blobs += len(tx.BlobHashes())
// If the tx is a blob tx, it must NOT have a sidecar attached to be valid in a block.
if tx.BlobTxSidecar() != nil {
return fmt.Errorf("unexpected blob sidecar in transaction at index %d", i)
}
// The individual checks for blob validity (version-check + not empty) // The individual checks for blob validity (version-check + not empty)
// happens in the state_transition check. // happens in StateTransition.
} }
// Check blob gas usage.
if header.BlobGasUsed != nil { if header.BlobGasUsed != nil {
if want := *header.BlobGasUsed / params.BlobTxBlobGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated if want := *header.BlobGasUsed / params.BlobTxBlobGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated
return fmt.Errorf("blob gas used mismatch (header %v, calculated %v)", *header.BlobGasUsed, blobs*params.BlobTxBlobGasPerBlob) return fmt.Errorf("blob gas used mismatch (header %v, calculated %v)", *header.BlobGasUsed, blobs*params.BlobTxBlobGasPerBlob)
@ -98,6 +108,8 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return errors.New("data blobs present in block body") return errors.New("data blobs present in block body")
} }
} }
// Ancestor block must be known.
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor return consensus.ErrUnknownAncestor

View File

@ -1085,19 +1085,30 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
ancientReceipts, liveReceipts []types.Receipts ancientReceipts, liveReceipts []types.Receipts
) )
// Do a sanity check that the provided chain is actually ordered and linked // Do a sanity check that the provided chain is actually ordered and linked
for i := 0; i < len(blockChain); i++ { for i, block := range blockChain {
if i != 0 { if i != 0 {
if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() { prev := blockChain[i-1]
log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(), if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
"prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash()) log.Error("Non contiguous receipt insert",
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, blockChain[i-1].NumberU64(), "number", block.Number(), "hash", block.Hash(), "parent", block.ParentHash(),
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4]) "prevnumber", prev.Number(), "prevhash", prev.Hash())
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])",
i-1, prev.NumberU64(), prev.Hash().Bytes()[:4],
i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4])
} }
} }
if blockChain[i].NumberU64() <= ancientLimit { if block.NumberU64() <= ancientLimit {
ancientBlocks, ancientReceipts = append(ancientBlocks, blockChain[i]), append(ancientReceipts, receiptChain[i]) ancientBlocks, ancientReceipts = append(ancientBlocks, block), append(ancientReceipts, receiptChain[i])
} else { } else {
liveBlocks, liveReceipts = append(liveBlocks, blockChain[i]), append(liveReceipts, receiptChain[i]) liveBlocks, liveReceipts = append(liveBlocks, block), append(liveReceipts, receiptChain[i])
}
// Here we also validate that blob transactions in the block do not contain a sidecar.
// While the sidecar does not affect the block hash / tx hash, sending blobs within a block is not allowed.
for txIndex, tx := range block.Transactions() {
if tx.Type() == types.BlobTxType && tx.BlobTxSidecar() != nil {
return 0, fmt.Errorf("block #%d contains unexpected blob sidecar in tx at index %d", block.NumberU64(), txIndex)
}
} }
} }

View File

@ -19,6 +19,7 @@ package blobpool
import ( import (
"container/heap" "container/heap"
"errors"
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
@ -35,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
@ -83,16 +83,6 @@ const (
limboedTransactionStore = "limbo" limboedTransactionStore = "limbo"
) )
// blobTx is a wrapper around types.BlobTx which also contains the literal blob
// data along with all the transaction metadata.
type blobTx struct {
Tx *types.Transaction
Blobs []kzg4844.Blob
Commits []kzg4844.Commitment
Proofs []kzg4844.Proof
}
// blobTxMeta is the minimal subset of types.BlobTx necessary to validate and // blobTxMeta is the minimal subset of types.BlobTx necessary to validate and
// schedule the blob transactions into the following blocks. Only ever add the // schedule the blob transactions into the following blocks. Only ever add the
// bare minimum needed fields to keep the size down (and thus number of entries // bare minimum needed fields to keep the size down (and thus number of entries
@ -455,22 +445,27 @@ func (p *BlobPool) Close() error {
// parseTransaction is a callback method on pool creation that gets called for // parseTransaction is a callback method on pool creation that gets called for
// each transaction on disk to create the in-memory metadata index. // each transaction on disk to create the in-memory metadata index.
func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error {
item := new(blobTx) tx := new(types.Transaction)
if err := rlp.DecodeBytes(blob, item); err != nil { if err := rlp.DecodeBytes(blob, tx); err != nil {
// This path is impossible unless the disk data representation changes // This path is impossible unless the disk data representation changes
// across restarts. For that ever unprobable case, recover gracefully // across restarts. For that ever unprobable case, recover gracefully
// by ignoring this data entry. // by ignoring this data entry.
log.Error("Failed to decode blob pool entry", "id", id, "err", err) log.Error("Failed to decode blob pool entry", "id", id, "err", err)
return err return err
} }
meta := newBlobTxMeta(id, size, item.Tx) if tx.BlobTxSidecar() == nil {
log.Error("Missing sidecar in blob pool entry", "id", id, "hash", tx.Hash())
return errors.New("missing blob sidecar")
}
sender, err := p.signer.Sender(item.Tx) meta := newBlobTxMeta(id, size, tx)
sender, err := p.signer.Sender(tx)
if err != nil { if err != nil {
// This path is impossible unless the signature validity changes across // This path is impossible unless the signature validity changes across
// restarts. For that ever unprobable case, recover gracefully by ignoring // restarts. For that ever unprobable case, recover gracefully by ignoring
// this data entry. // this data entry.
log.Error("Failed to recover blob tx sender", "id", id, "hash", item.Tx.Hash(), "err", err) log.Error("Failed to recover blob tx sender", "id", id, "hash", tx.Hash(), "err", err)
return err return err
} }
if _, ok := p.index[sender]; !ok { if _, ok := p.index[sender]; !ok {
@ -718,17 +713,17 @@ func (p *BlobPool) offload(addr common.Address, nonce uint64, id uint64, inclusi
log.Error("Blobs missing for included transaction", "from", addr, "nonce", nonce, "id", id, "err", err) log.Error("Blobs missing for included transaction", "from", addr, "nonce", nonce, "id", id, "err", err)
return return
} }
item := new(blobTx) var tx types.Transaction
if err = rlp.DecodeBytes(data, item); err != nil { if err = rlp.DecodeBytes(data, tx); err != nil {
log.Error("Blobs corrupted for included transaction", "from", addr, "nonce", nonce, "id", id, "err", err) log.Error("Blobs corrupted for included transaction", "from", addr, "nonce", nonce, "id", id, "err", err)
return return
} }
block, ok := inclusions[item.Tx.Hash()] block, ok := inclusions[tx.Hash()]
if !ok { if !ok {
log.Warn("Blob transaction swapped out by signer", "from", addr, "nonce", nonce, "id", id) log.Warn("Blob transaction swapped out by signer", "from", addr, "nonce", nonce, "id", id)
return return
} }
if err := p.limbo.push(item.Tx.Hash(), block, item.Blobs, item.Commits, item.Proofs); err != nil { if err := p.limbo.push(&tx, block); err != nil {
log.Warn("Failed to offload blob tx into limbo", "err", err) log.Warn("Failed to offload blob tx into limbo", "err", err)
return return
} }
@ -760,7 +755,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
for addr, txs := range reinject { for addr, txs := range reinject {
// Blindly push all the lost transactions back into the pool // Blindly push all the lost transactions back into the pool
for _, tx := range txs { for _, tx := range txs {
p.reinject(addr, tx) p.reinject(addr, tx.Hash())
} }
// Recheck the account's pooled transactions to drop included and // Recheck the account's pooled transactions to drop included and
// invalidated one // invalidated one
@ -920,16 +915,19 @@ func (p *BlobPool) reorg(oldHead, newHead *types.Header) (map[common.Address][]*
// Note, the method will not initialize the eviction cache values as those will // Note, the method will not initialize the eviction cache values as those will
// be done once for all transactions belonging to an account after all individual // be done once for all transactions belonging to an account after all individual
// transactions are injected back into the pool. // transactions are injected back into the pool.
func (p *BlobPool) reinject(addr common.Address, tx *types.Transaction) { func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) {
// Retrieve the associated blob from the limbo. Without the blobs, we cannot // Retrieve the associated blob from the limbo. Without the blobs, we cannot
// add the transaction back into the pool as it is not mineable. // add the transaction back into the pool as it is not mineable.
blobs, commits, proofs, err := p.limbo.pull(tx.Hash()) tx, err := p.limbo.pull(txhash)
if err != nil { if err != nil {
log.Error("Blobs unavailable, dropping reorged tx", "err", err) log.Error("Blobs unavailable, dropping reorged tx", "err", err)
return return
} }
// Serialize the transaction back into the primary datastore // TODO: seems like an easy optimization here would be getting the serialized tx
blob, err := rlp.EncodeToBytes(&blobTx{Tx: tx, Blobs: blobs, Commits: commits, Proofs: proofs}) // from limbo instead of re-serializing it here.
// Serialize the transaction back into the primary datastore.
blob, err := rlp.EncodeToBytes(tx)
if err != nil { if err != nil {
log.Error("Failed to encode transaction for storage", "hash", tx.Hash(), "err", err) log.Error("Failed to encode transaction for storage", "hash", tx.Hash(), "err", err)
return return
@ -939,9 +937,9 @@ func (p *BlobPool) reinject(addr common.Address, tx *types.Transaction) {
log.Error("Failed to write transaction into storage", "hash", tx.Hash(), "err", err) log.Error("Failed to write transaction into storage", "hash", tx.Hash(), "err", err)
return return
} }
// Update the indixes and metrics // Update the indixes and metrics
meta := newBlobTxMeta(id, p.store.Size(id), tx) meta := newBlobTxMeta(id, p.store.Size(id), tx)
if _, ok := p.index[addr]; !ok { if _, ok := p.index[addr]; !ok {
if err := p.reserve(addr, true); err != nil { if err := p.reserve(addr, true); err != nil {
log.Warn("Failed to reserve account for blob pool", "tx", tx.Hash(), "from", addr, "err", err) log.Warn("Failed to reserve account for blob pool", "tx", tx.Hash(), "from", addr, "err", err)
@ -1023,7 +1021,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
// validateTx checks whether a transaction is valid according to the consensus // validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size). // rules and adheres to some heuristic limits of the local node (price and size).
func (p *BlobPool) validateTx(tx *types.Transaction, blobs []kzg4844.Blob, commits []kzg4844.Commitment, proofs []kzg4844.Proof) error { func (p *BlobPool) validateTx(tx *types.Transaction) error {
// Ensure the transaction adheres to basic pool filters (type, size, tip) and // Ensure the transaction adheres to basic pool filters (type, size, tip) and
// consensus rules // consensus rules
baseOpts := &txpool.ValidationOptions{ baseOpts := &txpool.ValidationOptions{
@ -1032,7 +1030,7 @@ func (p *BlobPool) validateTx(tx *types.Transaction, blobs []kzg4844.Blob, commi
MaxSize: txMaxSize, MaxSize: txMaxSize,
MinTip: p.gasTip.ToBig(), MinTip: p.gasTip.ToBig(),
} }
if err := txpool.ValidateTransaction(tx, blobs, commits, proofs, p.head, p.signer, baseOpts); err != nil { if err := txpool.ValidateTransaction(tx, p.head, p.signer, baseOpts); err != nil {
return err return err
} }
// Ensure the transaction adheres to the stateful pool filters (nonce, balance) // Ensure the transaction adheres to the stateful pool filters (nonce, balance)
@ -1117,7 +1115,7 @@ func (p *BlobPool) Has(hash common.Hash) bool {
} }
// Get returns a transaction if it is contained in the pool, or nil otherwise. // Get returns a transaction if it is contained in the pool, or nil otherwise.
func (p *BlobPool) Get(hash common.Hash) *txpool.Transaction { func (p *BlobPool) Get(hash common.Hash) *types.Transaction {
// Track the amount of time waiting to retrieve a fully resolved blob tx from // Track the amount of time waiting to retrieve a fully resolved blob tx from
// the pool and the amount of time actually spent on pulling the data from disk. // the pool and the amount of time actually spent on pulling the data from disk.
getStart := time.Now() getStart := time.Now()
@ -1139,32 +1137,27 @@ func (p *BlobPool) Get(hash common.Hash) *txpool.Transaction {
log.Error("Tracked blob transaction missing from store", "hash", hash, "id", id, "err", err) log.Error("Tracked blob transaction missing from store", "hash", hash, "id", id, "err", err)
return nil return nil
} }
item := new(blobTx) item := new(types.Transaction)
if err = rlp.DecodeBytes(data, item); err != nil { if err = rlp.DecodeBytes(data, item); err != nil {
log.Error("Blobs corrupted for traced transaction", "hash", hash, "id", id, "err", err) log.Error("Blobs corrupted for traced transaction", "hash", hash, "id", id, "err", err)
return nil return nil
} }
return &txpool.Transaction{ return item
Tx: item.Tx,
BlobTxBlobs: item.Blobs,
BlobTxCommits: item.Commits,
BlobTxProofs: item.Proofs,
}
} }
// Add inserts a set of blob transactions into the pool if they pass validation (both // Add inserts a set of blob transactions into the pool if they pass validation (both
// consensus validity and pool restictions). // consensus validity and pool restictions).
func (p *BlobPool) Add(txs []*txpool.Transaction, local bool, sync bool) []error { func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
errs := make([]error, len(txs)) errs := make([]error, len(txs))
for i, tx := range txs { for i, tx := range txs {
errs[i] = p.add(tx.Tx, tx.BlobTxBlobs, tx.BlobTxCommits, tx.BlobTxProofs) errs[i] = p.add(tx)
} }
return errs return errs
} }
// Add inserts a new blob transaction into the pool if it passes validation (both // Add inserts a new blob transaction into the pool if it passes validation (both
// consensus validity and pool restictions). // consensus validity and pool restictions).
func (p *BlobPool) add(tx *types.Transaction, blobs []kzg4844.Blob, commits []kzg4844.Commitment, proofs []kzg4844.Proof) (err error) { func (p *BlobPool) add(tx *types.Transaction) (err error) {
// The blob pool blocks on adding a transaction. This is because blob txs are // The blob pool blocks on adding a transaction. This is because blob txs are
// only even pulled form the network, so this method will act as the overload // only even pulled form the network, so this method will act as the overload
// protection for fetches. // protection for fetches.
@ -1178,7 +1171,7 @@ func (p *BlobPool) add(tx *types.Transaction, blobs []kzg4844.Blob, commits []kz
}(time.Now()) }(time.Now())
// Ensure the transaction is valid from all perspectives // Ensure the transaction is valid from all perspectives
if err := p.validateTx(tx, blobs, commits, proofs); err != nil { if err := p.validateTx(tx); err != nil {
log.Trace("Transaction validation failed", "hash", tx.Hash(), "err", err) log.Trace("Transaction validation failed", "hash", tx.Hash(), "err", err)
return err return err
} }
@ -1203,7 +1196,7 @@ func (p *BlobPool) add(tx *types.Transaction, blobs []kzg4844.Blob, commits []kz
} }
// Transaction permitted into the pool from a nonce and cost perspective, // Transaction permitted into the pool from a nonce and cost perspective,
// insert it into the database and update the indices // insert it into the database and update the indices
blob, err := rlp.EncodeToBytes(&blobTx{Tx: tx, Blobs: blobs, Commits: commits, Proofs: proofs}) blob, err := rlp.EncodeToBytes(tx)
if err != nil { if err != nil {
log.Error("Failed to encode transaction for storage", "hash", tx.Hash(), "err", err) log.Error("Failed to encode transaction for storage", "hash", tx.Hash(), "err", err)
return err return err

View File

@ -193,8 +193,8 @@ func makeAddressReserver() txpool.AddressReserver {
// with a valid key, only setting the interesting fields from the perspective of // with a valid key, only setting the interesting fields from the perspective of
// the blob pool. // the blob pool.
func makeTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, key *ecdsa.PrivateKey) *types.Transaction { func makeTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, key *ecdsa.PrivateKey) *types.Transaction {
tx, _ := types.SignNewTx(key, types.LatestSigner(testChainConfig), makeUnsignedTx(nonce, gasTipCap, gasFeeCap, blobFeeCap)) blobtx := makeUnsignedTx(nonce, gasTipCap, gasFeeCap, blobFeeCap)
return tx return types.MustSignNewTx(key, types.LatestSigner(testChainConfig), blobtx)
} }
// makeUnsignedTx is a utility method to construct a random blob tranasaction // makeUnsignedTx is a utility method to construct a random blob tranasaction
@ -209,6 +209,11 @@ func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap
BlobFeeCap: uint256.NewInt(blobFeeCap), BlobFeeCap: uint256.NewInt(blobFeeCap),
BlobHashes: []common.Hash{emptyBlobVHash}, BlobHashes: []common.Hash{emptyBlobVHash},
Value: uint256.NewInt(100), Value: uint256.NewInt(100),
Sidecar: &types.BlobTxSidecar{
Blobs: []kzg4844.Blob{emptyBlob},
Commitments: []kzg4844.Commitment{emptyBlobCommit},
Proofs: []kzg4844.Proof{emptyBlobProof},
},
} }
} }
@ -341,7 +346,7 @@ func TestOpenDrops(t *testing.T) {
R: new(uint256.Int), R: new(uint256.Int),
S: new(uint256.Int), S: new(uint256.Int),
}) })
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
badsig, _ := store.Put(blob) badsig, _ := store.Put(blob)
// Insert a sequence of transactions with a nonce gap in between to verify // Insert a sequence of transactions with a nonce gap in between to verify
@ -354,7 +359,7 @@ func TestOpenDrops(t *testing.T) {
) )
for _, nonce := range []uint64{0, 1, 3, 4, 6, 7} { // first gap at #2, another at #5 for _, nonce := range []uint64{0, 1, 3, 4, 6, 7} { // first gap at #2, another at #5
tx := makeTx(nonce, 1, 1, 1, gapper) tx := makeTx(nonce, 1, 1, 1, gapper)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
if nonce < 2 { if nonce < 2 {
@ -371,7 +376,7 @@ func TestOpenDrops(t *testing.T) {
) )
for _, nonce := range []uint64{1, 2, 3} { // first gap at #0, all set dangling for _, nonce := range []uint64{1, 2, 3} { // first gap at #0, all set dangling
tx := makeTx(nonce, 1, 1, 1, dangler) tx := makeTx(nonce, 1, 1, 1, dangler)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
dangling[id] = struct{}{} dangling[id] = struct{}{}
@ -384,7 +389,7 @@ func TestOpenDrops(t *testing.T) {
) )
for _, nonce := range []uint64{0, 1, 2} { // account nonce at 3, all set filled for _, nonce := range []uint64{0, 1, 2} { // account nonce at 3, all set filled
tx := makeTx(nonce, 1, 1, 1, filler) tx := makeTx(nonce, 1, 1, 1, filler)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
filled[id] = struct{}{} filled[id] = struct{}{}
@ -397,7 +402,7 @@ func TestOpenDrops(t *testing.T) {
) )
for _, nonce := range []uint64{0, 1, 2, 3} { // account nonce at 2, half filled for _, nonce := range []uint64{0, 1, 2, 3} { // account nonce at 2, half filled
tx := makeTx(nonce, 1, 1, 1, overlapper) tx := makeTx(nonce, 1, 1, 1, overlapper)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
if nonce >= 2 { if nonce >= 2 {
@ -419,7 +424,7 @@ func TestOpenDrops(t *testing.T) {
} else { } else {
tx = makeTx(uint64(i), 1, 1, 1, underpayer) tx = makeTx(uint64(i), 1, 1, 1, underpayer)
} }
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
underpaid[id] = struct{}{} underpaid[id] = struct{}{}
@ -438,7 +443,7 @@ func TestOpenDrops(t *testing.T) {
} else { } else {
tx = makeTx(uint64(i), 1, 1, 1, outpricer) tx = makeTx(uint64(i), 1, 1, 1, outpricer)
} }
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
if i < 2 { if i < 2 {
@ -460,7 +465,7 @@ func TestOpenDrops(t *testing.T) {
} else { } else {
tx = makeTx(nonce, 1, 1, 1, exceeder) tx = makeTx(nonce, 1, 1, 1, exceeder)
} }
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
exceeded[id] = struct{}{} exceeded[id] = struct{}{}
@ -478,7 +483,7 @@ func TestOpenDrops(t *testing.T) {
} else { } else {
tx = makeTx(nonce, 1, 1, 1, overdrafter) tx = makeTx(nonce, 1, 1, 1, overdrafter)
} }
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob) id, _ := store.Put(blob)
if nonce < 1 { if nonce < 1 {
@ -494,7 +499,7 @@ func TestOpenDrops(t *testing.T) {
overcapped = make(map[uint64]struct{}) overcapped = make(map[uint64]struct{})
) )
for nonce := uint64(0); nonce < maxTxsPerAccount+3; nonce++ { for nonce := uint64(0); nonce < maxTxsPerAccount+3; nonce++ {
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: makeTx(nonce, 1, 1, 1, overcapper)}) blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, 1, 1, overcapper))
id, _ := store.Put(blob) id, _ := store.Put(blob)
if nonce < maxTxsPerAccount { if nonce < maxTxsPerAccount {
@ -625,7 +630,7 @@ func TestOpenIndex(t *testing.T) {
) )
for _, i := range []int{5, 3, 4, 2, 0, 1} { // Randomize the tx insertion order to force sorting on load for _, i := range []int{5, 3, 4, 2, 0, 1} { // Randomize the tx insertion order to force sorting on load
tx := makeTx(uint64(i), txExecTipCaps[i], txExecFeeCaps[i], txBlobFeeCaps[i], key) tx := makeTx(uint64(i), txExecTipCaps[i], txExecFeeCaps[i], txBlobFeeCaps[i], key)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx}) blob, _ := rlp.EncodeToBytes(tx)
store.Put(blob) store.Put(blob)
} }
store.Close() store.Close()
@ -718,9 +723,9 @@ func TestOpenHeap(t *testing.T) {
tx2 = makeTx(0, 1, 800, 70, key2) tx2 = makeTx(0, 1, 800, 70, key2)
tx3 = makeTx(0, 1, 1500, 110, key3) tx3 = makeTx(0, 1, 1500, 110, key3)
blob1, _ = rlp.EncodeToBytes(&blobTx{Tx: tx1}) blob1, _ = rlp.EncodeToBytes(tx1)
blob2, _ = rlp.EncodeToBytes(&blobTx{Tx: tx2}) blob2, _ = rlp.EncodeToBytes(tx2)
blob3, _ = rlp.EncodeToBytes(&blobTx{Tx: tx3}) blob3, _ = rlp.EncodeToBytes(tx3)
heapOrder = []common.Address{addr2, addr1, addr3} heapOrder = []common.Address{addr2, addr1, addr3}
heapIndex = map[common.Address]int{addr2: 0, addr1: 1, addr3: 2} heapIndex = map[common.Address]int{addr2: 0, addr1: 1, addr3: 2}
@ -794,9 +799,9 @@ func TestOpenCap(t *testing.T) {
tx2 = makeTx(0, 1, 800, 70, key2) tx2 = makeTx(0, 1, 800, 70, key2)
tx3 = makeTx(0, 1, 1500, 110, key3) tx3 = makeTx(0, 1, 1500, 110, key3)
blob1, _ = rlp.EncodeToBytes(&blobTx{Tx: tx1, Blobs: []kzg4844.Blob{emptyBlob}, Commits: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}}) blob1, _ = rlp.EncodeToBytes(tx1)
blob2, _ = rlp.EncodeToBytes(&blobTx{Tx: tx2, Blobs: []kzg4844.Blob{emptyBlob}, Commits: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}}) blob2, _ = rlp.EncodeToBytes(tx2)
blob3, _ = rlp.EncodeToBytes(&blobTx{Tx: tx3, Blobs: []kzg4844.Blob{emptyBlob}, Commits: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}}) blob3, _ = rlp.EncodeToBytes(tx3)
keep = []common.Address{addr1, addr3} keep = []common.Address{addr1, addr3}
drop = []common.Address{addr2} drop = []common.Address{addr2}
@ -1210,10 +1215,8 @@ func TestAdd(t *testing.T) {
// Sign the seed transactions and store them in the data store // Sign the seed transactions and store them in the data store
for _, tx := range seed.txs { for _, tx := range seed.txs {
var ( signed := types.MustSignNewTx(keys[acc], types.LatestSigner(testChainConfig), tx)
signed, _ = types.SignNewTx(keys[acc], types.LatestSigner(testChainConfig), tx) blob, _ := rlp.EncodeToBytes(signed)
blob, _ = rlp.EncodeToBytes(&blobTx{Tx: signed, Blobs: []kzg4844.Blob{emptyBlob}, Commits: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}})
)
store.Put(blob) store.Put(blob)
} }
} }
@ -1236,7 +1239,7 @@ func TestAdd(t *testing.T) {
// Add each transaction one by one, verifying the pool internals in between // Add each transaction one by one, verifying the pool internals in between
for j, add := range tt.adds { for j, add := range tt.adds {
signed, _ := types.SignNewTx(keys[add.from], types.LatestSigner(testChainConfig), add.tx) signed, _ := types.SignNewTx(keys[add.from], types.LatestSigner(testChainConfig), add.tx)
if err := pool.add(signed, []kzg4844.Blob{emptyBlob}, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}); !errors.Is(err, add.err) { if err := pool.add(signed); !errors.Is(err, add.err) {
t.Errorf("test %d, tx %d: adding transaction error mismatch: have %v, want %v", i, j, err, add.err) t.Errorf("test %d, tx %d: adding transaction error mismatch: have %v, want %v", i, j, err, add.err)
} }
verifyPoolInternals(t, pool) verifyPoolInternals(t, pool)

View File

@ -21,7 +21,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/billy" "github.com/holiman/billy"
@ -31,12 +30,9 @@ import (
// to which it belongs as well as the block number in which it was included for // to which it belongs as well as the block number in which it was included for
// finality eviction. // finality eviction.
type limboBlob struct { type limboBlob struct {
Owner common.Hash // Owner transaction's hash to support resurrecting reorged txs TxHash common.Hash // Owner transaction's hash to support resurrecting reorged txs
Block uint64 // Block in which the blob transaction was included Block uint64 // Block in which the blob transaction was included
Tx *types.Transaction
Blobs []kzg4844.Blob // The opaque blobs originally part of the transaction
Commits []kzg4844.Commitment // The commitments for the original blobs
Proofs []kzg4844.Proof // The proofs verifying the commitments
} }
// limbo is a light, indexed database to temporarily store recently included // limbo is a light, indexed database to temporarily store recently included
@ -98,19 +94,19 @@ func (l *limbo) parseBlob(id uint64, data []byte) error {
log.Error("Failed to decode blob limbo entry", "id", id, "err", err) log.Error("Failed to decode blob limbo entry", "id", id, "err", err)
return err return err
} }
if _, ok := l.index[item.Owner]; ok { if _, ok := l.index[item.TxHash]; ok {
// This path is impossible, unless due to a programming error a blob gets // This path is impossible, unless due to a programming error a blob gets
// inserted into the limbo which was already part of if. Recover gracefully // inserted into the limbo which was already part of if. Recover gracefully
// by ignoring this data entry. // by ignoring this data entry.
log.Error("Dropping duplicate blob limbo entry", "owner", item.Owner, "id", id) log.Error("Dropping duplicate blob limbo entry", "owner", item.TxHash, "id", id)
return errors.New("duplicate blob") return errors.New("duplicate blob")
} }
l.index[item.Owner] = id l.index[item.TxHash] = id
if _, ok := l.groups[item.Block]; !ok { if _, ok := l.groups[item.Block]; !ok {
l.groups[item.Block] = make(map[uint64]common.Hash) l.groups[item.Block] = make(map[uint64]common.Hash)
} }
l.groups[item.Block][id] = item.Owner l.groups[item.Block][id] = item.TxHash
return nil return nil
} }
@ -139,14 +135,14 @@ func (l *limbo) finalize(final *types.Header) {
// push stores a new blob transaction into the limbo, waiting until finality for // push stores a new blob transaction into the limbo, waiting until finality for
// it to be automatically evicted. // it to be automatically evicted.
func (l *limbo) push(tx common.Hash, block uint64, blobs []kzg4844.Blob, commits []kzg4844.Commitment, proofs []kzg4844.Proof) error { func (l *limbo) push(tx *types.Transaction, block uint64) error {
// If the blobs are already tracked by the limbo, consider it a programming // If the blobs are already tracked by the limbo, consider it a programming
// error. There's not much to do against it, but be loud. // error. There's not much to do against it, but be loud.
if _, ok := l.index[tx]; ok { if _, ok := l.index[tx.Hash()]; ok {
log.Error("Limbo cannot push already tracked blobs", "tx", tx) log.Error("Limbo cannot push already tracked blobs", "tx", tx)
return errors.New("already tracked blob transaction") return errors.New("already tracked blob transaction")
} }
if err := l.setAndIndex(tx, block, blobs, commits, proofs); err != nil { if err := l.setAndIndex(tx, block); err != nil {
log.Error("Failed to set and index liboed blobs", "tx", tx, "err", err) log.Error("Failed to set and index liboed blobs", "tx", tx, "err", err)
return err return err
} }
@ -156,21 +152,21 @@ func (l *limbo) push(tx common.Hash, block uint64, blobs []kzg4844.Blob, commits
// pull retrieves a previously pushed set of blobs back from the limbo, removing // pull retrieves a previously pushed set of blobs back from the limbo, removing
// it at the same time. This method should be used when a previously included blob // it at the same time. This method should be used when a previously included blob
// transaction gets reorged out. // transaction gets reorged out.
func (l *limbo) pull(tx common.Hash) ([]kzg4844.Blob, []kzg4844.Commitment, []kzg4844.Proof, error) { func (l *limbo) pull(tx common.Hash) (*types.Transaction, error) {
// If the blobs are not tracked by the limbo, there's not much to do. This // If the blobs are not tracked by the limbo, there's not much to do. This
// can happen for example if a blob transaction is mined without pushing it // can happen for example if a blob transaction is mined without pushing it
// into the network first. // into the network first.
id, ok := l.index[tx] id, ok := l.index[tx]
if !ok { if !ok {
log.Trace("Limbo cannot pull non-tracked blobs", "tx", tx) log.Trace("Limbo cannot pull non-tracked blobs", "tx", tx)
return nil, nil, nil, errors.New("unseen blob transaction") return nil, errors.New("unseen blob transaction")
} }
item, err := l.getAndDrop(id) item, err := l.getAndDrop(id)
if err != nil { if err != nil {
log.Error("Failed to get and drop limboed blobs", "tx", tx, "id", id, "err", err) log.Error("Failed to get and drop limboed blobs", "tx", tx, "id", id, "err", err)
return nil, nil, nil, err return nil, err
} }
return item.Blobs, item.Commits, item.Proofs, nil return item.Tx, nil
} }
// update changes the block number under which a blob transaction is tracked. This // update changes the block number under which a blob transaction is tracked. This
@ -180,33 +176,33 @@ func (l *limbo) pull(tx common.Hash) ([]kzg4844.Blob, []kzg4844.Commitment, []kz
// any of it since there's no clear error case. Some errors may be due to coding // any of it since there's no clear error case. Some errors may be due to coding
// issues, others caused by signers mining MEV stuff or swapping transactions. In // issues, others caused by signers mining MEV stuff or swapping transactions. In
// all cases, the pool needs to continue operating. // all cases, the pool needs to continue operating.
func (l *limbo) update(tx common.Hash, block uint64) { func (l *limbo) update(txhash common.Hash, block uint64) {
// If the blobs are not tracked by the limbo, there's not much to do. This // If the blobs are not tracked by the limbo, there's not much to do. This
// can happen for example if a blob transaction is mined without pushing it // can happen for example if a blob transaction is mined without pushing it
// into the network first. // into the network first.
id, ok := l.index[tx] id, ok := l.index[txhash]
if !ok { if !ok {
log.Trace("Limbo cannot update non-tracked blobs", "tx", tx) log.Trace("Limbo cannot update non-tracked blobs", "tx", txhash)
return return
} }
// If there was no change in the blob's inclusion block, don't mess around // If there was no change in the blob's inclusion block, don't mess around
// with heavy database operations. // with heavy database operations.
if _, ok := l.groups[block][id]; ok { if _, ok := l.groups[block][id]; ok {
log.Trace("Blob transaction unchanged in limbo", "tx", tx, "block", block) log.Trace("Blob transaction unchanged in limbo", "tx", txhash, "block", block)
return return
} }
// Retrieve the old blobs from the data store and write tehm back with a new // Retrieve the old blobs from the data store and write tehm back with a new
// block number. IF anything fails, there's not much to do, go on. // block number. IF anything fails, there's not much to do, go on.
item, err := l.getAndDrop(id) item, err := l.getAndDrop(id)
if err != nil { if err != nil {
log.Error("Failed to get and drop limboed blobs", "tx", tx, "id", id, "err", err) log.Error("Failed to get and drop limboed blobs", "tx", txhash, "id", id, "err", err)
return return
} }
if err := l.setAndIndex(tx, block, item.Blobs, item.Commits, item.Proofs); err != nil { if err := l.setAndIndex(item.Tx, block); err != nil {
log.Error("Failed to set and index limboed blobs", "tx", tx, "err", err) log.Error("Failed to set and index limboed blobs", "tx", txhash, "err", err)
return return
} }
log.Trace("Blob transaction updated in limbo", "tx", tx, "old-block", item.Block, "new-block", block) log.Trace("Blob transaction updated in limbo", "tx", txhash, "old-block", item.Block, "new-block", block)
} }
// getAndDrop retrieves a blob item from the limbo store and deletes it both from // getAndDrop retrieves a blob item from the limbo store and deletes it both from
@ -220,7 +216,7 @@ func (l *limbo) getAndDrop(id uint64) (*limboBlob, error) {
if err = rlp.DecodeBytes(data, item); err != nil { if err = rlp.DecodeBytes(data, item); err != nil {
return nil, err return nil, err
} }
delete(l.index, item.Owner) delete(l.index, item.TxHash)
delete(l.groups[item.Block], id) delete(l.groups[item.Block], id)
if len(l.groups[item.Block]) == 0 { if len(l.groups[item.Block]) == 0 {
delete(l.groups, item.Block) delete(l.groups, item.Block)
@ -233,13 +229,12 @@ func (l *limbo) getAndDrop(id uint64) (*limboBlob, error) {
// setAndIndex assembles a limbo blob database entry and stores it, also updating // setAndIndex assembles a limbo blob database entry and stores it, also updating
// the in-memory indices. // the in-memory indices.
func (l *limbo) setAndIndex(tx common.Hash, block uint64, blobs []kzg4844.Blob, commits []kzg4844.Commitment, proofs []kzg4844.Proof) error { func (l *limbo) setAndIndex(tx *types.Transaction, block uint64) error {
txhash := tx.Hash()
item := &limboBlob{ item := &limboBlob{
Owner: tx, TxHash: txhash,
Block: block, Block: block,
Blobs: blobs, Tx: tx,
Commits: commits,
Proofs: proofs,
} }
data, err := rlp.EncodeToBytes(item) data, err := rlp.EncodeToBytes(item)
if err != nil { if err != nil {
@ -249,10 +244,10 @@ func (l *limbo) setAndIndex(tx common.Hash, block uint64, blobs []kzg4844.Blob,
if err != nil { if err != nil {
return err return err
} }
l.index[tx] = id l.index[txhash] = id
if _, ok := l.groups[block]; !ok { if _, ok := l.groups[block]; !ok {
l.groups[block] = make(map[uint64]common.Hash) l.groups[block] = make(map[uint64]common.Hash)
} }
l.groups[block][id] = tx l.groups[block][id] = txhash
return nil return nil
} }

View File

@ -535,7 +535,7 @@ func (pool *LegacyPool) Pending(enforceTips bool) map[common.Address][]*txpool.L
lazies[i] = &txpool.LazyTransaction{ lazies[i] = &txpool.LazyTransaction{
Pool: pool, Pool: pool,
Hash: txs[i].Hash(), Hash: txs[i].Hash(),
Tx: &txpool.Transaction{Tx: txs[i]}, Tx: txs[i],
Time: txs[i].Time(), Time: txs[i].Time(),
GasFeeCap: txs[i].GasFeeCap(), GasFeeCap: txs[i].GasFeeCap(),
GasTipCap: txs[i].GasTipCap(), GasTipCap: txs[i].GasTipCap(),
@ -588,7 +588,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
if local { if local {
opts.MinTip = new(big.Int) opts.MinTip = new(big.Int)
} }
if err := txpool.ValidateTransaction(tx, nil, nil, nil, pool.currentHead.Load(), pool.signer, opts); err != nil { if err := txpool.ValidateTransaction(tx, pool.currentHead.Load(), pool.signer, opts); err != nil {
return err return err
} }
return nil return nil
@ -900,26 +900,13 @@ func (pool *LegacyPool) promoteTx(addr common.Address, hash common.Hash, tx *typ
return true return true
} }
// Add enqueues a batch of transactions into the pool if they are valid. Depending
// on the local flag, full pricing contraints will or will not be applied.
//
// If sync is set, the method will block until all internal maintenance related
// to the add is finished. Only use this during tests for determinism!
func (pool *LegacyPool) Add(txs []*txpool.Transaction, local bool, sync bool) []error {
unwrapped := make([]*types.Transaction, len(txs))
for i, tx := range txs {
unwrapped[i] = tx.Tx
}
return pool.addTxs(unwrapped, local, sync)
}
// addLocals enqueues a batch of transactions into the pool if they are valid, marking the // addLocals enqueues a batch of transactions into the pool if they are valid, marking the
// senders as a local ones, ensuring they go around the local pricing constraints. // senders as a local ones, ensuring they go around the local pricing constraints.
// //
// This method is used to add transactions from the RPC API and performs synchronous pool // This method is used to add transactions from the RPC API and performs synchronous pool
// reorganization and event propagation. // reorganization and event propagation.
func (pool *LegacyPool) addLocals(txs []*types.Transaction) []error { func (pool *LegacyPool) addLocals(txs []*types.Transaction) []error {
return pool.addTxs(txs, !pool.config.NoLocals, true) return pool.Add(txs, !pool.config.NoLocals, true)
} }
// addLocal enqueues a single local transaction into the pool if it is valid. This is // addLocal enqueues a single local transaction into the pool if it is valid. This is
@ -935,7 +922,7 @@ func (pool *LegacyPool) addLocal(tx *types.Transaction) error {
// This method is used to add transactions from the p2p network and does not wait for pool // This method is used to add transactions from the p2p network and does not wait for pool
// reorganization and internal event propagation. // reorganization and internal event propagation.
func (pool *LegacyPool) addRemotes(txs []*types.Transaction) []error { func (pool *LegacyPool) addRemotes(txs []*types.Transaction) []error {
return pool.addTxs(txs, false, false) return pool.Add(txs, false, false)
} }
// addRemote enqueues a single transaction into the pool if it is valid. This is a convenience // addRemote enqueues a single transaction into the pool if it is valid. This is a convenience
@ -947,16 +934,20 @@ func (pool *LegacyPool) addRemote(tx *types.Transaction) error {
// addRemotesSync is like addRemotes, but waits for pool reorganization. Tests use this method. // addRemotesSync is like addRemotes, but waits for pool reorganization. Tests use this method.
func (pool *LegacyPool) addRemotesSync(txs []*types.Transaction) []error { func (pool *LegacyPool) addRemotesSync(txs []*types.Transaction) []error {
return pool.addTxs(txs, false, true) return pool.Add(txs, false, true)
} }
// This is like addRemotes with a single transaction, but waits for pool reorganization. Tests use this method. // This is like addRemotes with a single transaction, but waits for pool reorganization. Tests use this method.
func (pool *LegacyPool) addRemoteSync(tx *types.Transaction) error { func (pool *LegacyPool) addRemoteSync(tx *types.Transaction) error {
return pool.addTxs([]*types.Transaction{tx}, false, true)[0] return pool.Add([]*types.Transaction{tx}, false, true)[0]
} }
// addTxs attempts to queue a batch of transactions if they are valid. // Add enqueues a batch of transactions into the pool if they are valid. Depending
func (pool *LegacyPool) addTxs(txs []*types.Transaction, local, sync bool) []error { // on the local flag, full pricing contraints will or will not be applied.
//
// If sync is set, the method will block until all internal maintenance related
// to the add is finished. Only use this during tests for determinism!
func (pool *LegacyPool) Add(txs []*types.Transaction, local, sync bool) []error {
// Filter out known ones without obtaining the pool lock or recovering signatures // Filter out known ones without obtaining the pool lock or recovering signatures
var ( var (
errs = make([]error, len(txs)) errs = make([]error, len(txs))
@ -1042,12 +1033,12 @@ func (pool *LegacyPool) Status(hash common.Hash) txpool.TxStatus {
} }
// Get returns a transaction if it is contained in the pool and nil otherwise. // Get returns a transaction if it is contained in the pool and nil otherwise.
func (pool *LegacyPool) Get(hash common.Hash) *txpool.Transaction { func (pool *LegacyPool) Get(hash common.Hash) *types.Transaction {
tx := pool.get(hash) tx := pool.get(hash)
if tx == nil { if tx == nil {
return nil return nil
} }
return &txpool.Transaction{Tx: tx} return tx
} }
// get returns a transaction if it is contained in the pool and nil otherwise. // get returns a transaction if it is contained in the pool and nil otherwise.

View File

@ -23,27 +23,16 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
// Transaction is a helper struct to group together a canonical transaction with
// satellite data items that are needed by the pool but are not part of the chain.
type Transaction struct {
Tx *types.Transaction // Canonical transaction
BlobTxBlobs []kzg4844.Blob // Blobs needed by the blob pool
BlobTxCommits []kzg4844.Commitment // Commitments needed by the blob pool
BlobTxProofs []kzg4844.Proof // Proofs needed by the blob pool
}
// LazyTransaction contains a small subset of the transaction properties that is // LazyTransaction contains a small subset of the transaction properties that is
// enough for the miner and other APIs to handle large batches of transactions; // enough for the miner and other APIs to handle large batches of transactions;
// and supports pulling up the entire transaction when really needed. // and supports pulling up the entire transaction when really needed.
type LazyTransaction struct { type LazyTransaction struct {
Pool SubPool // Transaction subpool to pull the real transaction up Pool SubPool // Transaction subpool to pull the real transaction up
Hash common.Hash // Transaction hash to pull up if needed Hash common.Hash // Transaction hash to pull up if needed
Tx *Transaction // Transaction if already resolved Tx *types.Transaction // Transaction if already resolved
Time time.Time // Time when the transaction was first seen Time time.Time // Time when the transaction was first seen
GasFeeCap *big.Int // Maximum fee per gas the transaction may consume GasFeeCap *big.Int // Maximum fee per gas the transaction may consume
@ -52,7 +41,7 @@ type LazyTransaction struct {
// Resolve retrieves the full transaction belonging to a lazy handle if it is still // Resolve retrieves the full transaction belonging to a lazy handle if it is still
// maintained by the transaction pool. // maintained by the transaction pool.
func (ltx *LazyTransaction) Resolve() *Transaction { func (ltx *LazyTransaction) Resolve() *types.Transaction {
if ltx.Tx == nil { if ltx.Tx == nil {
ltx.Tx = ltx.Pool.Get(ltx.Hash) ltx.Tx = ltx.Pool.Get(ltx.Hash)
} }
@ -99,12 +88,12 @@ type SubPool interface {
Has(hash common.Hash) bool Has(hash common.Hash) bool
// Get returns a transaction if it is contained in the pool, or nil otherwise. // Get returns a transaction if it is contained in the pool, or nil otherwise.
Get(hash common.Hash) *Transaction Get(hash common.Hash) *types.Transaction
// Add enqueues a batch of transactions into the pool if they are valid. Due // Add enqueues a batch of transactions into the pool if they are valid. Due
// to the large transaction churn, add may postpone fully integrating the tx // to the large transaction churn, add may postpone fully integrating the tx
// to a later point to batch multiple ones together. // to a later point to batch multiple ones together.
Add(txs []*Transaction, local bool, sync bool) []error Add(txs []*types.Transaction, local bool, sync bool) []error
// Pending retrieves all currently processable transactions, grouped by origin // Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce. // account and sorted by nonce.

View File

@ -249,7 +249,7 @@ func (p *TxPool) Has(hash common.Hash) bool {
} }
// Get returns a transaction if it is contained in the pool, or nil otherwise. // Get returns a transaction if it is contained in the pool, or nil otherwise.
func (p *TxPool) Get(hash common.Hash) *Transaction { func (p *TxPool) Get(hash common.Hash) *types.Transaction {
for _, subpool := range p.subpools { for _, subpool := range p.subpools {
if tx := subpool.Get(hash); tx != nil { if tx := subpool.Get(hash); tx != nil {
return tx return tx
@ -261,14 +261,14 @@ func (p *TxPool) Get(hash common.Hash) *Transaction {
// Add enqueues a batch of transactions into the pool if they are valid. Due // Add enqueues a batch of transactions into the pool if they are valid. Due
// to the large transaction churn, add may postpone fully integrating the tx // to the large transaction churn, add may postpone fully integrating the tx
// to a later point to batch multiple ones together. // to a later point to batch multiple ones together.
func (p *TxPool) Add(txs []*Transaction, local bool, sync bool) []error { func (p *TxPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
// Split the input transactions between the subpools. It shouldn't really // Split the input transactions between the subpools. It shouldn't really
// happen that we receive merged batches, but better graceful than strange // happen that we receive merged batches, but better graceful than strange
// errors. // errors.
// //
// We also need to track how the transactions were split across the subpools, // We also need to track how the transactions were split across the subpools,
// so we can piece back the returned errors into the original order. // so we can piece back the returned errors into the original order.
txsets := make([][]*Transaction, len(p.subpools)) txsets := make([][]*types.Transaction, len(p.subpools))
splits := make([]int, len(txs)) splits := make([]int, len(txs))
for i, tx := range txs { for i, tx := range txs {
@ -277,7 +277,7 @@ func (p *TxPool) Add(txs []*Transaction, local bool, sync bool) []error {
// Try to find a subpool that accepts the transaction // Try to find a subpool that accepts the transaction
for j, subpool := range p.subpools { for j, subpool := range p.subpools {
if subpool.Filter(tx.Tx) { if subpool.Filter(tx) {
txsets[j] = append(txsets[j], tx) txsets[j] = append(txsets[j], tx)
splits[i] = j splits[i] = j
break break

View File

@ -46,7 +46,7 @@ type ValidationOptions struct {
// //
// This check is public to allow different transaction pools to check the basic // This check is public to allow different transaction pools to check the basic
// rules without duplicating code and running the risk of missed updates. // rules without duplicating code and running the risk of missed updates.
func ValidateTransaction(tx *types.Transaction, blobs []kzg4844.Blob, commits []kzg4844.Commitment, proofs []kzg4844.Proof, head *types.Header, signer types.Signer, opts *ValidationOptions) error { func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error {
// Ensure transactions not implemented by the calling pool are rejected // Ensure transactions not implemented by the calling pool are rejected
if opts.Accept&(1<<tx.Type()) == 0 { if opts.Accept&(1<<tx.Type()) == 0 {
return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type()) return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type())
@ -110,6 +110,10 @@ func ValidateTransaction(tx *types.Transaction, blobs []kzg4844.Blob, commits []
} }
// Ensure blob transactions have valid commitments // Ensure blob transactions have valid commitments
if tx.Type() == types.BlobTxType { if tx.Type() == types.BlobTxType {
sidecar := tx.BlobTxSidecar()
if sidecar == nil {
return fmt.Errorf("missing sidecar in blob transaction")
}
// Ensure the number of items in the blob transaction and vairous side // Ensure the number of items in the blob transaction and vairous side
// data match up before doing any expensive validations // data match up before doing any expensive validations
hashes := tx.BlobHashes() hashes := tx.BlobHashes()
@ -119,37 +123,44 @@ func ValidateTransaction(tx *types.Transaction, blobs []kzg4844.Blob, commits []
if len(hashes) > params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob { if len(hashes) > params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob) return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
} }
if len(blobs) != len(hashes) { if err := validateBlobSidecar(hashes, sidecar); err != nil {
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(blobs), len(hashes)) return err
} }
if len(commits) != len(hashes) { }
return fmt.Errorf("invalid number of %d blob commitments compared to %d blob hashes", len(commits), len(hashes)) return nil
} }
if len(proofs) != len(hashes) {
return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", len(proofs), len(hashes))
}
// Blob quantities match up, validate that the provers match with the
// transaction hash before getting to the cryptography
hasher := sha256.New()
for i, want := range hashes {
hasher.Write(commits[i][:])
hash := hasher.Sum(nil)
hasher.Reset()
var vhash common.Hash func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) error {
vhash[0] = params.BlobTxHashVersion if len(sidecar.Blobs) != len(hashes) {
copy(vhash[1:], hash[1:]) return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
}
if len(sidecar.Commitments) != len(hashes) {
return fmt.Errorf("invalid number of %d blob commitments compared to %d blob hashes", len(sidecar.Commitments), len(hashes))
}
if len(sidecar.Proofs) != len(hashes) {
return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", len(sidecar.Proofs), len(hashes))
}
// Blob quantities match up, validate that the provers match with the
// transaction hash before getting to the cryptography
hasher := sha256.New()
for i, want := range hashes {
hasher.Write(sidecar.Commitments[i][:])
hash := hasher.Sum(nil)
hasher.Reset()
if vhash != want { var vhash common.Hash
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, vhash, want) vhash[0] = params.BlobTxHashVersion
} copy(vhash[1:], hash[1:])
if vhash != want {
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, vhash, want)
} }
// Blob commitments match with the hashes in the transaction, verify the }
// blobs themselves via KZG // Blob commitments match with the hashes in the transaction, verify the
for i := range blobs { // blobs themselves via KZG
if err := kzg4844.VerifyBlobProof(blobs[i], commits[i], proofs[i]); err != nil { for i := range sidecar.Blobs {
return fmt.Errorf("invalid blob %d: %v", i, err) if err := kzg4844.VerifyBlobProof(sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
} return fmt.Errorf("invalid blob %d: %v", i, err)
} }
} }
return nil return nil

View File

@ -82,9 +82,6 @@ type TxData interface {
value() *big.Int value() *big.Int
nonce() uint64 nonce() uint64
to() *common.Address to() *common.Address
blobGas() uint64
blobGasFeeCap() *big.Int
blobHashes() []common.Hash
rawSignatureValues() (v, r, s *big.Int) rawSignatureValues() (v, r, s *big.Int)
setSignatureValues(chainID, v, r, s *big.Int) setSignatureValues(chainID, v, r, s *big.Int)
@ -96,6 +93,9 @@ type TxData interface {
// copy of the computed value, i.e. callers are allowed to mutate the result. // copy of the computed value, i.e. callers are allowed to mutate the result.
// Method implementations can use 'dst' to store the result. // Method implementations can use 'dst' to store the result.
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
encode(*bytes.Buffer) error
decode([]byte) error
} }
// EncodeRLP implements rlp.Encoder // EncodeRLP implements rlp.Encoder
@ -116,7 +116,7 @@ func (tx *Transaction) EncodeRLP(w io.Writer) error {
// encodeTyped writes the canonical encoding of a typed transaction to w. // encodeTyped writes the canonical encoding of a typed transaction to w.
func (tx *Transaction) encodeTyped(w *bytes.Buffer) error { func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
w.WriteByte(tx.Type()) w.WriteByte(tx.Type())
return rlp.Encode(w, tx.inner) return tx.inner.encode(w)
} }
// MarshalBinary returns the canonical encoding of the transaction. // MarshalBinary returns the canonical encoding of the transaction.
@ -186,22 +186,19 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
if len(b) <= 1 { if len(b) <= 1 {
return nil, errShortTypedTx return nil, errShortTypedTx
} }
var inner TxData
switch b[0] { switch b[0] {
case AccessListTxType: case AccessListTxType:
var inner AccessListTx inner = new(AccessListTx)
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
case DynamicFeeTxType: case DynamicFeeTxType:
var inner DynamicFeeTx inner = new(DynamicFeeTx)
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
case BlobTxType: case BlobTxType:
var inner BlobTx inner = new(BlobTx)
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
default: default:
return nil, ErrTxTypeNotSupported return nil, ErrTxTypeNotSupported
} }
err := inner.decode(b[1:])
return inner, err
} }
// setDecoded sets the inner transaction and size after decoding. // setDecoded sets the inner transaction and size after decoding.
@ -288,15 +285,6 @@ func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.g
// GasFeeCap returns the fee cap per gas of the transaction. // GasFeeCap returns the fee cap per gas of the transaction.
func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) } func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) }
// BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise.
func (tx *Transaction) BlobGas() uint64 { return tx.inner.blobGas() }
// BlobGasFeeCap returns the blob gas fee cap per blob gas of the transaction for blob transactions, nil otherwise.
func (tx *Transaction) BlobGasFeeCap() *big.Int { return tx.inner.blobGasFeeCap() }
// BlobHashes returns the hases of the blob commitments for blob transactions, nil otherwise.
func (tx *Transaction) BlobHashes() []common.Hash { return tx.inner.blobHashes() }
// Value returns the ether amount of the transaction. // Value returns the ether amount of the transaction.
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) } func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
@ -383,14 +371,66 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i
return tx.EffectiveGasTipValue(baseFee).Cmp(other) return tx.EffectiveGasTipValue(baseFee).Cmp(other)
} }
// BlobGas returns the blob gas limit of the transaction for blob transactions, 0 otherwise.
func (tx *Transaction) BlobGas() uint64 {
if blobtx, ok := tx.inner.(*BlobTx); ok {
return blobtx.blobGas()
}
return 0
}
// BlobGasFeeCap returns the blob gas fee cap per blob gas of the transaction for blob transactions, nil otherwise.
func (tx *Transaction) BlobGasFeeCap() *big.Int {
if blobtx, ok := tx.inner.(*BlobTx); ok {
return blobtx.BlobFeeCap.ToBig()
}
return nil
}
// BlobHashes returns the hases of the blob commitments for blob transactions, nil otherwise.
func (tx *Transaction) BlobHashes() []common.Hash {
if blobtx, ok := tx.inner.(*BlobTx); ok {
return blobtx.BlobHashes
}
return nil
}
// BlobTxSidecar returns the sidecar of a blob transaction, nil otherwise.
func (tx *Transaction) BlobTxSidecar() *BlobTxSidecar {
if blobtx, ok := tx.inner.(*BlobTx); ok {
return blobtx.Sidecar
}
return nil
}
// BlobGasFeeCapCmp compares the blob fee cap of two transactions. // BlobGasFeeCapCmp compares the blob fee cap of two transactions.
func (tx *Transaction) BlobGasFeeCapCmp(other *Transaction) int { func (tx *Transaction) BlobGasFeeCapCmp(other *Transaction) int {
return tx.inner.blobGasFeeCap().Cmp(other.inner.blobGasFeeCap()) return tx.BlobGasFeeCap().Cmp(other.BlobGasFeeCap())
} }
// BlobGasFeeCapIntCmp compares the blob fee cap of the transaction against the given blob fee cap. // BlobGasFeeCapIntCmp compares the blob fee cap of the transaction against the given blob fee cap.
func (tx *Transaction) BlobGasFeeCapIntCmp(other *big.Int) int { func (tx *Transaction) BlobGasFeeCapIntCmp(other *big.Int) int {
return tx.inner.blobGasFeeCap().Cmp(other) return tx.BlobGasFeeCap().Cmp(other)
}
// WithoutBlobTxSidecar returns a copy of tx with the blob sidecar removed.
func (tx *Transaction) WithoutBlobTxSidecar() *Transaction {
blobtx, ok := tx.inner.(*BlobTx)
if !ok {
return tx
}
cpy := &Transaction{
inner: blobtx.withoutSidecar(),
time: tx.time,
}
// Note: tx.size cache not carried over because the sidecar is included in size!
if h := tx.hash.Load(); h != nil {
cpy.hash.Store(h)
}
if f := tx.from.Load(); f != nil {
cpy.from.Store(f)
}
return cpy
} }
// SetTime sets the decoding time of a transaction. This is used by tests to set // SetTime sets the decoding time of a transaction. This is used by tests to set
@ -428,13 +468,24 @@ func (tx *Transaction) Size() uint64 {
if size := tx.size.Load(); size != nil { if size := tx.size.Load(); size != nil {
return size.(uint64) return size.(uint64)
} }
// Cache miss, encode and cache.
// Note we rely on the assumption that all tx.inner values are RLP-encoded!
c := writeCounter(0) c := writeCounter(0)
rlp.Encode(&c, &tx.inner) rlp.Encode(&c, &tx.inner)
size := uint64(c) size := uint64(c)
if tx.Type() != LegacyTxType {
size += 1 // type byte // For blob transactions, add the size of the blob content and the outer list of the
// tx + sidecar encoding.
if sc := tx.BlobTxSidecar(); sc != nil {
size += rlp.ListSize(sc.encodedSize())
} }
// For typed transactions, the encoding also includes the leading type byte.
if tx.Type() != LegacyTxType {
size += 1
}
tx.size.Store(size) tx.size.Store(size)
return size return size
} }

View File

@ -17,9 +17,11 @@
package types package types
import ( import (
"bytes"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
) )
//go:generate go run github.com/fjl/gencodec -type AccessTuple -out gen_access_tuple.go //go:generate go run github.com/fjl/gencodec -type AccessTuple -out gen_access_tuple.go
@ -94,20 +96,17 @@ func (tx *AccessListTx) copy() TxData {
} }
// accessors for innerTx. // accessors for innerTx.
func (tx *AccessListTx) txType() byte { return AccessListTxType } func (tx *AccessListTx) txType() byte { return AccessListTxType }
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID } func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList } func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
func (tx *AccessListTx) data() []byte { return tx.Data } func (tx *AccessListTx) data() []byte { return tx.Data }
func (tx *AccessListTx) gas() uint64 { return tx.Gas } func (tx *AccessListTx) gas() uint64 { return tx.Gas }
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice }
func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *AccessListTx) value() *big.Int { return tx.Value } func (tx *AccessListTx) value() *big.Int { return tx.Value }
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
func (tx *AccessListTx) to() *common.Address { return tx.To } func (tx *AccessListTx) to() *common.Address { return tx.To }
func (tx *AccessListTx) blobGas() uint64 { return 0 }
func (tx *AccessListTx) blobGasFeeCap() *big.Int { return nil }
func (tx *AccessListTx) blobHashes() []common.Hash { return nil }
func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return dst.Set(tx.GasPrice) return dst.Set(tx.GasPrice)
@ -120,3 +119,11 @@ func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) { func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *big.Int) {
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
} }
func (tx *AccessListTx) encode(b *bytes.Buffer) error {
return rlp.Encode(b, tx)
}
func (tx *AccessListTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

View File

@ -17,10 +17,14 @@
package types package types
import ( import (
"bytes"
"crypto/sha256"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -38,12 +42,56 @@ type BlobTx struct {
BlobFeeCap *uint256.Int // a.k.a. maxFeePerBlobGas BlobFeeCap *uint256.Int // a.k.a. maxFeePerBlobGas
BlobHashes []common.Hash BlobHashes []common.Hash
// A blob transaction can optionally contain blobs. This field must be set when BlobTx
// is used to create a transaction for sigining.
Sidecar *BlobTxSidecar `rlp:"-"`
// Signature values // Signature values
V *uint256.Int `json:"v" gencodec:"required"` V *uint256.Int `json:"v" gencodec:"required"`
R *uint256.Int `json:"r" gencodec:"required"` R *uint256.Int `json:"r" gencodec:"required"`
S *uint256.Int `json:"s" gencodec:"required"` S *uint256.Int `json:"s" gencodec:"required"`
} }
// BlobTxSidecar contains the blobs of a blob transaction.
type BlobTxSidecar struct {
Blobs []kzg4844.Blob // Blobs needed by the blob pool
Commitments []kzg4844.Commitment // Commitments needed by the blob pool
Proofs []kzg4844.Proof // Proofs needed by the blob pool
}
// BlobHashes computes the blob hashes of the given blobs.
func (sc *BlobTxSidecar) BlobHashes() []common.Hash {
h := make([]common.Hash, len(sc.Commitments))
for i := range sc.Blobs {
h[i] = blobHash(&sc.Commitments[i])
}
return h
}
// encodedSize computes the RLP size of the sidecar elements. This does NOT return the
// encoded size of the BlobTxSidecar, it's just a helper for tx.Size().
func (sc *BlobTxSidecar) encodedSize() uint64 {
var blobs, commitments, proofs uint64
for i := range sc.Blobs {
blobs += rlp.BytesSize(sc.Blobs[i][:])
}
for i := range sc.Commitments {
commitments += rlp.BytesSize(sc.Commitments[i][:])
}
for i := range sc.Proofs {
proofs += rlp.BytesSize(sc.Proofs[i][:])
}
return rlp.ListSize(blobs) + rlp.ListSize(commitments) + rlp.ListSize(proofs)
}
// blobTxWithBlobs is used for encoding of transactions when blobs are present.
type blobTxWithBlobs struct {
BlobTx *BlobTx
Blobs []kzg4844.Blob
Commitments []kzg4844.Commitment
Proofs []kzg4844.Proof
}
// copy creates a deep copy of the transaction data and initializes all fields. // copy creates a deep copy of the transaction data and initializes all fields.
func (tx *BlobTx) copy() TxData { func (tx *BlobTx) copy() TxData {
cpy := &BlobTx{ cpy := &BlobTx{
@ -90,24 +138,29 @@ func (tx *BlobTx) copy() TxData {
if tx.S != nil { if tx.S != nil {
cpy.S.Set(tx.S) cpy.S.Set(tx.S)
} }
if tx.Sidecar != nil {
cpy.Sidecar = &BlobTxSidecar{
Blobs: append([]kzg4844.Blob(nil), tx.Sidecar.Blobs...),
Commitments: append([]kzg4844.Commitment(nil), tx.Sidecar.Commitments...),
Proofs: append([]kzg4844.Proof(nil), tx.Sidecar.Proofs...),
}
}
return cpy return cpy
} }
// accessors for innerTx. // accessors for innerTx.
func (tx *BlobTx) txType() byte { return BlobTxType } func (tx *BlobTx) txType() byte { return BlobTxType }
func (tx *BlobTx) chainID() *big.Int { return tx.ChainID.ToBig() } func (tx *BlobTx) chainID() *big.Int { return tx.ChainID.ToBig() }
func (tx *BlobTx) accessList() AccessList { return tx.AccessList } func (tx *BlobTx) accessList() AccessList { return tx.AccessList }
func (tx *BlobTx) data() []byte { return tx.Data } func (tx *BlobTx) data() []byte { return tx.Data }
func (tx *BlobTx) gas() uint64 { return tx.Gas } func (tx *BlobTx) gas() uint64 { return tx.Gas }
func (tx *BlobTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() } func (tx *BlobTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *BlobTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() } func (tx *BlobTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
func (tx *BlobTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() } func (tx *BlobTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *BlobTx) value() *big.Int { return tx.Value.ToBig() } func (tx *BlobTx) value() *big.Int { return tx.Value.ToBig() }
func (tx *BlobTx) nonce() uint64 { return tx.Nonce } func (tx *BlobTx) nonce() uint64 { return tx.Nonce }
func (tx *BlobTx) to() *common.Address { tmp := tx.To; return &tmp } func (tx *BlobTx) to() *common.Address { tmp := tx.To; return &tmp }
func (tx *BlobTx) blobGas() uint64 { return params.BlobTxBlobGasPerBlob * uint64(len(tx.BlobHashes)) } func (tx *BlobTx) blobGas() uint64 { return params.BlobTxBlobGasPerBlob * uint64(len(tx.BlobHashes)) }
func (tx *BlobTx) blobGasFeeCap() *big.Int { return tx.BlobFeeCap.ToBig() }
func (tx *BlobTx) blobHashes() []common.Hash { return tx.BlobHashes }
func (tx *BlobTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { func (tx *BlobTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil { if baseFee == nil {
@ -130,3 +183,64 @@ func (tx *BlobTx) setSignatureValues(chainID, v, r, s *big.Int) {
tx.R.SetFromBig(r) tx.R.SetFromBig(r)
tx.S.SetFromBig(s) tx.S.SetFromBig(s)
} }
func (tx *BlobTx) withoutSidecar() *BlobTx {
cpy := *tx
cpy.Sidecar = nil
return &cpy
}
func (tx *BlobTx) encode(b *bytes.Buffer) error {
if tx.Sidecar == nil {
return rlp.Encode(b, tx)
}
inner := &blobTxWithBlobs{
BlobTx: tx,
Blobs: tx.Sidecar.Blobs,
Commitments: tx.Sidecar.Commitments,
Proofs: tx.Sidecar.Proofs,
}
return rlp.Encode(b, inner)
}
func (tx *BlobTx) decode(input []byte) error {
// Here we need to support two formats: the network protocol encoding of the tx (with
// blobs) or the canonical encoding without blobs.
//
// The two encodings can be distinguished by checking whether the first element of the
// input list is itself a list.
outerList, _, err := rlp.SplitList(input)
if err != nil {
return err
}
firstElemKind, _, _, err := rlp.Split(outerList)
if err != nil {
return err
}
if firstElemKind != rlp.List {
return rlp.DecodeBytes(input, tx)
}
// It's a tx with blobs.
var inner blobTxWithBlobs
if err := rlp.DecodeBytes(input, &inner); err != nil {
return err
}
*tx = *inner.BlobTx
tx.Sidecar = &BlobTxSidecar{
Blobs: inner.Blobs,
Commitments: inner.Commitments,
Proofs: inner.Proofs,
}
return nil
}
func blobHash(commit *kzg4844.Commitment) common.Hash {
hasher := sha256.New()
hasher.Write(commit[:])
var vhash common.Hash
hasher.Sum(vhash[:0])
vhash[0] = params.BlobTxHashVersion
return vhash
}

View File

@ -0,0 +1,90 @@
package types
import (
"crypto/ecdsa"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/holiman/uint256"
)
// This test verifies that tx.Hash() is not affected by presence of a BlobTxSidecar.
func TestBlobTxHashing(t *testing.T) {
key, _ := crypto.GenerateKey()
withBlobs := createEmptyBlobTx(key, true)
withBlobsStripped := withBlobs.WithoutBlobTxSidecar()
withoutBlobs := createEmptyBlobTx(key, false)
hash := withBlobs.Hash()
t.Log("tx hash:", hash)
if h := withBlobsStripped.Hash(); h != hash {
t.Fatal("wrong tx hash after WithoutBlobTxSidecar:", h)
}
if h := withoutBlobs.Hash(); h != hash {
t.Fatal("wrong tx hash on tx created without sidecar:", h)
}
}
// This test verifies that tx.Size() takes BlobTxSidecar into account.
func TestBlobTxSize(t *testing.T) {
key, _ := crypto.GenerateKey()
withBlobs := createEmptyBlobTx(key, true)
withBlobsStripped := withBlobs.WithoutBlobTxSidecar()
withoutBlobs := createEmptyBlobTx(key, false)
withBlobsEnc, _ := withBlobs.MarshalBinary()
withoutBlobsEnc, _ := withoutBlobs.MarshalBinary()
size := withBlobs.Size()
t.Log("size with blobs:", size)
sizeNoBlobs := withoutBlobs.Size()
t.Log("size without blobs:", sizeNoBlobs)
if size != uint64(len(withBlobsEnc)) {
t.Error("wrong size with blobs:", size, "encoded length:", len(withBlobsEnc))
}
if sizeNoBlobs != uint64(len(withoutBlobsEnc)) {
t.Error("wrong size without blobs:", sizeNoBlobs, "encoded length:", len(withoutBlobsEnc))
}
if sizeNoBlobs >= size {
t.Error("size without blobs >= size with blobs")
}
if sz := withBlobsStripped.Size(); sz != sizeNoBlobs {
t.Fatal("wrong size on tx after WithoutBlobTxSidecar:", sz)
}
}
var (
emptyBlob = kzg4844.Blob{}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
)
func createEmptyBlobTx(key *ecdsa.PrivateKey, withSidecar bool) *Transaction {
sidecar := &BlobTxSidecar{
Blobs: []kzg4844.Blob{emptyBlob},
Commitments: []kzg4844.Commitment{emptyBlobCommit},
Proofs: []kzg4844.Proof{emptyBlobProof},
}
blobtx := &BlobTx{
ChainID: uint256.NewInt(1),
Nonce: 5,
GasTipCap: uint256.NewInt(22),
GasFeeCap: uint256.NewInt(5),
Gas: 25000,
To: common.Address{0x03, 0x04, 0x05},
Value: uint256.NewInt(99),
Data: make([]byte, 50),
BlobFeeCap: uint256.NewInt(15),
BlobHashes: sidecar.BlobHashes(),
}
if withSidecar {
blobtx.Sidecar = sidecar
}
signer := NewCancunSigner(blobtx.ChainID.ToBig())
return MustSignNewTx(key, signer, blobtx)
}

View File

@ -17,9 +17,11 @@
package types package types
import ( import (
"bytes"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
) )
// DynamicFeeTx represents an EIP-1559 transaction. // DynamicFeeTx represents an EIP-1559 transaction.
@ -83,20 +85,17 @@ func (tx *DynamicFeeTx) copy() TxData {
} }
// accessors for innerTx. // accessors for innerTx.
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType } func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID } func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList } func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
func (tx *DynamicFeeTx) data() []byte { return tx.Data } func (tx *DynamicFeeTx) data() []byte { return tx.Data }
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap } func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap } func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
func (tx *DynamicFeeTx) to() *common.Address { return tx.To } func (tx *DynamicFeeTx) to() *common.Address { return tx.To }
func (tx *DynamicFeeTx) blobGas() uint64 { return 0 }
func (tx *DynamicFeeTx) blobGasFeeCap() *big.Int { return nil }
func (tx *DynamicFeeTx) blobHashes() []common.Hash { return nil }
func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil { if baseFee == nil {
@ -116,3 +115,11 @@ func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) {
func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *big.Int) { func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *big.Int) {
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
} }
func (tx *DynamicFeeTx) encode(b *bytes.Buffer) error {
return rlp.Encode(b, tx)
}
func (tx *DynamicFeeTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

View File

@ -17,6 +17,7 @@
package types package types
import ( import (
"bytes"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -91,20 +92,17 @@ func (tx *LegacyTx) copy() TxData {
} }
// accessors for innerTx. // accessors for innerTx.
func (tx *LegacyTx) txType() byte { return LegacyTxType } func (tx *LegacyTx) txType() byte { return LegacyTxType }
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) } func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) }
func (tx *LegacyTx) accessList() AccessList { return nil } func (tx *LegacyTx) accessList() AccessList { return nil }
func (tx *LegacyTx) data() []byte { return tx.Data } func (tx *LegacyTx) data() []byte { return tx.Data }
func (tx *LegacyTx) gas() uint64 { return tx.Gas } func (tx *LegacyTx) gas() uint64 { return tx.Gas }
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice }
func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *LegacyTx) value() *big.Int { return tx.Value } func (tx *LegacyTx) value() *big.Int { return tx.Value }
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
func (tx *LegacyTx) to() *common.Address { return tx.To } func (tx *LegacyTx) to() *common.Address { return tx.To }
func (tx *LegacyTx) blobGas() uint64 { return 0 }
func (tx *LegacyTx) blobGasFeeCap() *big.Int { return nil }
func (tx *LegacyTx) blobHashes() []common.Hash { return nil }
func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return dst.Set(tx.GasPrice) return dst.Set(tx.GasPrice)
@ -117,3 +115,11 @@ func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) { func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *big.Int) {
tx.V, tx.R, tx.S = v, r, s tx.V, tx.R, tx.S = v, r, s
} }
func (tx *LegacyTx) encode(*bytes.Buffer) error {
panic("encode called on LegacyTx")
}
func (tx *LegacyTx) decode([]byte) error {
panic("decode called on LegacyTx)")
}

View File

@ -294,7 +294,7 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
} }
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
return b.eth.txPool.Add([]*txpool.Transaction{{Tx: signedTx}}, true, false)[0] return b.eth.txPool.Add([]*types.Transaction{signedTx}, true, false)[0]
} }
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
@ -303,7 +303,7 @@ func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
for _, batch := range pending { for _, batch := range pending {
for _, lazy := range batch { for _, lazy := range batch {
if tx := lazy.Resolve(); tx != nil { if tx := lazy.Resolve(); tx != nil {
txs = append(txs, tx.Tx) txs = append(txs, tx)
} }
} }
} }
@ -311,10 +311,7 @@ func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
} }
func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction {
if tx := b.eth.txPool.Get(hash); tx != nil { return b.eth.txPool.Get(hash)
return tx.Tx
}
return nil
} }
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {

View File

@ -35,7 +35,6 @@ import (
beaconConsensus "github.com/ethereum/go-ethereum/consensus/beacon" beaconConsensus "github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
@ -108,7 +107,7 @@ func TestEth2AssembleBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("error signing transaction, err=%v", err) t.Fatalf("error signing transaction, err=%v", err)
} }
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, true, false) ethservice.TxPool().Add([]*types.Transaction{tx}, true, false)
blockParams := engine.PayloadAttributes{ blockParams := engine.PayloadAttributes{
Timestamp: blocks[9].Time() + 5, Timestamp: blocks[9].Time() + 5,
} }
@ -145,11 +144,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
// Put the 10th block's tx in the pool and produce a new block // Put the 10th block's tx in the pool and produce a new block
txs := blocks[9].Transactions() txs := blocks[9].Transactions()
wrapped := make([]*txpool.Transaction, len(txs)) api.eth.TxPool().Add(txs, false, true)
for i, tx := range txs {
wrapped[i] = &txpool.Transaction{Tx: tx}
}
api.eth.TxPool().Add(wrapped, false, true)
blockParams := engine.PayloadAttributes{ blockParams := engine.PayloadAttributes{
Timestamp: blocks[8].Time() + 5, Timestamp: blocks[8].Time() + 5,
} }
@ -189,11 +184,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
// Put the 10th block's tx in the pool and produce a new block // Put the 10th block's tx in the pool and produce a new block
txs := blocks[9].Transactions() txs := blocks[9].Transactions()
wrapped := make([]*txpool.Transaction, len(txs)) ethservice.TxPool().Add(txs, true, false)
for i, tx := range txs {
wrapped[i] = &txpool.Transaction{Tx: tx}
}
ethservice.TxPool().Add(wrapped, true, false)
blockParams := engine.PayloadAttributes{ blockParams := engine.PayloadAttributes{
Timestamp: blocks[8].Time() + 5, Timestamp: blocks[8].Time() + 5,
} }
@ -315,7 +306,7 @@ func TestEth2NewBlock(t *testing.T) {
statedb, _ := ethservice.BlockChain().StateAt(parent.Root()) statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
nonce := statedb.GetNonce(testAddr) nonce := statedb.GetNonce(testAddr)
tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, true, false) ethservice.TxPool().Add([]*types.Transaction{tx}, true, false)
execData, err := assembleWithTransactions(api, parent.Hash(), &engine.PayloadAttributes{ execData, err := assembleWithTransactions(api, parent.Hash(), &engine.PayloadAttributes{
Timestamp: parent.Time() + 5, Timestamp: parent.Time() + 5,
@ -484,7 +475,7 @@ func TestFullAPI(t *testing.T) {
statedb, _ := ethservice.BlockChain().StateAt(parent.Root) statedb, _ := ethservice.BlockChain().StateAt(parent.Root)
nonce := statedb.GetNonce(testAddr) nonce := statedb.GetNonce(testAddr)
tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, true, false) ethservice.TxPool().Add([]*types.Transaction{tx}, true, false)
} }
setupBlocks(t, ethservice, 10, parent, callback, nil) setupBlocks(t, ethservice, 10, parent, callback, nil)
@ -610,7 +601,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
GasPrice: big.NewInt(2 * params.InitialBaseFee), GasPrice: big.NewInt(2 * params.InitialBaseFee),
Data: logCode, Data: logCode,
}) })
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, false, true) ethservice.TxPool().Add([]*types.Transaction{tx}, false, true)
var ( var (
params = engine.PayloadAttributes{ params = engine.PayloadAttributes{
Timestamp: parent.Time + 1, Timestamp: parent.Time + 1,
@ -1284,7 +1275,7 @@ func setupBodies(t *testing.T) (*node.Node, *eth.Ethereum, []*types.Block) {
statedb, _ := ethservice.BlockChain().StateAt(parent.Root) statedb, _ := ethservice.BlockChain().StateAt(parent.Root)
nonce := statedb.GetNonce(testAddr) nonce := statedb.GetNonce(testAddr)
tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey) tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, true, false) ethservice.TxPool().Add([]*types.Transaction{tx}, false, false)
} }
withdrawals := make([][]*types.Withdrawal, 10) withdrawals := make([][]*types.Withdrawal, 10)

View File

@ -798,7 +798,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
} }
} }
// Blocks must have a number of blobs corresponding to the header gas usage, // Blocks must have a number of blobs corresponding to the header gas usage,
// and zero before the Cancun hardfork // and zero before the Cancun hardfork.
var blobs int var blobs int
for _, tx := range txLists[index] { for _, tx := range txLists[index] {
// Count the number of blobs to validate against the header's blobGasUsed // Count the number of blobs to validate against the header's blobGasUsed
@ -814,6 +814,9 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
return errInvalidBody return errInvalidBody
} }
} }
if tx.BlobTxSidecar() != nil {
return errInvalidBody
}
} }
} }
if header.BlobGasUsed != nil { if header.BlobGasUsed != nil {

View File

@ -169,9 +169,9 @@ type TxFetcher struct {
alternates map[common.Hash]map[string]struct{} // In-flight transaction alternate origins if retrieval fails alternates map[common.Hash]map[string]struct{} // In-flight transaction alternate origins if retrieval fails
// Callbacks // Callbacks
hasTx func(common.Hash) bool // Retrieves a tx from the local txpool hasTx func(common.Hash) bool // Retrieves a tx from the local txpool
addTxs func([]*txpool.Transaction) []error // Insert a batch of transactions into local txpool addTxs func([]*types.Transaction) []error // Insert a batch of transactions into local txpool
fetchTxs func(string, []common.Hash) error // Retrieves a set of txs from a remote peer fetchTxs func(string, []common.Hash) error // Retrieves a set of txs from a remote peer
step chan struct{} // Notification channel when the fetcher loop iterates step chan struct{} // Notification channel when the fetcher loop iterates
clock mclock.Clock // Time wrapper to simulate in tests clock mclock.Clock // Time wrapper to simulate in tests
@ -180,14 +180,14 @@ type TxFetcher struct {
// NewTxFetcher creates a transaction fetcher to retrieve transaction // NewTxFetcher creates a transaction fetcher to retrieve transaction
// based on hash announcements. // based on hash announcements.
func NewTxFetcher(hasTx func(common.Hash) bool, addTxs func([]*txpool.Transaction) []error, fetchTxs func(string, []common.Hash) error) *TxFetcher { func NewTxFetcher(hasTx func(common.Hash) bool, addTxs func([]*types.Transaction) []error, fetchTxs func(string, []common.Hash) error) *TxFetcher {
return NewTxFetcherForTests(hasTx, addTxs, fetchTxs, mclock.System{}, nil) return NewTxFetcherForTests(hasTx, addTxs, fetchTxs, mclock.System{}, nil)
} }
// NewTxFetcherForTests is a testing method to mock out the realtime clock with // NewTxFetcherForTests is a testing method to mock out the realtime clock with
// a simulated version and the internal randomness with a deterministic one. // a simulated version and the internal randomness with a deterministic one.
func NewTxFetcherForTests( func NewTxFetcherForTests(
hasTx func(common.Hash) bool, addTxs func([]*txpool.Transaction) []error, fetchTxs func(string, []common.Hash) error, hasTx func(common.Hash) bool, addTxs func([]*types.Transaction) []error, fetchTxs func(string, []common.Hash) error,
clock mclock.Clock, rand *mrand.Rand) *TxFetcher { clock mclock.Clock, rand *mrand.Rand) *TxFetcher {
return &TxFetcher{ return &TxFetcher{
notify: make(chan *txAnnounce), notify: make(chan *txAnnounce),
@ -295,11 +295,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
) )
batch := txs[i:end] batch := txs[i:end]
wrapped := make([]*txpool.Transaction, len(batch)) for j, err := range f.addTxs(batch) {
for j, tx := range batch {
wrapped[j] = &txpool.Transaction{Tx: tx}
}
for j, err := range f.addTxs(wrapped) {
// Track the transaction hash if the price is too low for us. // Track the transaction hash if the price is too low for us.
// Avoid re-request this transaction when we receive another // Avoid re-request this transaction when we receive another
// announcement. // announcement.

View File

@ -378,7 +378,7 @@ func TestTransactionFetcherCleanup(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -417,7 +417,7 @@ func TestTransactionFetcherCleanupEmpty(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -455,7 +455,7 @@ func TestTransactionFetcherMissingRescheduling(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -501,7 +501,7 @@ func TestTransactionFetcherMissingCleanup(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -539,7 +539,7 @@ func TestTransactionFetcherBroadcasts(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -644,7 +644,7 @@ func TestTransactionFetcherTimeoutRescheduling(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -865,7 +865,7 @@ func TestTransactionFetcherUnderpricedDedup(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
errs := make([]error, len(txs)) errs := make([]error, len(txs))
for i := 0; i < len(errs); i++ { for i := 0; i < len(errs); i++ {
if i%2 == 0 { if i%2 == 0 {
@ -938,7 +938,7 @@ func TestTransactionFetcherUnderpricedDoSProtection(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
errs := make([]error, len(txs)) errs := make([]error, len(txs))
for i := 0; i < len(errs); i++ { for i := 0; i < len(errs); i++ {
errs[i] = txpool.ErrUnderpriced errs[i] = txpool.ErrUnderpriced
@ -964,7 +964,7 @@ func TestTransactionFetcherOutOfBoundDeliveries(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -1017,7 +1017,7 @@ func TestTransactionFetcherDrop(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -1083,7 +1083,7 @@ func TestTransactionFetcherDropRescheduling(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -1128,7 +1128,7 @@ func TestTransactionFetcherFuzzCrash01(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -1155,7 +1155,7 @@ func TestTransactionFetcherFuzzCrash02(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -1184,7 +1184,7 @@ func TestTransactionFetcherFuzzCrash03(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },
@ -1217,7 +1217,7 @@ func TestTransactionFetcherFuzzCrash04(t *testing.T) {
init: func() *TxFetcher { init: func() *TxFetcher {
return NewTxFetcher( return NewTxFetcher(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { func(string, []common.Hash) error {

View File

@ -68,10 +68,10 @@ type txPool interface {
// Get retrieves the transaction from local txpool with given // Get retrieves the transaction from local txpool with given
// tx hash. // tx hash.
Get(hash common.Hash) *txpool.Transaction Get(hash common.Hash) *types.Transaction
// Add should add the given transactions to the pool. // Add should add the given transactions to the pool.
Add(txs []*txpool.Transaction, local bool, sync bool) []error Add(txs []*types.Transaction, local bool, sync bool) []error
// Pending should return pending transactions. // Pending should return pending transactions.
// The slice should be modifiable by the caller. // The slice should be modifiable by the caller.
@ -287,7 +287,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
} }
return p.RequestTxs(hashes) return p.RequestTxs(hashes)
} }
addTxs := func(txs []*txpool.Transaction) []error { addTxs := func(txs []*types.Transaction) []error {
return h.txpool.Add(txs, false, false) return h.txpool.Add(txs, false, false)
} }
h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, addTxs, fetchTx) h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, addTxs, fetchTx)

View File

@ -28,7 +28,6 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
@ -308,12 +307,11 @@ func testSendTransactions(t *testing.T, protocol uint) {
handler := newTestHandler() handler := newTestHandler()
defer handler.close() defer handler.close()
insert := make([]*txpool.Transaction, 100) insert := make([]*types.Transaction, 100)
for nonce := range insert { for nonce := range insert {
tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, 10240)) tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, 10240))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
insert[nonce] = tx
insert[nonce] = &txpool.Transaction{Tx: tx}
} }
go handler.txpool.Add(insert, false, false) // Need goroutine to not block on feed go handler.txpool.Add(insert, false, false) // Need goroutine to not block on feed
time.Sleep(250 * time.Millisecond) // Wait until tx events get out of the system (can't use events, tx broadcaster races with peer join) time.Sleep(250 * time.Millisecond) // Wait until tx events get out of the system (can't use events, tx broadcaster races with peer join)
@ -376,8 +374,8 @@ func testSendTransactions(t *testing.T, protocol uint) {
} }
} }
for _, tx := range insert { for _, tx := range insert {
if _, ok := seen[tx.Tx.Hash()]; !ok { if _, ok := seen[tx.Hash()]; !ok {
t.Errorf("missing transaction: %x", tx.Tx.Hash()) t.Errorf("missing transaction: %x", tx.Hash())
} }
} }
} }
@ -434,12 +432,11 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
defer sub.Unsubscribe() defer sub.Unsubscribe()
} }
// Fill the source pool with transactions and wait for them at the sinks // Fill the source pool with transactions and wait for them at the sinks
txs := make([]*txpool.Transaction, 1024) txs := make([]*types.Transaction, 1024)
for nonce := range txs { for nonce := range txs {
tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil) tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil)
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
txs[nonce] = tx
txs[nonce] = &txpool.Transaction{Tx: tx}
} }
source.txpool.Add(txs, false, false) source.txpool.Add(txs, false, false)

View File

@ -72,32 +72,23 @@ func (p *testTxPool) Has(hash common.Hash) bool {
// Get retrieves the transaction from local txpool with given // Get retrieves the transaction from local txpool with given
// tx hash. // tx hash.
func (p *testTxPool) Get(hash common.Hash) *txpool.Transaction { func (p *testTxPool) Get(hash common.Hash) *types.Transaction {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
return p.pool[hash]
if tx := p.pool[hash]; tx != nil {
return &txpool.Transaction{Tx: tx}
}
return nil
} }
// Add appends a batch of transactions to the pool, and notifies any // Add appends a batch of transactions to the pool, and notifies any
// listeners if the addition channel is non nil // listeners if the addition channel is non nil
func (p *testTxPool) Add(txs []*txpool.Transaction, local bool, sync bool) []error { func (p *testTxPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
unwrapped := make([]*types.Transaction, len(txs))
for i, tx := range txs {
unwrapped[i] = tx.Tx
}
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
for _, tx := range unwrapped { for _, tx := range txs {
p.pool[tx.Hash()] = tx p.pool[tx.Hash()] = tx
} }
p.txFeed.Send(core.NewTxsEvent{Txs: txs})
p.txFeed.Send(core.NewTxsEvent{Txs: unwrapped}) return make([]error, len(txs))
return make([]error, len(unwrapped))
} }
// Pending returns all the transactions known to the pool // Pending returns all the transactions known to the pool
@ -118,7 +109,7 @@ func (p *testTxPool) Pending(enforceTips bool) map[common.Address][]*txpool.Lazy
for _, tx := range batch { for _, tx := range batch {
pending[addr] = append(pending[addr], &txpool.LazyTransaction{ pending[addr] = append(pending[addr], &txpool.LazyTransaction{
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: &txpool.Transaction{Tx: tx}, Tx: tx,
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(), GasTipCap: tx.GasTipCap(),

View File

@ -81,8 +81,8 @@ func (p *Peer) broadcastTransactions() {
) )
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ { for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
if tx := p.txpool.Get(queue[i]); tx != nil { if tx := p.txpool.Get(queue[i]); tx != nil {
txs = append(txs, tx.Tx) txs = append(txs, tx)
size += common.StorageSize(tx.Tx.Size()) size += common.StorageSize(tx.Size())
} }
hashesCount++ hashesCount++
} }
@ -151,8 +151,8 @@ func (p *Peer) announceTransactions() {
for count = 0; count < len(queue) && size < maxTxPacketSize; count++ { for count = 0; count < len(queue) && size < maxTxPacketSize; count++ {
if tx := p.txpool.Get(queue[count]); tx != nil { if tx := p.txpool.Get(queue[count]); tx != nil {
pending = append(pending, queue[count]) pending = append(pending, queue[count])
pendingTypes = append(pendingTypes, tx.Tx.Type()) pendingTypes = append(pendingTypes, tx.Type())
pendingSizes = append(pendingSizes, uint32(tx.Tx.Size())) pendingSizes = append(pendingSizes, uint32(tx.Size()))
size += common.HashLength size += common.HashLength
} }
} }

View File

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
@ -90,7 +90,7 @@ type Backend interface {
// TxPool defines the methods needed by the protocol handler to serve transactions. // TxPool defines the methods needed by the protocol handler to serve transactions.
type TxPool interface { type TxPool interface {
// Get retrieves the transaction from the local txpool with the given hash. // Get retrieves the transaction from the local txpool with the given hash.
Get(hash common.Hash) *txpool.Transaction Get(hash common.Hash) *types.Transaction
} }
// MakeProtocols constructs the P2P protocol definitions for `eth`. // MakeProtocols constructs the P2P protocol definitions for `eth`.

View File

@ -503,7 +503,7 @@ func answerGetPooledTransactions(backend Backend, query GetPooledTransactionsPac
continue continue
} }
// If known, encode and queue for response packet // If known, encode and queue for response packet
if encoded, err := rlp.EncodeToBytes(tx.Tx); err != nil { if encoded, err := rlp.EncodeToBytes(tx); err != nil {
log.Error("Failed to encode transaction", "err", err) log.Error("Failed to encode transaction", "err", err)
} else { } else {
hashes = append(hashes, hash) hashes = append(hashes, hash)

View File

@ -518,7 +518,7 @@ func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) {
hash := tx.Hash() hash := tx.Hash()
stats[i] = txStatus(backend, hash) stats[i] = txStatus(backend, hash)
if stats[i].Status == txpool.TxStatusUnknown { if stats[i].Status == txpool.TxStatusUnknown {
if errs := backend.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, false, backend.AddTxsSync()); errs[0] != nil { if errs := backend.TxPool().Add([]*types.Transaction{tx}, false, backend.AddTxsSync()); errs[0] != nil {
stats[i].Error = errs[0].Error() stats[i].Error = errs[0].Error()
continue continue
} }

View File

@ -88,7 +88,7 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
} }
groups[addr] = append(groups[addr], &txpool.LazyTransaction{ groups[addr] = append(groups[addr], &txpool.LazyTransaction{
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: &txpool.Transaction{Tx: tx}, Tx: tx,
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(), GasTipCap: tx.GasTipCap(),
@ -101,7 +101,7 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
txs := types.Transactions{} txs := types.Transactions{}
for tx := txset.Peek(); tx != nil; tx = txset.Peek() { for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
txs = append(txs, tx.Tx.Tx) txs = append(txs, tx.Tx)
txset.Shift() txset.Shift()
} }
if len(txs) != expectedCount { if len(txs) != expectedCount {
@ -153,7 +153,7 @@ func TestTransactionTimeSort(t *testing.T) {
groups[addr] = append(groups[addr], &txpool.LazyTransaction{ groups[addr] = append(groups[addr], &txpool.LazyTransaction{
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: &txpool.Transaction{Tx: tx}, Tx: tx,
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(), GasTipCap: tx.GasTipCap(),
@ -164,7 +164,7 @@ func TestTransactionTimeSort(t *testing.T) {
txs := types.Transactions{} txs := types.Transactions{}
for tx := txset.Peek(); tx != nil; tx = txset.Peek() { for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
txs = append(txs, tx.Tx.Tx) txs = append(txs, tx.Tx)
txset.Shift() txset.Shift()
} }
if len(txs) != len(keys) { if len(txs) != len(keys) {

View File

@ -30,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -133,7 +132,7 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
if err := backend.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, true, false); err != nil { if err := backend.TxPool().Add([]*types.Transaction{tx}, true, false); err != nil {
panic(err) panic(err)
} }
nonces[index]++ nonces[index]++

View File

@ -539,7 +539,7 @@ func (w *worker) mainLoop() {
acc, _ := types.Sender(w.current.signer, tx) acc, _ := types.Sender(w.current.signer, tx)
txs[acc] = append(txs[acc], &txpool.LazyTransaction{ txs[acc] = append(txs[acc], &txpool.LazyTransaction{
Hash: tx.Hash(), Hash: tx.Hash(),
Tx: &txpool.Transaction{Tx: tx}, Tx: tx.WithoutBlobTxSidecar(),
Time: tx.Time(), Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(), GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(), GasTipCap: tx.GasTipCap(),
@ -734,18 +734,18 @@ func (w *worker) updateSnapshot(env *environment) {
w.snapshotState = env.state.Copy() w.snapshotState = env.state.Copy()
} }
func (w *worker) commitTransaction(env *environment, tx *txpool.Transaction) ([]*types.Log, error) { func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
var ( var (
snap = env.state.Snapshot() snap = env.state.Snapshot()
gp = env.gasPool.Gas() gp = env.gasPool.Gas()
) )
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx.Tx, &env.header.GasUsed, *w.chain.GetVMConfig()) receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
if err != nil { if err != nil {
env.state.RevertToSnapshot(snap) env.state.RevertToSnapshot(snap)
env.gasPool.SetGas(gp) env.gasPool.SetGas(gp)
return nil, err return nil, err
} }
env.txs = append(env.txs, tx.Tx) env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt) env.receipts = append(env.receipts, receipt)
return receipt.Logs, nil return receipt.Logs, nil
@ -778,30 +778,30 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
tx := ltx.Resolve() tx := ltx.Resolve()
if tx == nil { if tx == nil {
log.Warn("Ignoring evicted transaction") log.Warn("Ignoring evicted transaction")
txs.Pop() txs.Pop()
continue continue
} }
// Error may be ignored here. The error has already been checked // Error may be ignored here. The error has already been checked
// during transaction acceptance is the transaction pool. // during transaction acceptance is the transaction pool.
from, _ := types.Sender(env.signer, tx.Tx) from, _ := types.Sender(env.signer, tx)
// Check whether the tx is replay protected. If we're not in the EIP155 hf // Check whether the tx is replay protected. If we're not in the EIP155 hf
// phase, start ignoring the sender until we do. // phase, start ignoring the sender until we do.
if tx.Tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
log.Trace("Ignoring reply protected transaction", "hash", tx.Tx.Hash(), "eip155", w.chainConfig.EIP155Block) log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block)
txs.Pop() txs.Pop()
continue continue
} }
// Start executing the transaction // Start executing the transaction
env.state.SetTxContext(tx.Tx.Hash(), env.tcount) env.state.SetTxContext(tx.Hash(), env.tcount)
logs, err := w.commitTransaction(env, tx) logs, err := w.commitTransaction(env, tx)
switch { switch {
case errors.Is(err, core.ErrNonceTooLow): case errors.Is(err, core.ErrNonceTooLow):
// New head notification data race between the transaction pool and miner, shift // New head notification data race between the transaction pool and miner, shift
log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Tx.Nonce()) log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
txs.Shift() txs.Shift()
case errors.Is(err, nil): case errors.Is(err, nil):
@ -813,7 +813,7 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
default: default:
// Transaction is regarded as invalid, drop all consecutive transactions from // Transaction is regarded as invalid, drop all consecutive transactions from
// the same sender because of `nonce-too-high` clause. // the same sender because of `nonce-too-high` clause.
log.Debug("Transaction failed, account skipped", "hash", tx.Tx.Hash(), "err", err) log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
txs.Pop() txs.Pop()
} }
} }

View File

@ -63,7 +63,7 @@ var (
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
// Test transactions // Test transactions
pendingTxs []*txpool.Transaction pendingTxs []*types.Transaction
newTxs []*types.Transaction newTxs []*types.Transaction
testConfig = &Config{ testConfig = &Config{
@ -93,7 +93,7 @@ func init() {
Gas: params.TxGas, Gas: params.TxGas,
GasPrice: big.NewInt(params.InitialBaseFee), GasPrice: big.NewInt(params.InitialBaseFee),
}) })
pendingTxs = append(pendingTxs, &txpool.Transaction{Tx: tx1}) pendingTxs = append(pendingTxs, tx1)
tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{ tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{
Nonce: 1, Nonce: 1,
@ -194,8 +194,8 @@ func TestGenerateAndImportBlock(t *testing.T) {
w.start() w.start()
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
b.txPool.Add([]*txpool.Transaction{{Tx: b.newRandomTx(true)}}, true, false) b.txPool.Add([]*types.Transaction{b.newRandomTx(true)}, true, false)
b.txPool.Add([]*txpool.Transaction{{Tx: b.newRandomTx(false)}}, true, false) b.txPool.Add([]*types.Transaction{b.newRandomTx(false)}, true, false)
select { select {
case ev := <-sub.Chan(): case ev := <-sub.Chan():

View File

@ -25,7 +25,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/fetcher" "github.com/ethereum/go-ethereum/eth/fetcher"
) )
@ -80,7 +79,7 @@ func Fuzz(input []byte) int {
f := fetcher.NewTxFetcherForTests( f := fetcher.NewTxFetcherForTests(
func(common.Hash) bool { return false }, func(common.Hash) bool { return false },
func(txs []*txpool.Transaction) []error { func(txs []*types.Transaction) []error {
return make([]error, len(txs)) return make([]error, len(txs))
}, },
func(string, []common.Hash) error { return nil }, func(string, []common.Hash) error { return nil },