Merge remote-tracking branch 'upstream/master' into abi-fixes

This commit is contained in:
ulmentflam 2023-09-11 11:12:39 -04:00
commit 0f65144fc0
No known key found for this signature in database
61 changed files with 1098 additions and 524 deletions

View file

@ -120,14 +120,14 @@ var (
// Distros for which packages are created.
// Note: vivid is unsupported because there is no golang-1.6 package for it.
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish,
// kinetic
debDistroGoBoots = map[string]string{
"trusty": "golang-1.11", // EOL: 04/2024
"xenial": "golang-go", // EOL: 04/2026
"bionic": "golang-go", // EOL: 04/2028
"focal": "golang-go", // EOL: 04/2030
"jammy": "golang-go", // EOL: 04/2032
"kinetic": "golang-go", // EOL: 07/2023
"lunar": "golang-go", // EOL: 01/2024
}

View file

@ -77,7 +77,7 @@ func (c *Chain) RootAt(height int) common.Hash {
// ForkID gets the fork id of the chain.
func (c *Chain) ForkID() forkid.ID {
return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len()), c.blocks[0].Time())
return forkid.NewID(c.chainConfig, c.blocks[0], uint64(c.Len()), c.blocks[0].Time())
}
// Shorten returns a copy chain of a desired height from the imported

View file

@ -1719,7 +1719,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
cfg.TransactionHistory = ctx.Uint64(TransactionHistoryFlag.Name)
} else if ctx.IsSet(TxLookupLimitFlag.Name) {
log.Warn("The flag --txlookuplimit is deprecated and will be removed, please use --history.transactions")
cfg.TransactionHistory = ctx.Uint64(TransactionHistoryFlag.Name)
cfg.TransactionHistory = ctx.Uint64(TxLookupLimitFlag.Name)
}
if ctx.String(GCModeFlag.Name) == "archive" && cfg.TransactionHistory != 0 {
cfg.TransactionHistory = 0

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 {
return fmt.Errorf("transaction root hash mismatch (header value %x, calculated %x)", header.TxHash, hash)
}
// Withdrawals are present after the Shanghai fork.
if header.WithdrawalsHash != nil {
// 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
return errors.New("withdrawals present in block body")
}
// Blob transactions may be present after the Cancun fork.
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
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)
// happens in the state_transition check.
// happens in StateTransition.
}
// Check blob gas usage.
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
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")
}
}
// Ancestor block must be known.
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor

View file

