Merge branch 'ethereum:master' into master

This commit is contained in:
Martin Totovski 2025-03-19 09:14:18 +02:00 committed by GitHub
commit 6fe1408eb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 289 additions and 87 deletions

View file

@ -1189,8 +1189,7 @@ func (p *BlobPool) Has(hash common.Hash) bool {
return p.lookup.exists(hash)
}
// Get returns a transaction if it is contained in the pool, or nil otherwise.
func (p *BlobPool) Get(hash common.Hash) *types.Transaction {
func (p *BlobPool) getRLP(hash common.Hash) []byte {
// 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()
@ -1212,14 +1211,31 @@ func (p *BlobPool) Get(hash common.Hash) *types.Transaction {
log.Error("Tracked blob transaction missing from store", "hash", hash, "id", id, "err", err)
return nil
}
return data
}
// Get returns a transaction if it is contained in the pool, or nil otherwise.
func (p *BlobPool) Get(hash common.Hash) *types.Transaction {
data := p.getRLP(hash)
if len(data) == 0 {
return nil
}
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)
if err := rlp.DecodeBytes(data, item); err != nil {
id, _ := p.lookup.storeidOfTx(hash)
log.Error("Blobs corrupted for traced transaction",
"hash", hash, "id", id, "err", err)
return nil
}
return item
}
// GetRLP returns a RLP-encoded transaction if it is contained in the pool.
func (p *BlobPool) GetRLP(hash common.Hash) []byte {
return p.getRLP(hash)
}
// GetBlobs returns a number of blobs are proofs for the given versioned hashes.
// This is a utility method for the engine API, enabling consensus clients to
// retrieve blobs from the pools directly instead of the network.

View file

