core/types: optimize modernSigner.Equal (#32971)
Some checks are pending
/ Docker Image (push) Waiting to run
/ Linux Build (push) Waiting to run
/ Linux Build (arm) (push) Waiting to run
/ Keeper Build (push) Waiting to run
/ Windows Build (push) Waiting to run

Equal is called every time the transaction sender is accessed,
even when the sender is cached, so it is worth optimizing.

---------

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
cui 2025-10-27 23:04:06 +08:00 committed by GitHub
parent 447b5f7e19
commit 33dbd64a23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 34 additions and 11 deletions

View file

@ -20,7 +20,6 @@ import (
"crypto/ecdsa"
"errors"
"fmt"
"maps"
"math/big"
"github.com/ethereum/go-ethereum/common"
@ -183,18 +182,31 @@ type Signer interface {
// modernSigner is the signer implementation that handles non-legacy transaction types.
// For legacy transactions, it defers to one of the legacy signers (frontier, homestead, eip155).
type modernSigner struct {
txtypes map[byte]struct{}
txtypes txtypeSet
chainID *big.Int
legacy Signer
}
// txtypeSet is a bitmap for transaction types.
type txtypeSet [2]uint64
func (v *txtypeSet) set(txType byte) {
v[txType/64] |= 1 << (txType % 64)
}
func (v *txtypeSet) has(txType byte) bool {
if txType >= byte(len(v)*64) {
return false
}
return v[txType/64]&(1<<(txType%64)) != 0
}
func newModernSigner(chainID *big.Int, fork forks.Fork) Signer {
if chainID == nil || chainID.Sign() <= 0 {
panic(fmt.Sprintf("invalid chainID %v", chainID))
}
s := &modernSigner{
chainID: chainID,
txtypes: make(map[byte]struct{}, 4),
}
// configure legacy signer
switch {
@ -205,19 +217,19 @@ func newModernSigner(chainID *big.Int, fork forks.Fork) Signer {
default:
s.legacy = FrontierSigner{}
}
s.txtypes[LegacyTxType] = struct{}{}
s.txtypes.set(LegacyTxType)
// configure tx types
if fork >= forks.Berlin {
s.txtypes[AccessListTxType] = struct{}{}
s.txtypes.set(AccessListTxType)
}
if fork >= forks.London {
s.txtypes[DynamicFeeTxType] = struct{}{}
s.txtypes.set(DynamicFeeTxType)
}
if fork >= forks.Cancun {
s.txtypes[BlobTxType] = struct{}{}
s.txtypes.set(BlobTxType)
}
if fork >= forks.Prague {
s.txtypes[SetCodeTxType] = struct{}{}
s.txtypes.set(SetCodeTxType)
}
return s
}
@ -228,7 +240,7 @@ func (s *modernSigner) ChainID() *big.Int {
func (s *modernSigner) Equal(s2 Signer) bool {
other, ok := s2.(*modernSigner)
return ok && s.chainID.Cmp(other.chainID) == 0 && maps.Equal(s.txtypes, other.txtypes) && s.legacy.Equal(other.legacy)
return ok && s.chainID.Cmp(other.chainID) == 0 && s.txtypes == other.txtypes && s.legacy.Equal(other.legacy)
}
func (s *modernSigner) Hash(tx *Transaction) common.Hash {
@ -236,8 +248,7 @@ func (s *modernSigner) Hash(tx *Transaction) common.Hash {
}
func (s *modernSigner) supportsType(txtype byte) bool {
_, ok := s.txtypes[txtype]
return ok
return s.txtypes.has(txtype)
}
func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) {

View file

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params/forks"
"github.com/ethereum/go-ethereum/rlp"
)
@ -188,3 +189,14 @@ func createTestLegacyTxInner() *LegacyTx {
Data: nil,
}
}
func Benchmark_modernSigner_Equal(b *testing.B) {
signer1 := newModernSigner(big.NewInt(1), forks.Amsterdam)
signer2 := newModernSigner(big.NewInt(1), forks.Amsterdam)
for b.Loop() {
if !signer1.Equal(signer2) {
b.Fatal("expected signers to be equal")
}
}
}