@ -1085,19 +1085,30 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
ancientReceipts, liveReceipts []types.Receipts
)
// 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 blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(),
"prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash())
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(),
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
prev := blockChain[i-1]
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
log.Error("Non contiguous receipt insert",
"number", block.Number(), "hash", block.Hash(), "parent", block.ParentHash(),
"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 {
ancientBlocks, ancientReceipts = append(ancientBlocks, blockChain[i]), append(ancientReceipts, receiptChain[i])
if block.NumberU64() <= ancientLimit {
ancientBlocks, ancientReceipts = append(ancientBlocks, block), append(ancientReceipts, receiptChain[i])
} 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

@ -73,9 +73,9 @@ type ID struct {
type Filter func(id ID) error
// NewID calculates the Ethereum fork ID from the chain config, genesis hash, head and time.
func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) ID {
func NewID(config *params.ChainConfig, genesis *types.Block, head, time uint64) ID {
// Calculate the starting checksum from the genesis hash
hash := crc32.ChecksumIEEE(genesis[:])
hash := crc32.ChecksumIEEE(genesis.Hash().Bytes())
// Calculate the current fork checksum and the next fork block
forksByBlock, forksByTime := gatherForks(config)
@ -88,6 +88,10 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) I
return ID{Hash: checksumToBytes(hash), Next: fork}
}
for _, fork := range forksByTime {
if fork <= genesis.Time() {
// Fork active in genesis, skip in forkid calculation
continue
}
if fork <= time {
// Fork already passed, checksum the previous hash and fork timestamp
hash = checksumUpdate(hash, fork)
@ -104,7 +108,7 @@ func NewIDWithChain(chain Blockchain) ID {
return NewID(
chain.Config(),
chain.Genesis().Hash(),
chain.Genesis(),
head.Number.Uint64(),
head.Time,
)

View file

@ -18,10 +18,14 @@ package forkid
import (
"bytes"
"hash/crc32"
"math"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
@ -36,13 +40,13 @@ func TestCreation(t *testing.T) {
}
tests := []struct {
config *params.ChainConfig
genesis common.Hash
genesis *types.Block
cases []testcase
}{
// Mainnet test cases
{
params.MainnetChainConfig,
params.MainnetGenesisHash,
core.DefaultGenesisBlock().ToBlock(),
[]testcase{
{0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced
{1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block
@ -77,7 +81,7 @@ func TestCreation(t *testing.T) {
// Goerli test cases
{
params.GoerliChainConfig,
params.GoerliGenesisHash,
core.DefaultGoerliGenesisBlock().ToBlock(),
[]testcase{
{0, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block
{1561650, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block
@ -94,7 +98,7 @@ func TestCreation(t *testing.T) {
// Sepolia test cases
{
params.SepoliaChainConfig,
params.SepoliaGenesisHash,
core.DefaultSepoliaGenesisBlock().ToBlock(),
[]testcase{
{0, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block
{1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block
@ -382,3 +386,55 @@ func TestEncoding(t *testing.T) {
}
}
}
// Tests that time-based forks which are active at genesis are not included in
// forkid hash.
func TestTimeBasedForkInGenesis(t *testing.T) {
var (
time = uint64(1690475657)
genesis = types.NewBlockWithHeader(&types.Header{Time: time})
forkidHash = checksumToBytes(crc32.ChecksumIEEE(genesis.Hash().Bytes()))
config = func(shanghai, cancun uint64) *params.ChainConfig {
return &params.ChainConfig{
ChainID: big.NewInt(1337),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
MergeNetsplitBlock: big.NewInt(0),
ShanghaiTime: &shanghai,
CancunTime: &cancun,
Ethash: new(params.EthashConfig),
}
}
)
tests := []struct {
config *params.ChainConfig
want ID
}{
// Shanghai active before genesis, skip
{config(time-1, time+1), ID{Hash: forkidHash, Next: time + 1}},
// Shanghai active at genesis, skip
{config(time, time+1), ID{Hash: forkidHash, Next: time + 1}},
// Shanghai not active, skip
{config(time+1, time+2), ID{Hash: forkidHash, Next: time + 1}},
}
for _, tt := range tests {
if have := NewID(tt.config, genesis, 0, time); have != tt.want {
t.Fatalf("incorrect forkid hash: have %x, want %x", have, tt.want)
}
}
}

View file

@ -19,6 +19,7 @@ package blobpool
import (
"container/heap"
"errors"
"fmt"
"math"
"math/big"
@ -35,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool"
"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/log"
"github.com/ethereum/go-ethereum/metrics"
@ -83,16 +83,6 @@ const (
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
// 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
@ -455,22 +445,27 @@ func (p *BlobPool) Close() error {
// parseTransaction is a callback method on pool creation that gets called for
// each transaction on disk to create the in-memory metadata index.
func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error {
item := new(blobTx)
if err := rlp.DecodeBytes(blob, item); err != nil {
tx := new(types.Transaction)
if err := rlp.DecodeBytes(blob, tx); err != nil {
// This path is impossible unless the disk data representation changes
// across restarts. For that ever unprobable case, recover gracefully
// by ignoring this data entry.
log.Error("Failed to decode blob pool entry", "id", id, "err", 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 {
// This path is impossible unless the signature validity changes across
// restarts. For that ever unprobable case, recover gracefully by ignoring
// 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
}
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)
return
}
item := new(blobTx)
if err = rlp.DecodeBytes(data, item); err != nil {
var tx types.Transaction
if err = rlp.DecodeBytes(data, tx); err != nil {
log.Error("Blobs corrupted for included transaction", "from", addr, "nonce", nonce, "id", id, "err", err)
return
}
block, ok := inclusions[item.Tx.Hash()]
block, ok := inclusions[tx.Hash()]
if !ok {
log.Warn("Blob transaction swapped out by signer", "from", addr, "nonce", nonce, "id", id)
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)
return
}
@ -760,7 +755,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
for addr, txs := range reinject {
// Blindly push all the lost transactions back into the pool
for _, tx := range txs {
p.reinject(addr, tx)
p.reinject(addr, tx.Hash())
}
// Recheck the account's pooled transactions to drop included and
// 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
// be done once for all transactions belonging to an account after all individual
// 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
// 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 {
log.Error("Blobs unavailable, dropping reorged tx", "err", err)
return
}
// Serialize the transaction back into the primary datastore
blob, err := rlp.EncodeToBytes(&blobTx{Tx: tx, Blobs: blobs, Commits: commits, Proofs: proofs})
// TODO: seems like an easy optimization here would be getting the serialized tx
// from limbo instead of re-serializing it here.
// Serialize the transaction back into the primary datastore.
blob, err := rlp.EncodeToBytes(tx)
if err != nil {
log.Error("Failed to encode transaction for storage", "hash", tx.Hash(), "err", err)
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)
return
}
// Update the indixes and metrics
meta := newBlobTxMeta(id, p.store.Size(id), tx)
if _, ok := p.index[addr]; !ok {
if err := p.reserve(addr, true); err != nil {
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
// 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
// consensus rules
baseOpts := &txpool.ValidationOptions{
@ -1032,7 +1030,7 @@ func (p *BlobPool) validateTx(tx *types.Transaction, blobs []kzg4844.Blob, commi
MaxSize: txMaxSize,
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
}
// 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.
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
// the pool and the amount of time actually spent on pulling the data from disk.
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)
return nil
}
item := new(blobTx)
item := new(types.Transaction)
if err = rlp.DecodeBytes(data, item); err != nil {
log.Error("Blobs corrupted for traced transaction", "hash", hash, "id", id, "err", err)
return nil
}
return &txpool.Transaction{
Tx: item.Tx,
BlobTxBlobs: item.Blobs,
BlobTxCommits: item.Commits,
BlobTxProofs: item.Proofs,
}
return item
}
// Add inserts a set of blob transactions into the pool if they pass validation (both
// 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))
for i, tx := range txs {
errs[i] = p.add(tx.Tx, tx.BlobTxBlobs, tx.BlobTxCommits, tx.BlobTxProofs)
errs[i] = p.add(tx)
}
return errs
}
// Add inserts a new blob transaction into the pool if it passes validation (both
// 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
// only even pulled form the network, so this method will act as the overload
// protection for fetches.
@ -1178,7 +1171,7 @@ func (p *BlobPool) add(tx *types.Transaction, blobs []kzg4844.Blob, commits []kz
}(time.Now())
// 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)
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,
// 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 {
log.Error("Failed to encode transaction for storage", "hash", tx.Hash(), "err", 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
// the blob pool.
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))
return tx
blobtx := makeUnsignedTx(nonce, gasTipCap, gasFeeCap, blobFeeCap)
return types.MustSignNewTx(key, types.LatestSigner(testChainConfig), blobtx)
}
// 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),
BlobHashes: []common.Hash{emptyBlobVHash},
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),
S: new(uint256.Int),
})
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
badsig, _ := store.Put(blob)
// 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
tx := makeTx(nonce, 1, 1, 1, gapper)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
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
tx := makeTx(nonce, 1, 1, 1, dangler)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
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
tx := makeTx(nonce, 1, 1, 1, filler)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
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
tx := makeTx(nonce, 1, 1, 1, overlapper)
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
if nonce >= 2 {
@ -419,7 +424,7 @@ func TestOpenDrops(t *testing.T) {
} else {
tx = makeTx(uint64(i), 1, 1, 1, underpayer)
}
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
underpaid[id] = struct{}{}
@ -438,7 +443,7 @@ func TestOpenDrops(t *testing.T) {
} else {
tx = makeTx(uint64(i), 1, 1, 1, outpricer)
}
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
if i < 2 {
@ -460,7 +465,7 @@ func TestOpenDrops(t *testing.T) {
} else {
tx = makeTx(nonce, 1, 1, 1, exceeder)
}
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
exceeded[id] = struct{}{}
@ -478,7 +483,7 @@ func TestOpenDrops(t *testing.T) {
} else {
tx = makeTx(nonce, 1, 1, 1, overdrafter)
}
blob, _ := rlp.EncodeToBytes(&blobTx{Tx: tx})
blob, _ := rlp.EncodeToBytes(tx)
id, _ := store.Put(blob)
if nonce < 1 {
@ -494,7 +499,7 @@ func TestOpenDrops(t *testing.T) {
overcapped = make(map[uint64]struct{})
)
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)
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
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.Close()
@ -718,9 +723,9 @@ func TestOpenHeap(t *testing.T) {
tx2 = makeTx(0, 1, 800, 70, key2)
tx3 = makeTx(0, 1, 1500, 110, key3)
blob1, _ = rlp.EncodeToBytes(&blobTx{Tx: tx1})
blob2, _ = rlp.EncodeToBytes(&blobTx{Tx: tx2})
blob3, _ = rlp.EncodeToBytes(&blobTx{Tx: tx3})
blob1, _ = rlp.EncodeToBytes(tx1)
blob2, _ = rlp.EncodeToBytes(tx2)
blob3, _ = rlp.EncodeToBytes(tx3)
heapOrder = []common.Address{addr2, addr1, addr3}
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)
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}})
blob2, _ = rlp.EncodeToBytes(&blobTx{Tx: tx2, Blobs: []kzg4844.Blob{emptyBlob}, Commits: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}})
blob3, _ = rlp.EncodeToBytes(&blobTx{Tx: tx3, Blobs: []kzg4844.Blob{emptyBlob}, Commits: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}})
blob1, _ = rlp.EncodeToBytes(tx1)
blob2, _ = rlp.EncodeToBytes(tx2)
blob3, _ = rlp.EncodeToBytes(tx3)
keep = []common.Address{addr1, addr3}
drop = []common.Address{addr2}
@ -1210,10 +1215,8 @@ func TestAdd(t *testing.T) {
// Sign the seed transactions and store them in the data store
for _, tx := range seed.txs {
var (
signed, _ = types.SignNewTx(keys[acc], types.LatestSigner(testChainConfig), tx)
blob, _ = rlp.EncodeToBytes(&blobTx{Tx: signed, Blobs: []kzg4844.Blob{emptyBlob}, Commits: []kzg4844.Commitment{emptyBlobCommit}, Proofs: []kzg4844.Proof{emptyBlobProof}})
)
signed := types.MustSignNewTx(keys[acc], types.LatestSigner(testChainConfig), tx)
blob, _ := rlp.EncodeToBytes(signed)
store.Put(blob)
}
}
@ -1236,7 +1239,7 @@ func TestAdd(t *testing.T) {
// Add each transaction one by one, verifying the pool internals in between
for j, add := range tt.adds {
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)
}
verifyPoolInternals(t, pool)

View file

@ -21,7 +21,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"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/rlp"
"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
// finality eviction.
type limboBlob struct {
Owner common.Hash // Owner transaction's hash to support resurrecting reorged txs
Block uint64 // Block in which the blob transaction was included
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
TxHash common.Hash // Owner transaction's hash to support resurrecting reorged txs
Block uint64 // Block in which the blob transaction was included
Tx *types.Transaction
}
// 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)
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
// inserted into the limbo which was already part of if. Recover gracefully
// 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")
}
l.index[item.Owner] = id
l.index[item.TxHash] = id
if _, ok := l.groups[item.Block]; !ok {
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
}
@ -139,14 +135,14 @@ func (l *limbo) finalize(final *types.Header) {
// push stores a new blob transaction into the limbo, waiting until finality for
// 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
// 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)
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)
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
// it at the same time. This method should be used when a previously included blob
// 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
// can happen for example if a blob transaction is mined without pushing it
// into the network first.
id, ok := l.index[tx]
if !ok {
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)
if err != nil {
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
@ -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
// issues, others caused by signers mining MEV stuff or swapping transactions. In
// 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
// can happen for example if a blob transaction is mined without pushing it
// into the network first.
id, ok := l.index[tx]
id, ok := l.index[txhash]
if !ok {
log.Trace("Limbo cannot update non-tracked blobs", "tx", tx)
log.Trace("Limbo cannot update non-tracked blobs", "tx", txhash)
return
}
// If there was no change in the blob's inclusion block, don't mess around
// with heavy database operations.
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
}
// 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.
item, err := l.getAndDrop(id)
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
}
if err := l.setAndIndex(tx, block, item.Blobs, item.Commits, item.Proofs); err != nil {
log.Error("Failed to set and index limboed blobs", "tx", tx, "err", err)
if err := l.setAndIndex(item.Tx, block); err != nil {
log.Error("Failed to set and index limboed blobs", "tx", txhash, "err", err)
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
@ -220,7 +216,7 @@ func (l *limbo) getAndDrop(id uint64) (*limboBlob, error) {
if err = rlp.DecodeBytes(data, item); err != nil {
return nil, err
}
delete(l.index, item.Owner)
delete(l.index, item.TxHash)
delete(l.groups[item.Block], id)
if len(l.groups[item.Block]) == 0 {
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
// 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{
Owner: tx,
Block: block,
Blobs: blobs,
Commits: commits,
Proofs: proofs,
TxHash: txhash,
Block: block,
Tx: tx,
}
data, err := rlp.EncodeToBytes(item)
if err != nil {
@ -249,10 +244,10 @@ func (l *limbo) setAndIndex(tx common.Hash, block uint64, blobs []kzg4844.Blob,
if err != nil {
return err
}
l.index[tx] = id
l.index[txhash] = id
if _, ok := l.groups[block]; !ok {
l.groups[block] = make(map[uint64]common.Hash)
}
l.groups[block][id] = tx
l.groups[block][id] = txhash
return nil
}

View file

@ -535,7 +535,7 @@ func (pool *LegacyPool) Pending(enforceTips bool) map[common.Address][]*txpool.L
lazies[i] = &txpool.LazyTransaction{
Pool: pool,
Hash: txs[i].Hash(),
Tx: &txpool.Transaction{Tx: txs[i]},
Tx: txs[i],
Time: txs[i].Time(),
GasFeeCap: txs[i].GasFeeCap(),
GasTipCap: txs[i].GasTipCap(),
@ -588,7 +588,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
if local {
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 nil
@ -900,26 +900,13 @@ func (pool *LegacyPool) promoteTx(addr common.Address, hash common.Hash, tx *typ
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
// 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
// reorganization and event propagation.
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
@ -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
// reorganization and internal event propagation.
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
@ -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.
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.
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.
func (pool *LegacyPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
// 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 []*types.Transaction, local, sync bool) []error {
// Filter out known ones without obtaining the pool lock or recovering signatures
var (
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.
func (pool *LegacyPool) Get(hash common.Hash) *txpool.Transaction {
func (pool *LegacyPool) Get(hash common.Hash) *types.Transaction {
tx := pool.get(hash)
if tx == nil {
return nil
}
return &txpool.Transaction{Tx: tx}
return tx
}
// get returns a transaction if it is contained in the pool and nil otherwise.

View file

@ -53,9 +53,10 @@ func (h *nonceHeap) Pop() interface{} {
// sortedMap is a nonce->transaction hash map with a heap based index to allow
// iterating over the contents in a nonce-incrementing way.
type sortedMap struct {
items map[uint64]*types.Transaction // Hash map storing the transaction data
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
cache types.Transactions // Cache of the transactions already sorted
items map[uint64]*types.Transaction // Hash map storing the transaction data
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
cache types.Transactions // Cache of the transactions already sorted
cacheMu sync.Mutex // Mutex covering the cache
}
// newSortedMap creates a new nonce-sorted transaction map.
@ -78,7 +79,9 @@ func (m *sortedMap) Put(tx *types.Transaction) {
if m.items[nonce] == nil {
heap.Push(m.index, nonce)
}
m.cacheMu.Lock()
m.items[nonce], m.cache = tx, nil
m.cacheMu.Unlock()
}
// Forward removes all transactions from the map with a nonce lower than the
@ -94,9 +97,11 @@ func (m *sortedMap) Forward(threshold uint64) types.Transactions {
delete(m.items, nonce)
}
// If we had a cached order, shift the front
m.cacheMu.Lock()
if m.cache != nil {
m.cache = m.cache[len(removed):]
}
m.cacheMu.Unlock()
return removed
}
@ -120,7 +125,9 @@ func (m *sortedMap) reheap() {
*m.index = append(*m.index, nonce)
}
heap.Init(m.index)
m.cacheMu.Lock()
m.cache = nil
m.cacheMu.Unlock()
}
// filter is identical to Filter, but **does not** regenerate the heap. This method
@ -136,7 +143,9 @@ func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transacti
}
}
if len(removed) > 0 {
m.cacheMu.Lock()
m.cache = nil
m.cacheMu.Unlock()
}
return removed
}
@ -160,9 +169,11 @@ func (m *sortedMap) Cap(threshold int) types.Transactions {
heap.Init(m.index)
// If we had a cache, shift the back
m.cacheMu.Lock()
if m.cache != nil {
m.cache = m.cache[:len(m.cache)-len(drops)]
}
m.cacheMu.Unlock()
return drops
}
@ -182,7 +193,9 @@ func (m *sortedMap) Remove(nonce uint64) bool {
}
}
delete(m.items, nonce)
m.cacheMu.Lock()
m.cache = nil
m.cacheMu.Unlock()
return true
}
@ -206,7 +219,9 @@ func (m *sortedMap) Ready(start uint64) types.Transactions {
delete(m.items, next)
heap.Pop(m.index)
}
m.cacheMu.Lock()
m.cache = nil
m.cacheMu.Unlock()
return ready
}
@ -217,6 +232,8 @@ func (m *sortedMap) Len() int {
}
func (m *sortedMap) flatten() types.Transactions {
m.cacheMu.Lock()
defer m.cacheMu.Unlock()
// If the sorting was not cached yet, create and cache it
if m.cache == nil {
m.cache = make(types.Transactions, 0, len(m.items))
@ -232,8 +249,8 @@ func (m *sortedMap) flatten() types.Transactions {
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
func (m *sortedMap) Flatten() types.Transactions {
// Copy the cache to prevent accidental modifications
cache := m.flatten()
// Copy the cache to prevent accidental modification
txs := make(types.Transactions, len(cache))
copy(txs, cache)
return txs

View file

@ -23,27 +23,16 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"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
// enough for the miner and other APIs to handle large batches of transactions;
// and supports pulling up the entire transaction when really needed.
type LazyTransaction struct {
Pool SubPool // Transaction subpool to pull the real transaction up
Hash common.Hash // Transaction hash to pull up if needed
Tx *Transaction // Transaction if already resolved
Pool SubPool // Transaction subpool to pull the real transaction up
Hash common.Hash // Transaction hash to pull up if needed
Tx *types.Transaction // Transaction if already resolved
Time time.Time // Time when the transaction was first seen
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
// maintained by the transaction pool.
func (ltx *LazyTransaction) Resolve() *Transaction {
func (ltx *LazyTransaction) Resolve() *types.Transaction {
if ltx.Tx == nil {
ltx.Tx = ltx.Pool.Get(ltx.Hash)
}
@ -99,12 +88,12 @@ type SubPool interface {
Has(hash common.Hash) bool
// 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
// to the large transaction churn, add may postpone fully integrating the tx
// 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
// 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.
func (p *TxPool) Get(hash common.Hash) *Transaction {
func (p *TxPool) Get(hash common.Hash) *types.Transaction {
for _, subpool := range p.subpools {
if tx := subpool.Get(hash); tx != nil {
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
// to the large transaction churn, add may postpone fully integrating the tx
// 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
// happen that we receive merged batches, but better graceful than strange
// errors.
//
// 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.
txsets := make([][]*Transaction, len(p.subpools))
txsets := make([][]*types.Transaction, len(p.subpools))
splits := make([]int, len(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
for j, subpool := range p.subpools {
if subpool.Filter(tx.Tx) {
if subpool.Filter(tx) {
txsets[j] = append(txsets[j], tx)
splits[i] = j
break

View file

@ -46,7 +46,7 @@ type ValidationOptions struct {
//
// This check is public to allow different transaction pools to check the basic
// 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
if opts.Accept&(1<<tx.Type()) == 0 {
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
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
// data match up before doing any expensive validations
hashes := tx.BlobHashes()
@ -119,37 +123,44 @@ func ValidateTransaction(tx *types.Transaction, blobs []kzg4844.Blob, commits []
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)
}
if len(blobs) != len(hashes) {
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(blobs), len(hashes))
if err := validateBlobSidecar(hashes, sidecar); err != nil {
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))
}
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()
}
return nil
}
var vhash common.Hash
vhash[0] = params.BlobTxHashVersion
copy(vhash[1:], hash[1:])
func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) error {
if len(sidecar.Blobs) != len(hashes) {
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 {
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, vhash, want)
}
var vhash common.Hash
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
for i := range blobs {
if err := kzg4844.VerifyBlobProof(blobs[i], commits[i], proofs[i]); err != nil {
return fmt.Errorf("invalid blob %d: %v", i, err)
}
}
// Blob commitments match with the hashes in the transaction, verify the
// blobs themselves via KZG
for i := range sidecar.Blobs {
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

View file

@ -82,9 +82,6 @@ type TxData interface {
value() *big.Int
nonce() uint64
to() *common.Address
blobGas() uint64
blobGasFeeCap() *big.Int
blobHashes() []common.Hash
rawSignatureValues() (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.
// Method implementations can use 'dst' to store the result.
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
encode(*bytes.Buffer) error
decode([]byte) error
}
// 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.
func (tx *Transaction) encodeTyped(w *bytes.Buffer) error {
w.WriteByte(tx.Type())
return rlp.Encode(w, tx.inner)
return tx.inner.encode(w)
}
// MarshalBinary returns the canonical encoding of the transaction.
@ -186,22 +186,19 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
if len(b) <= 1 {
return nil, errShortTypedTx
}
var inner TxData
switch b[0] {
case AccessListTxType:
var inner AccessListTx
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
inner = new(AccessListTx)
case DynamicFeeTxType:
var inner DynamicFeeTx
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
inner = new(DynamicFeeTx)
case BlobTxType:
var inner BlobTx
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
inner = new(BlobTx)
default:
return nil, ErrTxTypeNotSupported
}
err := inner.decode(b[1:])
return inner, err
}
// 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.
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.
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)
}
// 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.
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.
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
@ -428,13 +468,24 @@ func (tx *Transaction) Size() uint64 {
if size := tx.size.Load(); size != nil {
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)
rlp.Encode(&c, &tx.inner)
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)
return size
}

View file

@ -373,20 +373,20 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
itx.BlobHashes = dec.BlobVersionedHashes
// signature R
var ok bool
var overflow bool
if dec.R == nil {
return errors.New("missing required field 'r' in transaction")
}
itx.R, ok = uint256.FromBig((*big.Int)(dec.R))
if !ok {
itx.R, overflow = uint256.FromBig((*big.Int)(dec.R))
if overflow {
return errors.New("'r' value overflows uint256")
}
// signature S
if dec.S == nil {
return errors.New("missing required field 's' in transaction")
}
itx.S, ok = uint256.FromBig((*big.Int)(dec.S))
if !ok {
itx.S, overflow = uint256.FromBig((*big.Int)(dec.S))
if overflow {
return errors.New("'s' value overflows uint256")
}
// signature V
@ -394,8 +394,8 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
if err != nil {
return err
}
itx.V, ok = uint256.FromBig(vbig)
if !ok {
itx.V, overflow = uint256.FromBig(vbig)
if overflow {
return errors.New("'v' value overflows uint256")
}
if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {

View file

@ -17,9 +17,11 @@
package types
import (
"bytes"
"math/big"
"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
@ -94,20 +96,17 @@ func (tx *AccessListTx) copy() TxData {
}
// accessors for innerTx.
func (tx *AccessListTx) txType() byte { return AccessListTxType }
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
func (tx *AccessListTx) data() []byte { return tx.Data }
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
func (tx *AccessListTx) gasPrice() *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) value() *big.Int { return tx.Value }
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
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) txType() byte { return AccessListTxType }
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
func (tx *AccessListTx) data() []byte { return tx.Data }
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
func (tx *AccessListTx) gasPrice() *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) value() *big.Int { return tx.Value }
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
func (tx *AccessListTx) to() *common.Address { return tx.To }
func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
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) {
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
import (
"bytes"
"crypto/sha256"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
@ -38,12 +42,56 @@ type BlobTx struct {
BlobFeeCap *uint256.Int // a.k.a. maxFeePerBlobGas
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
V *uint256.Int `json:"v" gencodec:"required"`
R *uint256.Int `json:"r" 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.
func (tx *BlobTx) copy() TxData {
cpy := &BlobTx{
@ -90,24 +138,29 @@ func (tx *BlobTx) copy() TxData {
if tx.S != nil {
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
}
// accessors for innerTx.
func (tx *BlobTx) txType() byte { return BlobTxType }
func (tx *BlobTx) chainID() *big.Int { return tx.ChainID.ToBig() }
func (tx *BlobTx) accessList() AccessList { return tx.AccessList }
func (tx *BlobTx) data() []byte { return tx.Data }
func (tx *BlobTx) gas() uint64 { return tx.Gas }
func (tx *BlobTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *BlobTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
func (tx *BlobTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *BlobTx) value() *big.Int { return tx.Value.ToBig() }
func (tx *BlobTx) nonce() uint64 { return tx.Nonce }
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) blobGasFeeCap() *big.Int { return tx.BlobFeeCap.ToBig() }
func (tx *BlobTx) blobHashes() []common.Hash { return tx.BlobHashes }
func (tx *BlobTx) txType() byte { return BlobTxType }
func (tx *BlobTx) chainID() *big.Int { return tx.ChainID.ToBig() }
func (tx *BlobTx) accessList() AccessList { return tx.AccessList }
func (tx *BlobTx) data() []byte { return tx.Data }
func (tx *BlobTx) gas() uint64 { return tx.Gas }
func (tx *BlobTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *BlobTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
func (tx *BlobTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
func (tx *BlobTx) value() *big.Int { return tx.Value.ToBig() }
func (tx *BlobTx) nonce() uint64 { return tx.Nonce }
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) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil {
@ -130,3 +183,64 @@ func (tx *BlobTx) setSignatureValues(chainID, v, r, s *big.Int) {
tx.R.SetFromBig(r)
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
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
// DynamicFeeTx represents an EIP-1559 transaction.
@ -83,20 +85,17 @@ func (tx *DynamicFeeTx) copy() TxData {
}
// accessors for innerTx.
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
func (tx *DynamicFeeTx) data() []byte { return tx.Data }
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
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) txType() byte { return DynamicFeeTxType }
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
func (tx *DynamicFeeTx) data() []byte { return tx.Data }
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
func (tx *DynamicFeeTx) to() *common.Address { return tx.To }
func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
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) {
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
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
@ -91,20 +92,17 @@ func (tx *LegacyTx) copy() TxData {
}
// accessors for innerTx.
func (tx *LegacyTx) txType() byte { return LegacyTxType }
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) }
func (tx *LegacyTx) accessList() AccessList { return nil }
func (tx *LegacyTx) data() []byte { return tx.Data }
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
func (tx *LegacyTx) gasPrice() *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) value() *big.Int { return tx.Value }
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
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) txType() byte { return LegacyTxType }
func (tx *LegacyTx) chainID() *big.Int { return deriveChainId(tx.V) }
func (tx *LegacyTx) accessList() AccessList { return nil }
func (tx *LegacyTx) data() []byte { return tx.Data }
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
func (tx *LegacyTx) gasPrice() *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) value() *big.Int { return tx.Value }
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
func (tx *LegacyTx) to() *common.Address { return tx.To }
func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
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) {
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

@ -121,7 +121,7 @@ func (g *G2) FromBytes(in []byte) (*PointG2, error) {
return p, nil
}
// DecodePoint given encoded (x, y) coordinates in 256 bytes returns a valid G1 Point.
// DecodePoint given encoded (x, y) coordinates in 256 bytes returns a valid G2 Point.
func (g *G2) DecodePoint(in []byte) (*PointG2, error) {
if len(in) != 256 {
return nil, errors.New("invalid g2 point length")

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 {
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) {
@ -303,7 +303,7 @@ func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
for _, batch := range pending {
for _, lazy := range batch {
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 {
if tx := b.eth.txPool.Get(hash); tx != nil {
return tx.Tx
}
return nil
return b.eth.txPool.Get(hash)
}
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"
"github.com/ethereum/go-ethereum/consensus/ethash"
"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/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
@ -108,7 +107,7 @@ func TestEth2AssembleBlock(t *testing.T) {
if err != nil {
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{
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
txs := blocks[9].Transactions()
wrapped := make([]*txpool.Transaction, len(txs))
for i, tx := range txs {
wrapped[i] = &txpool.Transaction{Tx: tx}
}
api.eth.TxPool().Add(wrapped, false, true)
api.eth.TxPool().Add(txs, false, true)
blockParams := engine.PayloadAttributes{
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
txs := blocks[9].Transactions()
wrapped := make([]*txpool.Transaction, len(txs))
for i, tx := range txs {
wrapped[i] = &txpool.Transaction{Tx: tx}
}
ethservice.TxPool().Add(wrapped, true, false)
ethservice.TxPool().Add(txs, true, false)
blockParams := engine.PayloadAttributes{
Timestamp: blocks[8].Time() + 5,
}
@ -315,7 +306,7 @@ func TestEth2NewBlock(t *testing.T) {
statedb, _ := ethservice.BlockChain().StateAt(parent.Root())
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)
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{
Timestamp: parent.Time() + 5,
@ -484,7 +475,7 @@ func TestFullAPI(t *testing.T) {
statedb, _ := ethservice.BlockChain().StateAt(parent.Root)
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)
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, true, false)
ethservice.TxPool().Add([]*types.Transaction{tx}, true, false)
}
setupBlocks(t, ethservice, 10, parent, callback, nil)
@ -610,7 +601,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
GasPrice: big.NewInt(2 * params.InitialBaseFee),
Data: logCode,
})
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, false, true)
ethservice.TxPool().Add([]*types.Transaction{tx}, false, true)
var (
params = engine.PayloadAttributes{
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)
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)
ethservice.TxPool().Add([]*txpool.Transaction{{Tx: tx}}, true, false)
ethservice.TxPool().Add([]*types.Transaction{tx}, false, false)
}
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,
// and zero before the Cancun hardfork
// and zero before the Cancun hardfork.
var blobs int
for _, tx := range txLists[index] {
// 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
}
}
if tx.BlobTxSidecar() != nil {
return errInvalidBody
}
}
}
if header.BlobGasUsed != nil {

View file

@ -648,7 +648,7 @@ func (s *skeleton) processNewHead(head *types.Header, final *types.Header, force
}
if parent := rawdb.ReadSkeletonHeader(s.db, number-1); parent.Hash() != head.ParentHash {
if force {
log.Warn("Beacon chain forked", "ancestor", parent.Number, "hash", parent.Hash(), "want", head.ParentHash)
log.Warn("Beacon chain forked", "ancestor", number-1, "hash", parent.Hash(), "want", head.ParentHash)
}
return true
}

View file

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

View file

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

View file

@ -68,10 +68,10 @@ type txPool interface {
// Get retrieves the transaction from local txpool with given
// tx hash.
Get(hash common.Hash) *txpool.Transaction
Get(hash common.Hash) *types.Transaction
// 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.
// The slice should be modifiable by the caller.
@ -287,7 +287,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
}
return p.RequestTxs(hashes)
}
addTxs := func(txs []*txpool.Transaction) []error {
addTxs := func(txs []*types.Transaction) []error {
return h.txpool.Add(txs, false, false)
}
h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, addTxs, fetchTx)
@ -355,7 +355,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
number = head.Number.Uint64()
td = h.chain.GetTd(hash, number)
)
forkID := forkid.NewID(h.chain.Config(), genesis.Hash(), number, head.Time)
forkID := forkid.NewID(h.chain.Config(), genesis, number, head.Time)
if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
peer.Log().Debug("Ethereum handshake failed", "err", err)
return err

View file

@ -28,7 +28,6 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"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/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
@ -308,12 +307,11 @@ func testSendTransactions(t *testing.T, protocol uint) {
handler := newTestHandler()
defer handler.close()
insert := make([]*txpool.Transaction, 100)
insert := make([]*types.Transaction, 100)
for nonce := range insert {
tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, 10240))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
insert[nonce] = &txpool.Transaction{Tx: tx}
insert[nonce] = tx
}
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)
@ -376,8 +374,8 @@ func testSendTransactions(t *testing.T, protocol uint) {
}
}
for _, tx := range insert {
if _, ok := seen[tx.Tx.Hash()]; !ok {
t.Errorf("missing transaction: %x", tx.Tx.Hash())
if _, ok := seen[tx.Hash()]; !ok {
t.Errorf("missing transaction: %x", tx.Hash())
}
}
}
@ -434,12 +432,11 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
defer sub.Unsubscribe()
}
// 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 {
tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil)
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
txs[nonce] = &txpool.Transaction{Tx: tx}
txs[nonce] = tx
}
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
// tx hash.
func (p *testTxPool) Get(hash common.Hash) *txpool.Transaction {
func (p *testTxPool) Get(hash common.Hash) *types.Transaction {
p.lock.Lock()
defer p.lock.Unlock()
if tx := p.pool[hash]; tx != nil {
return &txpool.Transaction{Tx: tx}
}
return nil
return p.pool[hash]
}
// Add appends a batch of transactions to the pool, and notifies any
// listeners if the addition channel is non nil
func (p *testTxPool) Add(txs []*txpool.Transaction, local bool, sync bool) []error {
unwrapped := make([]*types.Transaction, len(txs))
for i, tx := range txs {
unwrapped[i] = tx.Tx
}
func (p *testTxPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
p.lock.Lock()
defer p.lock.Unlock()
for _, tx := range unwrapped {
for _, tx := range txs {
p.pool[tx.Hash()] = tx
}
p.txFeed.Send(core.NewTxsEvent{Txs: unwrapped})
return make([]error, len(unwrapped))
p.txFeed.Send(core.NewTxsEvent{Txs: txs})
return make([]error, len(txs))
}
// 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 {
pending[addr] = append(pending[addr], &txpool.LazyTransaction{
Hash: tx.Hash(),
Tx: &txpool.Transaction{Tx: tx},
Tx: tx,
Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(),

View file

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

View file

@ -61,6 +61,6 @@ func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) {
func currentENREntry(chain *core.BlockChain) *enrEntry {
head := chain.CurrentHeader()
return &enrEntry{
ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), head.Number.Uint64(), head.Time),
ForkID: forkid.NewID(chain.Config(), chain.Genesis(), head.Number.Uint64(), head.Time),
}
}

View file

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"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/p2p"
"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.
type TxPool interface {
// 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`.

View file

@ -503,7 +503,7 @@ func answerGetPooledTransactions(backend Backend, query GetPooledTransactionsPac
continue
}
// 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)
} else {
hashes = append(hashes, hash)

View file

@ -40,7 +40,7 @@ func testHandshake(t *testing.T, protocol uint) {
genesis = backend.chain.Genesis()
head = backend.chain.CurrentBlock()
td = backend.chain.GetTd(head.Hash(), head.Number.Uint64())
forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time)
forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time)
)
tests := []struct {
code uint64

View file

@ -236,7 +236,12 @@ func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
t.ctx["to"] = t.vm.ToValue(to.Bytes())
t.ctx["input"] = t.vm.ToValue(input)
t.ctx["gas"] = t.vm.ToValue(t.gasLimit)
t.ctx["gasPrice"] = t.vm.ToValue(env.TxContext.GasPrice)
gasPriceBig, err := t.toBig(t.vm, env.TxContext.GasPrice.String())
if err != nil {
t.err = err
return
}
t.ctx["gasPrice"] = gasPriceBig
valueBig, err := t.toBig(t.vm, value.String())
if err != nil {
t.err = err

View file

@ -108,6 +108,16 @@ func (ec *Client) PeerCount(ctx context.Context) (uint64, error) {
return uint64(result), err
}
// BlockReceipts returns the receipts of a given block number or hash
func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) {
var r []*types.Receipt
err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash)
if err == nil && r == nil {
return nil, ethereum.NotFound
}
return r, err
}
type rpcBlock struct {
Hash common.Hash `json:"hash"`
Transactions []rpcTransaction `json:"transactions"`

2
go.mod
View file

@ -18,7 +18,7 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.1.0
github.com/docker/docker v1.6.2
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127
github.com/ethereum/c-kzg-4844 v0.3.1
github.com/fatih/color v1.7.0
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e

4
go.sum
View file

@ -114,8 +114,8 @@ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5O
github.com/docker/docker v1.6.2 h1:HlFGsy+9/xrgMmhmN+NGhCc5SHGJ7I+kHosRR1xc/aI=
github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjUQYeC8R4ILzVcIe8+5edAJJnE=
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo=
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=

View file

@ -170,5 +170,8 @@ func applyEnvFlags(env Environment) Environment {
if *CronJobFlag {
env.IsCronJob = true
}
if *UbuntuVersionFlag != "" {
env.UbuntuVersion = *UbuntuVersionFlag
}
return env
}

View file

@ -897,6 +897,34 @@ func (s *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address
return res[:], state.Error()
}
// GetBlockReceipts returns the block receipts for the given block hash or number or tag.
func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) {
block, err := s.b.BlockByNumberOrHash(ctx, blockNrOrHash)
if block == nil || err != nil {
// When the block doesn't exist, the RPC method should return JSON null
// as per specification.
return nil, nil
}
receipts, err := s.b.GetReceipts(ctx, block.Hash())
if err != nil {
return nil, err
}
txs := block.Transactions()
if len(txs) != len(receipts) {
return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts))
}
// Derive the sender.
signer := types.MakeSigner(s.b.ChainConfig(), block.Number(), block.Time())
result := make([]map[string]interface{}, len(receipts))
for i, receipt := range receipts {
result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i)
}
return result, nil
}
// OverrideAccount indicates the overriding fields of account during the execution
// of a message call.
// Note, state and stateDiff can't be specified at the same time. If state is
@ -1134,12 +1162,30 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO
return result.Return(), result.Err
}
// executeEstimate is a helper that executes the transaction under a given gas limit and returns
// true if the transaction fails for a reason that might be related to not enough gas. A non-nil
// error means execution failed due to reasons unrelated to the gas limit.
func executeEstimate(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, gasCap uint64, gasLimit uint64) (bool, *core.ExecutionResult, error) {
args.Gas = (*hexutil.Uint64)(&gasLimit)
result, err := doCall(ctx, b, args, state, header, nil, nil, 0, gasCap)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
}
return result.Failed(), result, nil
}
// DoEstimateGas returns the lowest possible gas limit that allows the transaction to run
// successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if
// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil &
// non-zero) and `gasCap` (if non-zero).
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) {
// Binary search the gas requirement, as it may be higher than the amount used
// Binary search the gas limit, as it may need to be higher than the amount used
var (
lo uint64 = params.TxGas - 1
hi uint64
cap uint64
lo uint64 // lowest-known gas limit where tx execution fails
hi uint64 // lowest-known gas limit where tx execution succeeds
)
// Use zero address if sender unspecified.
if args.From == nil {
@ -1170,16 +1216,17 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
} else {
feeCap = common.Big0
}
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return 0, err
}
if err := overrides.Apply(state); err != nil {
return 0, err
}
// Recap the highest gas limit with account's available balance.
if feeCap.BitLen() != 0 {
state, _, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if err != nil {
return 0, err
}
err = overrides.Apply(state)
if err != nil {
return 0, err
}
balance := state.GetBalance(*args.From) // from can't be nil
available := new(big.Int).Set(balance)
if args.Value != nil {
@ -1206,39 +1253,42 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
hi = gasCap
}
cap = hi
// Create a helper to check if a gas allowance results in an executable transaction
executable := func(gas uint64, state *state.StateDB, header *types.Header) (bool, *core.ExecutionResult, error) {
args.Gas = (*hexutil.Uint64)(&gas)
result, err := doCall(ctx, b, args, state, header, nil, nil, 0, gasCap)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
}
return result.Failed(), result, nil
}
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return 0, err
}
err = overrides.Apply(state)
// We first execute the transaction at the highest allowable gas limit, since if this fails we
// can return error immediately.
failed, result, err := executeEstimate(ctx, b, args, state.Copy(), header, gasCap, hi)
if err != nil {
return 0, err
}
// Execute the binary search and hone in on an executable gas limit
for lo+1 < hi {
s := state.Copy()
mid := (hi + lo) / 2
failed, _, err := executable(mid, s, header)
if failed {
if result != nil && result.Err != vm.ErrOutOfGas {
if len(result.Revert()) > 0 {
return 0, newRevertError(result)
}
return 0, result.Err
}
return 0, fmt.Errorf("gas required exceeds allowance (%d)", hi)
}
// For almost any transaction, the gas consumed by the unconstrained execution above
// lower-bounds the gas limit required for it to succeed. One exception is those txs that
// explicitly check gas remaining in order to successfully execute within a given limit, but we
// probably don't want to return a lowest possible gas limit for these cases anyway.
lo = result.UsedGas - 1
// If the error is not nil(consensus error), it means the provided message
// call or transaction will never be accepted no matter how much gas it is
// assigned. Return the error directly, don't struggle any more.
// Binary search for the smallest gas limit that allows the tx to execute successfully.
for lo+1 < hi {
mid := (hi + lo) / 2
if mid > lo*2 {
// Most txs don't need much higher gas limit than their gas used, and most txs don't
// require near the full block limit of gas, so the selection of where to bisect the
// range here is skewed to favor the low side.
mid = lo * 2
}
failed, _, err = executeEstimate(ctx, b, args, state.Copy(), header, gasCap, mid)
if err != nil {
// This should not happen under normal conditions since if we make it this far the
// transaction had run without error at least once before.
log.Error("execution error in estimate gas", "err", err)
return 0, err
}
if failed {
@ -1247,28 +1297,14 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
hi = mid
}
}
// Reject the transaction as invalid if it still fails at the highest allowance
if hi == cap {
failed, result, err := executable(hi, state, header)
if err != nil {
return 0, err
}
if failed {
if result != nil && result.Err != vm.ErrOutOfGas {
if len(result.Revert()) > 0 {
return 0, newRevertError(result)
}
return 0, result.Err
}
// Otherwise, the specified gas cap is too low
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
}
}
return hexutil.Uint64(hi), nil
}
// EstimateGas returns an estimate of the amount of gas needed to execute the
// given transaction against the current pending block.
// EstimateGas returns the lowest possible gas limit that allows the transaction to run
// successfully at block `blockNrOrHash`, or the latest block if `blockNrOrHash` is unspecified. It
// returns error if the transaction would revert or if there are unexpected failures. The returned
// value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap
// configuration (if non-zero).
func (s *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) {
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
if blockNrOrHash != nil {
@ -1297,15 +1333,18 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
"transactionsRoot": head.TxHash,
"receiptsRoot": head.ReceiptHash,
}
if head.BaseFee != nil {
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
}
if head.WithdrawalsHash != nil {
result["withdrawalsRoot"] = head.WithdrawalsHash
}
if head.BlobGasUsed != nil {
result["blobGasUsed"] = hexutil.Uint64(*head.BlobGasUsed)
}
if head.ExcessBlobGas != nil {
result["excessBlobGas"] = hexutil.Uint64(*head.ExcessBlobGas)
}
return result
}
@ -1364,26 +1403,28 @@ func (s *BlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inc
// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
type RPCTransaction struct {
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
Value *hexutil.Big `json:"value"`
Type hexutil.Uint64 `json:"type"`
Accesses *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
BlockHash *common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex *hexutil.Uint64 `json:"transactionIndex"`
Value *hexutil.Big `json:"value"`
Type hexutil.Uint64 `json:"type"`
Accesses *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
}
// newRPCTransaction returns a transaction that will serialize to the RPC
@ -1437,15 +1478,43 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
// if the transaction has been mined, compute the effective gas price
if baseFee != nil && blockHash != (common.Hash{}) {
// price = min(gasTipCap + baseFee, gasFeeCap)
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
result.GasPrice = (*hexutil.Big)(price)
result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee))
} else {
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
}
case types.BlobTxType:
al := tx.AccessList()
yparity := hexutil.Uint64(v.Sign())
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
result.YParity = &yparity
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
// if the transaction has been mined, compute the effective gas price
if baseFee != nil && blockHash != (common.Hash{}) {
result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee))
} else {
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
}
result.MaxFeePerBlobGas = (*hexutil.Big)(tx.BlobGasFeeCap())
result.BlobVersionedHashes = tx.BlobHashes()
}
return result
}
// effectiveGasPrice computes the transaction gas fee, based on the given basefee value.
//
// price = min(gasTipCap + baseFee, gasFeeCap)
func effectiveGasPrice(tx *types.Transaction, baseFee *big.Int) *big.Int {
fee := tx.GasTipCap()
fee = fee.Add(fee, baseFee)
if tx.GasTipCapIntCmp(fee) < 0 {
return tx.GasTipCap()
}
return fee
}
// NewRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
func NewRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
var (
@ -1717,13 +1786,18 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
// Derive the sender.
signer := types.MakeSigner(s.b.ChainConfig(), header.Number, header.Time)
return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil
}
// marshalReceipt marshals a transaction receipt into a JSON object.
func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber uint64, signer types.Signer, tx *types.Transaction, txIndex int) map[string]interface{} {
from, _ := types.Sender(signer, tx)
fields := map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(blockNumber),
"transactionHash": hash,
"transactionIndex": hexutil.Uint64(index),
"transactionHash": tx.Hash(),
"transactionIndex": hexutil.Uint64(txIndex),
"from": from,
"to": tx.To(),
"gasUsed": hexutil.Uint64(receipt.GasUsed),
@ -1745,11 +1819,16 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
fields["logs"] = []*types.Log{}
}
if tx.Type() == types.BlobTxType {
fields["blobGasUsed"] = hexutil.Uint64(receipt.BlobGasUsed)
fields["blobGasPrice"] = (*hexutil.Big)(receipt.BlobGasPrice)
}
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if receipt.ContractAddress != (common.Address{}) {
fields["contractAddress"] = receipt.ContractAddress
}
return fields, nil
return fields
}
// sign is a helper function that signs a transaction with the private key of the given address.

View file

@ -1766,9 +1766,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) {
}
}
func TestRPCGetTransactionReceipt(t *testing.T) {
t.Parallel()
func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Hash) {
// Initialize test accounts
var (
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
@ -1794,9 +1792,8 @@ func TestRPCGetTransactionReceipt(t *testing.T) {
contract: {Balance: big.NewInt(params.Ether), Code: common.FromHex("0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a9059cbb14610030575b600080fd5b61004a6004803603810190610045919061016a565b610060565b60405161005791906101c5565b60405180910390f35b60008273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516100bf91906101ef565b60405180910390a36001905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610101826100d6565b9050919050565b610111816100f6565b811461011c57600080fd5b50565b60008135905061012e81610108565b92915050565b6000819050919050565b61014781610134565b811461015257600080fd5b50565b6000813590506101648161013e565b92915050565b60008060408385031215610181576101806100d1565b5b600061018f8582860161011f565b92505060206101a085828601610155565b9150509250929050565b60008115159050919050565b6101bf816101aa565b82525050565b60006020820190506101da60008301846101b6565b92915050565b6101e981610134565b82525050565b600060208201905061020460008301846101e0565b9291505056fea2646970667358221220b469033f4b77b9565ee84e0a2f04d496b18160d26034d54f9487e57788fd36d564736f6c63430008120033")},
},
}
genBlocks = 5
signer = types.LatestSignerForChainID(params.TestChainConfig.ChainID)
txHashes = make([]common.Hash, genBlocks)
signer = types.LatestSignerForChainID(params.TestChainConfig.ChainID)
txHashes = make([]common.Hash, genBlocks)
)
backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
var (
@ -1838,16 +1835,16 @@ func TestRPCGetTransactionReceipt(t *testing.T) {
txHashes[i] = tx.Hash()
}
})
api := NewTransactionAPI(backend, new(AddrLocker))
blockHashes := make([]common.Hash, genBlocks+1)
ctx := context.Background()
for i := 0; i <= genBlocks; i++ {
header, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
if err != nil {
t.Errorf("failed to get block: %d err: %v", i, err)
}
blockHashes[i] = header.Hash()
}
return backend, txHashes
}
func TestRPCGetTransactionReceipt(t *testing.T) {
t.Parallel()
var (
backend, txHashes = setupReceiptBackend(t, 5)
api = NewTransactionAPI(backend, new(AddrLocker))
)
var testSuite = []struct {
txHash common.Hash
@ -2000,3 +1997,102 @@ func TestRPCGetTransactionReceipt(t *testing.T) {
require.JSONEqf(t, want, have, "test %d: json not match, want: %s, have: %s", i, want, have)
}
}
func TestRPCGetBlockReceipts(t *testing.T) {
t.Parallel()
var (
genBlocks = 5
backend, _ = setupReceiptBackend(t, genBlocks)
api = NewBlockChainAPI(backend)
)
blockHashes := make([]common.Hash, genBlocks+1)
ctx := context.Background()
for i := 0; i <= genBlocks; i++ {
header, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
if err != nil {
t.Errorf("failed to get block: %d err: %v", i, err)
}
blockHashes[i] = header.Hash()
}
var testSuite = []struct {
test rpc.BlockNumberOrHash
want string
}{
// 0. block without any txs(hash)
{
test: rpc.BlockNumberOrHashWithHash(blockHashes[0], false),
want: `[]`,
},
// 1. block without any txs(number)
{
test: rpc.BlockNumberOrHashWithNumber(0),
want: `[]`,
},
// 2. earliest tag
{
test: rpc.BlockNumberOrHashWithNumber(rpc.EarliestBlockNumber),
want: `[]`,
},
// 3. latest tag
{
test: rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber),
want: `[{"blockHash":"0x08e23d8e3711a21fbb8becd7de22fda8fb0a49fba14e1be763d00f99063627e1","blockNumber":"0x5","contractAddress":"0xfdaa97661a584d977b4d3abb5370766ff5b86a18","cumulativeGasUsed":"0xe01a","effectiveGasPrice":"0x1ecb3f75","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0xe01a","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":null,"transactionHash":"0x8f3c4e2663af0312d508ebd8587f0c88dccbbc8a9bcc322421ff4bc28c456a92","transactionIndex":"0x0","type":"0x1"}]`,
},
// 4. block with legacy transfer tx(hash)
{
test: rpc.BlockNumberOrHashWithHash(blockHashes[1], false),
want: `[{"blockHash":"0x1356e49a24d4504e450b303aa770f4ae13c29b9ffacaea1d7dd4043396229dd9","blockNumber":"0x1","contractAddress":null,"cumulativeGasUsed":"0x5208","effectiveGasPrice":"0x342770c0","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":"0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e","transactionHash":"0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e","transactionIndex":"0x0","type":"0x0"}]`,
},
// 5. block with contract create tx(number)
{
test: rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(2)),
want: `[{"blockHash":"0x4fc27a4efa7fb8faa04b12b53ec8c8424ab4c21aab1323846365f000e8b4a594","blockNumber":"0x2","contractAddress":"0xae9bea628c4ce503dcfd7e305cab4e29e7476592","cumulativeGasUsed":"0xcf4e","effectiveGasPrice":"0x2db16291","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0xcf4e","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":null,"transactionHash":"0x340e58cda5086495010b571fe25067fecc9954dc4ee3cedece00691fa3f5904a","transactionIndex":"0x0","type":"0x0"}]`,
},
// 6. block with legacy contract call tx(hash)
{
test: rpc.BlockNumberOrHashWithHash(blockHashes[3], false),
want: `[{"blockHash":"0x73385c190219326907524b0020ef453ebc450eaa971ebce16f79e2d23e7e8d4d","blockNumber":"0x3","contractAddress":null,"cumulativeGasUsed":"0x5e28","effectiveGasPrice":"0x281c2534","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0x5e28","logs":[{"address":"0x0000000000000000000000000000000000031ec7","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000703c4b2bd70c169f5717101caee543299fc946c7","0x0000000000000000000000000000000000000000000000000000000000000003"],"data":"0x000000000000000000000000000000000000000000000000000000000000000d","blockNumber":"0x3","transactionHash":"0x9dbf43ec9afc8d711932618616471088f66ba4f25fd5c672d97473d02dae967f","transactionIndex":"0x0","blockHash":"0x73385c190219326907524b0020ef453ebc450eaa971ebce16f79e2d23e7e8d4d","logIndex":"0x0","removed":false}],"logsBloom":"0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000800000000000000008000000000000000000000000000000000020000000080000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000400000000002000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000","status":"0x1","to":"0x0000000000000000000000000000000000031ec7","transactionHash":"0x9dbf43ec9afc8d711932618616471088f66ba4f25fd5c672d97473d02dae967f","transactionIndex":"0x0","type":"0x0"}]`,
},
// 7. block with dynamic fee tx(number)
{
test: rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(4)),
want: `[{"blockHash":"0x77c3f8919590e0e68db4ce74a3da3140ac3e96dd3d078a48db1da4c08b07503d","blockNumber":"0x4","contractAddress":null,"cumulativeGasUsed":"0x538d","effectiveGasPrice":"0x2325c3e8","from":"0x703c4b2bd70c169f5717101caee543299fc946c7","gasUsed":"0x538d","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x0","to":"0x0000000000000000000000000000000000031ec7","transactionHash":"0x672e3e39adf23b5656989b7a36e54d54004b1866f53871113bc52e137edb9faf","transactionIndex":"0x0","type":"0x2"}]`,
},
// 8. block is empty
{
test: rpc.BlockNumberOrHashWithHash(common.Hash{}, false),
want: `null`,
},
// 9. block is not found
{
test: rpc.BlockNumberOrHashWithHash(common.HexToHash("deadbeef"), false),
want: `null`,
},
// 10. block is not found
{
test: rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(genBlocks + 1)),
want: `null`,
},
}
for i, tt := range testSuite {
var (
result interface{}
err error
)
result, err = api.GetBlockReceipts(context.Background(), tt.test)
if err != nil {
t.Errorf("test %d: want no error, have %v", i, err)
continue
}
data, err := json.Marshal(result)
if err != nil {
t.Errorf("test %d: json marshal error", i)
continue
}
want, have := tt.want, string(data)
require.JSONEqf(t, want, have, "test %d: json not match, want: %s, have: %s", i, want, have)
}
}

View file

@ -617,6 +617,11 @@ web3._extend({
params: 4,
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null],
}),
new web3._extend.Method({
name: 'getBlockReceipts',
call: 'eth_getBlockReceipts',
params: 1,
}),
],
properties: [
new web3._extend.Property({

View file

@ -69,7 +69,7 @@ func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error {
p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
// Execute the LES handshake
forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.genesis, h.backend.blockchain.CurrentHeader().Number.Uint64(), h.backend.blockchain.CurrentHeader().Time)
forkid := forkid.NewID(h.backend.blockchain.Config(), h.backend.BlockChain().Genesis(), h.backend.blockchain.CurrentHeader().Number.Uint64(), h.backend.blockchain.CurrentHeader().Time)
if err := p.Handshake(h.backend.blockchain.Genesis().Hash(), forkid, h.forkFilter); err != nil {
p.Log().Debug("Light Ethereum handshake failed", "err", err)
return err

View file

@ -124,8 +124,8 @@ func TestHandshake(t *testing.T) {
genesis = common.HexToHash("cafebabe")
chain1, chain2 = &fakeChain{}, &fakeChain{}
forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis().Hash(), chain1.CurrentHeader().Number.Uint64(), chain1.CurrentHeader().Time)
forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis().Hash(), chain2.CurrentHeader().Number.Uint64(), chain2.CurrentHeader().Time)
forkID1 = forkid.NewID(chain1.Config(), chain1.Genesis(), chain1.CurrentHeader().Number.Uint64(), chain1.CurrentHeader().Time)
forkID2 = forkid.NewID(chain2.Config(), chain2.Genesis(), chain2.CurrentHeader().Number.Uint64(), chain2.CurrentHeader().Time)
filter1, filter2 = forkid.NewFilter(chain1), forkid.NewFilter(chain2)
)

View file

@ -116,7 +116,7 @@ func (h *serverHandler) handle(p *clientPeer) error {
hash = head.Hash()
number = head.Number.Uint64()
td = h.blockchain.GetTd(hash, number)
forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis().Hash(), number, head.Time)
forkID = forkid.NewID(h.blockchain.Config(), h.blockchain.Genesis(), number, head.Time)
)
if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), forkID, h.forkFilter, h.server); err != nil {
p.Log().Debug("Light Ethereum handshake failed", "err", err)

View file

@ -518,7 +518,7 @@ func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) {
hash := tx.Hash()
stats[i] = txStatus(backend, hash)
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()
continue
}

View file

@ -499,7 +499,7 @@ func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*t
head = server.handler.blockchain.CurrentHeader()
td = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64())
)
forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time)
forkID := forkid.NewID(server.handler.blockchain.Config(), genesis, head.Number.Uint64(), head.Time)
tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID)
// Ensure the connection is established or exits when any error occurs