@ -40,6 +40,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
@ -1010,6 +1011,20 @@ func (pool *LegacyPool) get(hash common.Hash) *types.Transaction {
return pool.all.Get(hash)
}
// GetRLP returns a RLP-encoded transaction if it is contained in the pool.
func (pool *LegacyPool) GetRLP(hash common.Hash) []byte {
tx := pool.all.Get(hash)
if tx == nil {
return nil
}
encoded, err := rlp.EncodeToBytes(tx)
if err != nil {
log.Error("Failed to encoded transaction in legacy pool", "hash", hash, "err", err)
return nil
}
return encoded
}
// GetBlobs is not supported by the legacy transaction pool, it is just here to
// implement the txpool.SubPool interface.
func (pool *LegacyPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) {

View file

@ -124,6 +124,9 @@ type SubPool interface {
// Get returns a transaction if it is contained in the pool, or nil otherwise.
Get(hash common.Hash) *types.Transaction
// GetRLP returns a RLP-encoded transaction if it is contained in the pool.
GetRLP(hash common.Hash) []byte
// GetBlobs returns a number of blobs are proofs for the given versioned hashes.
// This is a utility method for the engine API, enabling consensus clients to
// retrieve blobs from the pools directly instead of the network.

View file

@ -309,6 +309,17 @@ func (p *TxPool) Get(hash common.Hash) *types.Transaction {
return nil
}
// GetRLP returns a RLP-encoded transaction if it is contained in the pool.
func (p *TxPool) GetRLP(hash common.Hash) []byte {
for _, subpool := range p.subpools {
encoded := subpool.GetRLP(hash)
if len(encoded) != 0 {
return encoded
}
}
return nil
}
// GetBlobs returns a number of blobs are proofs for the given versioned hashes.
// This is a utility method for the engine API, enabling consensus clients to
// retrieve blobs from the pools directly instead of the network.

View file

@ -100,6 +100,9 @@ type TxData interface {
encode(*bytes.Buffer) error
decode([]byte) error
// sigHash returns the hash of the transaction that is ought to be signed
sigHash(*big.Int) common.Hash
}
// EncodeRLP implements rlp.Encoder

View file

@ -233,20 +233,7 @@ func (s pragueSigner) Hash(tx *Transaction) common.Hash {
if tx.Type() != SetCodeTxType {
return s.cancunSigner.Hash(tx)
}
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
tx.SetCodeAuthorizations(),
})
return tx.inner.sigHash(s.chainId)
}
type cancunSigner struct{ londonSigner }
@ -301,21 +288,7 @@ func (s cancunSigner) Hash(tx *Transaction) common.Hash {
if tx.Type() != BlobTxType {
return s.londonSigner.Hash(tx)
}
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
tx.BlobGasFeeCap(),
tx.BlobHashes(),
})
return tx.inner.sigHash(s.chainId)
}
type londonSigner struct{ eip2930Signer }
@ -369,19 +342,7 @@ func (s londonSigner) Hash(tx *Transaction) common.Hash {
if tx.Type() != DynamicFeeTxType {
return s.eip2930Signer.Hash(tx)
}
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
})
return tx.inner.sigHash(s.chainId)
}
type eip2930Signer struct{ EIP155Signer }
@ -444,18 +405,7 @@ func (s eip2930Signer) Hash(tx *Transaction) common.Hash {
case LegacyTxType:
return s.EIP155Signer.Hash(tx)
case AccessListTxType:
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasPrice(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
})
return tx.inner.sigHash(s.chainId)
default:
// This _should_ not happen, but in case someone sends in a bad
// json struct via RPC, it's probably more prudent to return an
@ -525,15 +475,7 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
tx.Nonce(),
tx.GasPrice(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
s.chainId, uint(0), uint(0),
})
return tx.inner.sigHash(s.chainId)
}
// HomesteadSigner implements Signer interface using the
@ -597,7 +539,7 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *
// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
return rlpHash([]any{
tx.Nonce(),
tx.GasPrice(),
tx.Gas(),

View file

@ -576,3 +576,20 @@ func TestYParityJSONUnmarshalling(t *testing.T) {
}
}
}
func BenchmarkHash(b *testing.B) {
signer := NewLondonSigner(big.NewInt(1))
to := common.Address{}
tx := NewTx(&DynamicFeeTx{
ChainID: big.NewInt(123),
Nonce: 1,
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
GasTipCap: big.NewInt(500),
GasFeeCap: big.NewInt(500),
})
for i := 0; i < b.N; i++ {
signer.Hash(tx)
}
}

View file

@ -127,3 +127,18 @@ func (tx *AccessListTx) encode(b *bytes.Buffer) error {
func (tx *AccessListTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}
func (tx *AccessListTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
AccessListTxType,
[]any{
chainID,
tx.Nonce,
tx.GasPrice,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
})
}

View file

@ -259,3 +259,21 @@ func (tx *BlobTx) decode(input []byte) error {
}
return nil
}
func (tx *BlobTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
BlobTxType,
[]any{
chainID,
tx.Nonce,
tx.GasTipCap,
tx.GasFeeCap,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
tx.BlobFeeCap,
tx.BlobHashes,
})
}

View file

@ -123,3 +123,19 @@ func (tx *DynamicFeeTx) encode(b *bytes.Buffer) error {
func (tx *DynamicFeeTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}
func (tx *DynamicFeeTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
DynamicFeeTxType,
[]any{
chainID,
tx.Nonce,
tx.GasTipCap,
tx.GasFeeCap,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
})
}

View file

