crypto: all: create global hasher pool

This commit is contained in:
MariusVanDerWijden 2025-05-06 14:02:08 +02:00 committed by Gary Rong
parent 485ff4bbff
commit 80ccc0e322
17 changed files with 48 additions and 103 deletions

View file

@ -18,7 +18,6 @@ package rawdb
import (
"fmt"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
@ -45,25 +44,6 @@ const HashScheme = "hash"
// on extra state diffs to survive deep reorg.
const PathScheme = "path"
// hasher is used to compute the sha256 hash of the provided data.
type hasher struct{ sha crypto.KeccakState }
var hasherPool = sync.Pool{
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
}
func newHasher() *hasher {
return hasherPool.Get().(*hasher)
}
func (h *hasher) hash(data []byte) common.Hash {
return crypto.HashData(h.sha, data)
}
func (h *hasher) release() {
hasherPool.Put(h)
}
// ReadAccountTrieNode retrieves the account trie node with the specified node path.
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
data, _ := db.Get(accountTrieNodeKey(path))
@ -170,9 +150,7 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c
if len(blob) == 0 {
return false
}
h := newHasher()
defer h.release()
return h.hash(blob) == hash // exists but not match
return crypto.Keccak256Hash(blob) == hash // exists but not match
default:
panic(fmt.Sprintf("Unknown scheme %v", scheme))
}
@ -194,9 +172,7 @@ func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash
if len(blob) == 0 {
return nil
}
h := newHasher()
defer h.release()
if h.hash(blob) != hash {
if crypto.Keccak256Hash(blob) != hash {
return nil // exists but not match
}
return blob

View file

@ -645,6 +645,7 @@ func SafeDeleteRange(db ethdb.KeyValueStore, start, end []byte, hashScheme bool,
buff = crypto.NewKeccakState()
startTime = time.Now()
)
defer crypto.ReturnKeccakState(buff)
batch := db.NewBatch()
it := db.NewIterator(nil, start)

View file

@ -386,6 +386,7 @@ func (s *stateObject) commitStorage(op *accountUpdate) {
return blob
}
)
defer crypto.ReturnKeccakState(buf)
for key, val := range s.pendingStorage {
// Skip the noop storage changes, it might be possible the value
// of tracked slot is same in originStorage and pendingStorage

View file

@ -1067,6 +1067,7 @@ func (s *StateDB) handleDestruction(noStorageWiping bool) (map[common.Hash]*acco
buf = crypto.NewKeccakState()
deletes = make(map[common.Hash]*accountDelete)
)
defer crypto.ReturnKeccakState(buf)
for addr, prevObj := range s.stateObjectsDestruct {
prev := prevObj.origin

View file

@ -140,11 +140,11 @@ func Bloom9(data []byte) []byte {
// bloomValues returns the bytes (index-value pairs) to set for the given data
func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) {
sha := hasherPool.Get().(crypto.KeccakState)
sha := crypto.NewKeccakState()
defer crypto.ReturnKeccakState(sha)
sha.Reset()
sha.Write(data)
sha.Read(hashbuf)
hasherPool.Put(sha)
// The actual bits to flip
v1 := byte(1 << (hashbuf[1] & 0x7))
v2 := byte(1 << (hashbuf[3] & 0x7))

View file

@ -25,14 +25,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
// hasherPool holds LegacyKeccak256 hashers for rlpHash.
var hasherPool = sync.Pool{
New: func() interface{} { return sha3.NewLegacyKeccak256() },
}
// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
var encodeBufferPool = sync.Pool{
New: func() interface{} { return new(bytes.Buffer) },
@ -56,8 +50,8 @@ func getPooledBuffer(size uint64) ([]byte, *bytes.Buffer, error) {
// rlpHash encodes x and hashes the encoded bytes.
func rlpHash(x interface{}) (h common.Hash) {
sha := hasherPool.Get().(crypto.KeccakState)
defer hasherPool.Put(sha)
sha := crypto.NewKeccakState()
defer crypto.ReturnKeccakState(sha)
sha.Reset()
rlp.Encode(sha, x)
sha.Read(h[:])
@ -67,8 +61,8 @@ func rlpHash(x interface{}) (h common.Hash) {
// prefixedRlpHash writes the prefix into the hasher before rlp-encoding x.
// It's used for typed transactions.
func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) {
sha := hasherPool.Get().(crypto.KeccakState)
defer hasherPool.Put(sha)
sha := crypto.NewKeccakState()
defer crypto.ReturnKeccakState(sha)
sha.Reset()
sha.Write([]byte{prefix})
rlp.Encode(sha, x)

View file

@ -555,7 +555,8 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), crypto.Keccak256(code))
inithash := crypto.HashData(evm.interpreter.hasher, code)
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
}

View file

@ -22,7 +22,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
@ -234,11 +233,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
offset, size := scope.Stack.pop(), scope.Stack.peek()
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
if interpreter.hasher == nil {
interpreter.hasher = crypto.NewKeccakState()
} else {
interpreter.hasher.Reset()
}
interpreter.hasher.Write(data)
interpreter.hasher.Read(interpreter.hasherBuf[:])

View file

@ -150,7 +150,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
}
}
evm.Config.ExtraEips = extraEips
return &EVMInterpreter{evm: evm, table: table}
return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()}
}
// Run loops and evaluates the contract's code with the given input data and returns