View file

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

View file

@ -30,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"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/types"
"github.com/ethereum/go-ethereum/crypto"
@ -133,7 +132,7 @@ func main() {
if err != nil {
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)
}
nonces[index]++

View file

@ -539,7 +539,7 @@ func (w *worker) mainLoop() {
acc, _ := types.Sender(w.current.signer, tx)
txs[acc] = append(txs[acc], &txpool.LazyTransaction{
Hash: tx.Hash(),
Tx: &txpool.Transaction{Tx: tx},
Tx: tx.WithoutBlobTxSidecar(),
Time: tx.Time(),
GasFeeCap: tx.GasFeeCap(),
GasTipCap: tx.GasTipCap(),
@ -734,18 +734,18 @@ func (w *worker) updateSnapshot(env *environment) {
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 (
snap = env.state.Snapshot()
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 {
env.state.RevertToSnapshot(snap)
env.gasPool.SetGas(gp)
return nil, err
}
env.txs = append(env.txs, tx.Tx)
env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt)
return receipt.Logs, nil
@ -778,30 +778,30 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
tx := ltx.Resolve()
if tx == nil {
log.Warn("Ignoring evicted transaction")
txs.Pop()
continue
}
// Error may be ignored here. The error has already been checked
// 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
// phase, start ignoring the sender until we do.
if tx.Tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
log.Trace("Ignoring reply protected transaction", "hash", tx.Tx.Hash(), "eip155", w.chainConfig.EIP155Block)
if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block)
txs.Pop()
continue
}
// 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)
switch {
case errors.Is(err, core.ErrNonceTooLow):
// 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()
case errors.Is(err, nil):
@ -813,7 +813,7 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
default:
// Transaction is regarded as invalid, drop all consecutive transactions from
// 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()
}
}

View file

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

View file

@ -36,6 +36,13 @@ const (
DefaultAuthPort = 8551 // Default port for the authenticated apis
)
const (
// Engine API batch limits: these are not configurable by users, and should cover the
// needs of all CLs.
engineAPIBatchItemLimit = 2000
engineAPIBatchResponseSizeLimit = 250 * 1000 * 1000
)
var (
DefaultAuthCors = []string{"localhost"} // Default cors domain for the authenticated apis
DefaultAuthVhosts = []string{"localhost"} // Default virtual hosts for the authenticated apis

View file

@ -449,8 +449,11 @@ func (n *Node) startRPC() error {
if err := server.setListenAddr(n.config.AuthAddr, port); err != nil {
return err
}
sharedConfig := rpcConfig
sharedConfig.jwtSecret = secret
sharedConfig := rpcEndpointConfig{
jwtSecret: secret,
batchItemLimit: engineAPIBatchItemLimit,
batchResponseSizeLimit: engineAPIBatchResponseSizeLimit,
}
if err := server.enableRPC(allAPIs, httpConfig{
CorsAllowedOrigins: DefaultAuthCors,
Vhosts: n.config.AuthVirtualHosts,

View file

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

View file

@ -51,9 +51,8 @@ func hexToCompact(hex []byte) []byte {
return buf
}
// hexToCompactInPlace places the compact key in input buffer, returning the length
// needed for the representation
func hexToCompactInPlace(hex []byte) int {
// hexToCompactInPlace places the compact key in input buffer, returning the compacted key.
func hexToCompactInPlace(hex []byte) []byte {
var (
hexLen = len(hex) // length of the hex input
firstByte = byte(0)
@ -77,7 +76,7 @@ func hexToCompactInPlace(hex []byte) int {
hex[bi] = hex[ni]<<4 | hex[ni+1]
}
hex[0] = firstByte
return binLen
return hex[:binLen]
}
func compactToHex(compact []byte) []byte {

View file

@ -86,8 +86,7 @@ func TestHexToCompactInPlace(t *testing.T) {
} {
hexBytes, _ := hex.DecodeString(key)
exp := hexToCompact(hexBytes)
sz := hexToCompactInPlace(hexBytes)
got := hexBytes[:sz]
got := hexToCompactInPlace(hexBytes)
if !bytes.Equal(exp, got) {
t.Fatalf("test %d: encoding err\ninp %v\ngot %x\nexp %x\n", i, key, got, exp)
}
@ -102,8 +101,7 @@ func TestHexToCompactInPlaceRandom(t *testing.T) {
hexBytes := keybytesToHex(key)
hexOrig := []byte(string(hexBytes))
exp := hexToCompact(hexBytes)
sz := hexToCompactInPlace(hexBytes)
got := hexBytes[:sz]
got := hexToCompactInPlace(hexBytes)
if !bytes.Equal(exp, got) {
t.Fatalf("encoding err \ncpt %x\nhex %x\ngot %x\nexp %x\n",
@ -119,6 +117,13 @@ func BenchmarkHexToCompact(b *testing.B) {
}
}
func BenchmarkHexToCompactInPlace(b *testing.B) {
testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}
for i := 0; i < b.N; i++ {
hexToCompactInPlace(testBytes)
}
}
func BenchmarkCompactToHex(b *testing.B) {
testBytes := []byte{0, 15, 1, 12, 11, 8, 16 /*term*/}
for i := 0; i < b.N; i++ {

View file

@ -444,7 +444,7 @@ func (st *StackTrie) hashRec(hasher *hasher, path []byte) {
case extNode:
st.children[0].hashRec(hasher, append(path, st.key...))
n := shortNode{Key: hexToCompact(st.key)}
n := shortNode{Key: hexToCompactInPlace(st.key)}
if len(st.children[0].val) < 32 {
n.Val = rawNode(st.children[0].val)
} else {
@ -460,7 +460,7 @@ func (st *StackTrie) hashRec(hasher *hasher, path []byte) {
case leafNode:
st.key = append(st.key, byte(16))
n := shortNode{Key: hexToCompact(st.key), Val: valueNode(st.val)}
n := shortNode{Key: hexToCompactInPlace(st.key), Val: valueNode(st.val)}
n.encode(hasher.encbuf)
encodedNode = hasher.encodedBytes()

View file

@ -165,6 +165,38 @@ func TestStackTrieInsertAndHash(t *testing.T) {
{"123e", "x___________________________2", "0d230561e398c579e09a9f7b69ceaf7d3970f5a436fdb28b68b7a37c5bdd6b80"},
{"13aa", "x___________________________3", "ff0dc70ce2e5db90ee42a4c2ad12139596b890e90eb4e16526ab38fa465b35cf"},
},
{ // branch node with short values
{"01", "a", "b48605025f5f4b129d40a420e721aa7d504487f015fce85b96e52126365ef7dc"},
{"80", "b", "2dc6b680daf74db067cb7aeaad73265ded93d96fce190fcbf64f498d475672ab"},
{"ee", "c", "017dc705a54ac5328dd263fa1bae68d655310fb3e3f7b7bc57e9a43ddf99c4bf"},
{"ff", "d", "bd5a3584d271d459bd4eb95247b2fc88656b3671b60c1125ffe7bc0b689470d0"},
},
{ // ext node with short branch node, then becoming long
{"a0", "a", "a83e028cb1e4365935661a9fd36a5c65c30b9ab416eaa877424146ca2a69d088"},
{"a1", "b", "f586a4639b07b01798ca65e05c253b75d51135ebfbf6f8d6e87c0435089e65f0"},
{"a2", "c", "63e297c295c008e09a8d531e18d57f270b6bc403e23179b915429db948cd62e3"},
{"a3", "d", "94a7b721535578e9381f1f4e4b6ec29f8bdc5f0458a30320684c562f5d47b4b5"},
{"a4", "e", "4b7e66d1c81965cdbe8fab8295ef56bc57fefdc5733d4782d2f8baf630f083c6"},
{"a5", "f", "2997e7b502198ce1783b5277faacf52b25844fb55a99b63e88bdbbafac573106"},
{"a6", "g", "bee629dd27a40772b2e1a67ec6db270d26acdf8d3b674dfae27866ad6ae1f48b"},
},
{ // branch node with short values, then long ones
{"a001", "v1", "b9cc982d995392b51e6787f1915f0b88efd4ad8b30f138da0a3e2242f2323e35"},
{"b002", "v2", "a7b474bc77ef5097096fa0ee6298fdae8928c0bc3724e7311cd0fa9ed1942fc7"},
{"c003", "v___________________________3", "dceb5bb7c92b0e348df988a8d9fc36b101397e38ebd405df55ba6ee5f14a264a"},
{"d004", "v___________________________4", "36e60ecb86b9626165e1c6543c42ecbe4d83bca58e8e1124746961511fce362a"},
},
{ // ext node to branch node with short values, then long ones
{"8002", "v1", "3258fcb3e9e7d7234ecd3b8d4743999e4ab3a21592565e0a5ca64c141e8620d9"},
{"8004", "v2", "b6cb95b7024a83c17624a3c9bed09b4b5e8ed426f49f54b8ad13c39028b1e75a"},
{"8008", "v___________________________3", "c769d82963abe6f0900bf69754738eeb2f84559777cfa87a44f54e1aab417871"},
{"800d", "v___________________________4", "1cad1fdaab1a6fa95d7b780fd680030e423eb76669971368ba04797a8d9cdfc9"},
},
{ // ext node with a child of size 31 (Y) and branch node with a child of size 31 (X)
{"000001", "ZZZZZZZZZ", "cef154b87c03c563408520ff9b26923c360cbc3ddb590c079bedeeb25a8c9c77"},
{"000002", "Y", "2130735e600f612f6e657a32bd7be64ddcaec6512c5694844b19de713922895d"},
{"000003", "XXXXXXXXXXXXXXXXXXXXXXXXXXXX", "962c0fffdeef7612a4f7bff1950d67e3e81c878e48b9ae45b3b374253b050bd8"},
},
}
st := NewStackTrie(nil)
for i, test := range tests {