@ -123,3 +123,16 @@ func (tx *LegacyTx) encode(*bytes.Buffer) error {
func (tx *LegacyTx) decode([]byte) error {
panic("decode called on LegacyTx)")
}
// OBS: This is the post-EIP155 hash, the pre-EIP155 does not contain a chainID.
func (tx *LegacyTx) sigHash(chainID *big.Int) common.Hash {
return rlpHash([]any{
tx.Nonce,
tx.GasPrice,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
chainID, uint(0), uint(0),
})
}

View file

@ -223,3 +223,20 @@ func (tx *SetCodeTx) encode(b *bytes.Buffer) error {
func (tx *SetCodeTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}
func (tx *SetCodeTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
SetCodeTxType,
[]any{
chainID,
tx.Nonce,
tx.GasTipCap,
tx.GasFeeCap,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
tx.AuthList,
})
}

View file

@ -67,6 +67,10 @@ type txPool interface {
// tx hash.
Get(hash common.Hash) *types.Transaction
// GetRLP retrieves the RLP-encoded transaction from local txpool
// with given tx hash.
GetRLP(hash common.Hash) []byte
// Add should add the given transactions to the pool.
Add(txs []*types.Transaction, sync bool) []error

View file

@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
@ -78,6 +79,20 @@ func (p *testTxPool) Get(hash common.Hash) *types.Transaction {
return p.pool[hash]
}
// Get retrieves the transaction from local txpool with given
// tx hash.
func (p *testTxPool) GetRLP(hash common.Hash) []byte {
p.lock.Lock()
defer p.lock.Unlock()
tx := p.pool[hash]
if tx != nil {
blob, _ := rlp.EncodeToBytes(tx)
return blob
}
return nil
}
// 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 []*types.Transaction, sync bool) []error {

View file

@ -86,6 +86,10 @@ type Backend interface {
type TxPool interface {
// Get retrieves the transaction from the local txpool with the given hash.
Get(hash common.Hash) *types.Transaction
// GetRLP retrieves the RLP-encoded transaction from the local txpool with
// the given hash.
GetRLP(hash common.Hash) []byte
}
// MakeProtocols constructs the P2P protocol definitions for `eth`.

View file

@ -18,9 +18,11 @@ package eth
import (
"bytes"
"crypto/sha256"
"math"
"math/big"
"math/rand"
"os"
"testing"
"time"
@ -30,15 +32,18 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
var (
@ -62,12 +67,12 @@ type testBackend struct {
// newTestBackend creates an empty chain and wraps it into a mock backend.
func newTestBackend(blocks int) *testBackend {
return newTestBackendWithGenerator(blocks, false, nil)
return newTestBackendWithGenerator(blocks, false, false, nil)
}
// newTestBackendWithGenerator creates a chain with a number of explicitly defined blocks and
// wraps it into a mock backend.
func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend {
func newTestBackendWithGenerator(blocks int, shanghai bool, cancun bool, generator func(int, *core.BlockGen)) *testBackend {
var (
// Create a database pre-initialize with a genesis block
db = rawdb.NewMemoryDatabase()
@ -99,9 +104,21 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int,
}
}
if cancun {
config.CancunTime = u64(0)
config.BlobScheduleConfig = &params.BlobScheduleConfig{
Cancun: &params.BlobConfig{
Target: 3,
Max: 6,
UpdateFraction: params.DefaultCancunBlobConfig.UpdateFraction,
},
}
}
gspec := &core.Genesis{
Config: config,
Alloc: types.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
Difficulty: common.Big0,
}
chain, _ := core.NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil)
@ -115,8 +132,12 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int,
txconfig := legacypool.DefaultConfig
txconfig.Journal = "" // Don't litter the disk with test journals
pool := legacypool.New(txconfig, chain)
txpool, _ := txpool.New(txconfig.PriceLimit, chain, []txpool.SubPool{pool})
storage, _ := os.MkdirTemp("", "blobpool-")
defer os.RemoveAll(storage)
blobPool := blobpool.New(blobpool.Config{Datadir: storage}, chain)
legacyPool := legacypool.New(txconfig, chain)
txpool, _ := txpool.New(txconfig.PriceLimit, chain, []txpool.SubPool{legacyPool, blobPool})
return &testBackend{
db: db,
@ -351,7 +372,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
}
}
backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen)
backend := newTestBackendWithGenerator(maxBodiesServe+15, true, false, gen)
defer backend.close()
peer, _ := newTestPeer("peer", protocol, backend)
@ -471,7 +492,7 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
}
}
// Assemble the test environment
backend := newTestBackendWithGenerator(4, false, generator)
backend := newTestBackendWithGenerator(4, false, false, generator)
defer backend.close()
peer, _ := newTestPeer("peer", protocol, backend)
@ -548,7 +569,7 @@ func setup() (*testBackend, *testPeer) {
block.SetExtra([]byte("yeehaw"))
}
}
backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen)
backend := newTestBackendWithGenerator(maxBodiesServe+15, true, false, gen)
peer, _ := newTestPeer("peer", ETH68, backend)
// Discard all messages
go func() {
@ -573,3 +594,80 @@ func FuzzEthProtocolHandlers(f *testing.F) {
handler(backend, decoder{msg: msg}, peer.Peer)
})
}
func TestGetPooledTransaction(t *testing.T) {
t.Run("blobTx", func(t *testing.T) {
testGetPooledTransaction(t, true)
})
t.Run("legacyTx", func(t *testing.T) {
testGetPooledTransaction(t, false)
})
}
func testGetPooledTransaction(t *testing.T, blobTx bool) {
var (
emptyBlob = kzg4844.Blob{}
emptyBlobs = []kzg4844.Blob{emptyBlob}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(&emptyBlob)
emptyBlobProof, _ = kzg4844.ComputeBlobProof(&emptyBlob, emptyBlobCommit)
emptyBlobHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
)
backend := newTestBackendWithGenerator(0, true, true, nil)
defer backend.close()
peer, _ := newTestPeer("peer", ETH68, backend)
defer peer.close()
var (
tx *types.Transaction
err error
signer = types.NewCancunSigner(params.TestChainConfig.ChainID)
)
if blobTx {
tx, err = types.SignNewTx(testKey, signer, &types.BlobTx{
ChainID: uint256.MustFromBig(params.TestChainConfig.ChainID),
Nonce: 0,
GasTipCap: uint256.NewInt(20_000_000_000),
GasFeeCap: uint256.NewInt(21_000_000_000),
Gas: 21000,
To: testAddr,
BlobHashes: []common.Hash{emptyBlobHash},
BlobFeeCap: uint256.MustFromBig(common.Big1),
Sidecar: &types.BlobTxSidecar{
Blobs: emptyBlobs,
Commitments: []kzg4844.Commitment{emptyBlobCommit},
Proofs: []kzg4844.Proof{emptyBlobProof},
},
})
if err != nil {
t.Fatal(err)
}
} else {
tx, err = types.SignTx(
types.NewTransaction(0, testAddr, big.NewInt(10_000), params.TxGas, big.NewInt(1_000_000_000), nil),
signer,
testKey,
)
if err != nil {
t.Fatal(err)
}
}
errs := backend.txpool.Add([]*types.Transaction{tx}, true)
for _, err := range errs {
if err != nil {
t.Fatal(err)
}
}
// Send the hash request and verify the response
p2p.Send(peer.app, GetPooledTransactionsMsg, GetPooledTransactionsPacket{
RequestId: 123,
GetPooledTransactionsRequest: []common.Hash{tx.Hash()},
})
if err := p2p.ExpectMsg(peer.app, PooledTransactionsMsg, PooledTransactionsPacket{
RequestId: 123,
PooledTransactionsResponse: []*types.Transaction{tx},
}); err != nil {
t.Errorf("pooled transaction mismatch: %v", err)
}
}

View file

@ -397,19 +397,14 @@ func answerGetPooledTransactions(backend Backend, query GetPooledTransactionsReq
break
}
// Retrieve the requested transaction, skipping if unknown to us
tx := backend.TxPool().Get(hash)
if tx == nil {
encoded := backend.TxPool().GetRLP(hash)
if len(encoded) == 0 {
continue
}
// If known, encode and queue for response packet
if encoded, err := rlp.EncodeToBytes(tx); err != nil {
log.Error("Failed to encode transaction", "err", err)
} else {
hashes = append(hashes, hash)
txs = append(txs, encoded)
bytes += len(encoded)
}
}
return hashes, txs
}