View file

@ -28,6 +28,7 @@ import (
"io"
"math/big"
"os"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
@ -70,7 +71,19 @@ type KeccakState interface {
// NewKeccakState creates a new KeccakState
func NewKeccakState() KeccakState {
state := hasherPool.Get().(KeccakState)
state.Reset()
return state
}
var hasherPool = sync.Pool{
New: func() any {
return sha3.NewLegacyKeccak256().(KeccakState)
},
}
func ReturnKeccakState(state KeccakState) {
hasherPool.Put(state)
}
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
@ -89,6 +102,7 @@ func Keccak256(data ...[]byte) []byte {
d.Write(b)
}
d.Read(b)
ReturnKeccakState(d)
return b
}
@ -100,6 +114,7 @@ func Keccak256Hash(data ...[]byte) (h common.Hash) {
d.Write(b)
}
d.Read(h[:])
ReturnKeccakState(d)
return h
}

View file

@ -488,6 +488,7 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) {
hasher = crypto.NewKeccakState()
hash = make([]byte, 32)
)
defer crypto.ReturnKeccakState(hasher)
for _, tx := range txs {
var maybeDirect bool
switch {

View file

@ -2655,6 +2655,7 @@ func (s *Syncer) onByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error
// Cross reference the requested bytecodes with the response to find gaps
// that the serving node is missing
hasher := crypto.NewKeccakState()
defer crypto.ReturnKeccakState(hasher)
hash := make([]byte, 32)
codes := make([][]byte, len(req.hashes))
@ -2907,6 +2908,7 @@ func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error
nodes = make([][]byte, len(req.hashes))
fills uint64
)
defer crypto.ReturnKeccakState(hasher)
for i, j := 0, 0; i < len(trienodes); i++ {
// Find the next hash that we've been served, leaving misses with nils
hasher.Reset()

View file

@ -34,7 +34,7 @@ type hasher struct {
// hasherPool holds pureHashers
var hasherPool = sync.Pool{
New: func() interface{} {
New: func() any {
return &hasher{
tmp: make([]byte, 0, 550), // cap is as large as a full fullNode.
sha: crypto.NewKeccakState(),

View file

@ -729,9 +729,7 @@ func (s *Sync) hasNode(owner common.Hash, path []byte, hash common.Hash) (exists
} else {
blob = rawdb.ReadStorageTrieNode(s.database, owner, path)
}
h := newBlobHasher()
defer h.release()
exists = hash == h.hash(blob)
exists = hash == crypto.Keccak256Hash(blob)
inconsistent = !exists && len(blob) != 0
return exists, inconsistent
}
@ -746,23 +744,3 @@ func ResolvePath(path []byte) (common.Hash, []byte) {
}
return owner, path
}
// blobHasher is used to compute the sha256 hash of the provided data.
type blobHasher struct{ state crypto.KeccakState }
// blobHasherPool is the pool for reusing pre-allocated hash state.
var blobHasherPool = sync.Pool{
New: func() interface{} { return &blobHasher{state: crypto.NewKeccakState()} },
}
func newBlobHasher() *blobHasher {
return blobHasherPool.Get().(*blobHasher)
}
func (h *blobHasher) hash(data []byte) common.Hash {
return crypto.HashData(h.state, data)
}
func (h *blobHasher) release() {
blobHasherPool.Put(h)
}

View file

@ -115,15 +115,12 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
dirtyNodeMissMeter.Mark(1)
// Try to retrieve the trie node from the clean memory cache
h := newHasher()
defer h.release()
key := nodeCacheKey(owner, path)
if dl.nodes != nil {
if blob := dl.nodes.Get(nil, key); len(blob) > 0 {
cleanNodeHitMeter.Mark(1)
cleanNodeReadMeter.Mark(int64(len(blob)))
return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
}
cleanNodeMissMeter.Mark(1)
}
@ -138,7 +135,7 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
dl.nodes.Set(key, blob)
cleanNodeWriteMeter.Mark(int64(len(blob)))
}
return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil
return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil
}
// account directly retrieves the account RLP associated with a particular
@ -359,22 +356,3 @@ func (dl *diskLayer) resetCache() {
dl.nodes.Reset()
}
}
// hasher is used to compute the sha256 hash of the provided data.
type hasher struct{ sha crypto.KeccakState }
var hasherPool = sync.Pool{
New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} },
}
func newHasher() *hasher {
return hasherPool.Get().(*hasher)
}
func (h *hasher) hash(data []byte) common.Hash {
return crypto.HashData(h.sha, data)
}
func (h *hasher) release() {
hasherPool.Put(h)
}

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
@ -85,10 +86,10 @@ func apply(db database.NodeDatabase, prevRoot common.Hash, postRoot common.Hash,
func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
// The account was present in prev-state, decode it from the
// 'slim-rlp' format bytes.
h := newHasher()
defer h.release()
h := crypto.NewKeccakState()
defer crypto.ReturnKeccakState(h)
addrHash := h.hash(addr.Bytes())
addrHash := crypto.HashData(h, addr.Bytes())
prev, err := types.FullAccount(ctx.accounts[addr])
if err != nil {
return err
@ -113,7 +114,7 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address)
for key, val := range ctx.storages[addr] {
tkey := key
if ctx.rawStorageKey {
tkey = h.hash(key.Bytes())
tkey = crypto.HashData(h, key.Bytes())
}
var err error
if len(val) == 0 {
@ -149,10 +150,10 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address)
// account and storage is wiped out correctly.
func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
// The account must be existent in post-state, load the account.
h := newHasher()
defer h.release()
h := crypto.NewKeccakState()
defer crypto.ReturnKeccakState(h)
addrHash := h.hash(addr.Bytes())
addrHash := crypto.HashData(h, addr.Bytes())
blob, err := ctx.accountTrie.Get(addrHash.Bytes())
if err != nil {
return err
@ -174,7 +175,7 @@ func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address)
}
tkey := key
if ctx.rawStorageKey {
tkey = h.hash(key.Bytes())
tkey = crypto.HashData(h, key.Bytes())
}
if err := st.Delete(tkey.Bytes()); err != nil {
return err

View file

@ -282,6 +282,7 @@ func (h *history) stateSet() (map[common.Hash][]byte, map[common.Hash]map[common
accounts = make(map[common.Hash][]byte)
storages = make(map[common.Hash]map[common.Hash][]byte)
)
defer crypto.ReturnKeccakState(buff)
for addr, blob := range h.accounts {
addrHash := crypto.HashData(buff, addr.Bytes())
accounts[addrHash] = blob