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 ( import (
"fmt" "fmt"
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -45,25 +44,6 @@ const HashScheme = "hash"
// on extra state diffs to survive deep reorg. // on extra state diffs to survive deep reorg.
const PathScheme = "path" 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. // ReadAccountTrieNode retrieves the account trie node with the specified node path.
func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte { func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte {
data, _ := db.Get(accountTrieNodeKey(path)) 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 { if len(blob) == 0 {
return false return false
} }
h := newHasher() return crypto.Keccak256Hash(blob) == hash // exists but not match
defer h.release()
return h.hash(blob) == hash // exists but not match
default: default:
panic(fmt.Sprintf("Unknown scheme %v", scheme)) 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 { if len(blob) == 0 {
return nil return nil
} }
h := newHasher() if crypto.Keccak256Hash(blob) != hash {
defer h.release()
if h.hash(blob) != hash {
return nil // exists but not match return nil // exists but not match
} }
return blob return blob

View file

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

View file

@ -386,6 +386,7 @@ func (s *stateObject) commitStorage(op *accountUpdate) {
return blob return blob
} }
) )
defer crypto.ReturnKeccakState(buf)
for key, val := range s.pendingStorage { for key, val := range s.pendingStorage {
// Skip the noop storage changes, it might be possible the value // Skip the noop storage changes, it might be possible the value
// of tracked slot is same in originStorage and pendingStorage // 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() buf = crypto.NewKeccakState()
deletes = make(map[common.Hash]*accountDelete) deletes = make(map[common.Hash]*accountDelete)
) )
defer crypto.ReturnKeccakState(buf)
for addr, prevObj := range s.stateObjectsDestruct { for addr, prevObj := range s.stateObjectsDestruct {
prev := prevObj.origin 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 // 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) { 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.Reset()
sha.Write(data) sha.Write(data)
sha.Read(hashbuf) sha.Read(hashbuf)
hasherPool.Put(sha)
// The actual bits to flip // The actual bits to flip
v1 := byte(1 << (hashbuf[1] & 0x7)) v1 := byte(1 << (hashbuf[1] & 0x7))
v2 := byte(1 << (hashbuf[3] & 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/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "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. // encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
var encodeBufferPool = sync.Pool{ var encodeBufferPool = sync.Pool{
New: func() interface{} { return new(bytes.Buffer) }, 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. // rlpHash encodes x and hashes the encoded bytes.
func rlpHash(x interface{}) (h common.Hash) { func rlpHash(x interface{}) (h common.Hash) {
sha := hasherPool.Get().(crypto.KeccakState) sha := crypto.NewKeccakState()
defer hasherPool.Put(sha) defer crypto.ReturnKeccakState(sha)
sha.Reset() sha.Reset()
rlp.Encode(sha, x) rlp.Encode(sha, x)
sha.Read(h[:]) 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. // prefixedRlpHash writes the prefix into the hasher before rlp-encoding x.
// It's used for typed transactions. // It's used for typed transactions.
func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) { func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) {
sha := hasherPool.Get().(crypto.KeccakState) sha := crypto.NewKeccakState()
defer hasherPool.Put(sha) defer crypto.ReturnKeccakState(sha)
sha.Reset() sha.Reset()
sha.Write([]byte{prefix}) sha.Write([]byte{prefix})
rlp.Encode(sha, x) 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:] // 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. // 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) { 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) 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/common"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -234,11 +233,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
offset, size := scope.Stack.pop(), scope.Stack.peek() offset, size := scope.Stack.pop(), scope.Stack.peek()
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
if interpreter.hasher == nil { interpreter.hasher.Reset()
interpreter.hasher = crypto.NewKeccakState()
} else {
interpreter.hasher.Reset()
}
interpreter.hasher.Write(data) interpreter.hasher.Write(data)
interpreter.hasher.Read(interpreter.hasherBuf[:]) interpreter.hasher.Read(interpreter.hasherBuf[:])

View file

@ -150,7 +150,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
} }
} }
evm.Config.ExtraEips = extraEips 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 // Run loops and evaluates the contract's code with the given input data and returns

View file

@ -28,6 +28,7 @@ import (
"io" "io"
"math/big" "math/big"
"os" "os"
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
@ -70,7 +71,19 @@ type KeccakState interface {
// NewKeccakState creates a new KeccakState // NewKeccakState creates a new KeccakState
func NewKeccakState() KeccakState { func NewKeccakState() KeccakState {
return sha3.NewLegacyKeccak256().(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 // 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.Write(b)
} }
d.Read(b) d.Read(b)
ReturnKeccakState(d)
return b return b
} }
@ -100,6 +114,7 @@ func Keccak256Hash(data ...[]byte) (h common.Hash) {
d.Write(b) d.Write(b)
} }
d.Read(h[:]) d.Read(h[:])
ReturnKeccakState(d)
return h return h
} }

View file

@ -488,6 +488,7 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) {
hasher = crypto.NewKeccakState() hasher = crypto.NewKeccakState()
hash = make([]byte, 32) hash = make([]byte, 32)
) )
defer crypto.ReturnKeccakState(hasher)
for _, tx := range txs { for _, tx := range txs {
var maybeDirect bool var maybeDirect bool
switch { 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 // Cross reference the requested bytecodes with the response to find gaps
// that the serving node is missing // that the serving node is missing
hasher := crypto.NewKeccakState() hasher := crypto.NewKeccakState()
defer crypto.ReturnKeccakState(hasher)
hash := make([]byte, 32) hash := make([]byte, 32)
codes := make([][]byte, len(req.hashes)) 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)) nodes = make([][]byte, len(req.hashes))
fills uint64 fills uint64
) )
defer crypto.ReturnKeccakState(hasher)
for i, j := 0, 0; i < len(trienodes); i++ { for i, j := 0, 0; i < len(trienodes); i++ {
// Find the next hash that we've been served, leaving misses with nils // Find the next hash that we've been served, leaving misses with nils
hasher.Reset() hasher.Reset()

View file

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

View file

@ -729,9 +729,7 @@ func (s *Sync) hasNode(owner common.Hash, path []byte, hash common.Hash) (exists
} else { } else {
blob = rawdb.ReadStorageTrieNode(s.database, owner, path) blob = rawdb.ReadStorageTrieNode(s.database, owner, path)
} }
h := newBlobHasher() exists = hash == crypto.Keccak256Hash(blob)
defer h.release()
exists = hash == h.hash(blob)
inconsistent = !exists && len(blob) != 0 inconsistent = !exists && len(blob) != 0
return exists, inconsistent return exists, inconsistent
} }
@ -746,23 +744,3 @@ func ResolvePath(path []byte) (common.Hash, []byte) {
} }
return owner, path 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) dirtyNodeMissMeter.Mark(1)
// Try to retrieve the trie node from the clean memory cache // Try to retrieve the trie node from the clean memory cache
h := newHasher()
defer h.release()
key := nodeCacheKey(owner, path) key := nodeCacheKey(owner, path)
if dl.nodes != nil { if dl.nodes != nil {
if blob := dl.nodes.Get(nil, key); len(blob) > 0 { if blob := dl.nodes.Get(nil, key); len(blob) > 0 {
cleanNodeHitMeter.Mark(1) cleanNodeHitMeter.Mark(1)
cleanNodeReadMeter.Mark(int64(len(blob))) 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) 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) dl.nodes.Set(key, blob)
cleanNodeWriteMeter.Mark(int64(len(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 // account directly retrieves the account RLP associated with a particular
@ -359,22 +356,3 @@ func (dl *diskLayer) resetCache() {
dl.nodes.Reset() 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/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode" "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 { func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
// The account was present in prev-state, decode it from the // The account was present in prev-state, decode it from the
// 'slim-rlp' format bytes. // 'slim-rlp' format bytes.
h := newHasher() h := crypto.NewKeccakState()
defer h.release() defer crypto.ReturnKeccakState(h)
addrHash := h.hash(addr.Bytes()) addrHash := crypto.HashData(h, addr.Bytes())
prev, err := types.FullAccount(ctx.accounts[addr]) prev, err := types.FullAccount(ctx.accounts[addr])
if err != nil { if err != nil {
return err return err
@ -113,7 +114,7 @@ func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address)
for key, val := range ctx.storages[addr] { for key, val := range ctx.storages[addr] {
tkey := key tkey := key
if ctx.rawStorageKey { if ctx.rawStorageKey {
tkey = h.hash(key.Bytes()) tkey = crypto.HashData(h, key.Bytes())
} }
var err error var err error
if len(val) == 0 { 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. // account and storage is wiped out correctly.
func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) error { func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) error {
// The account must be existent in post-state, load the account. // The account must be existent in post-state, load the account.
h := newHasher() h := crypto.NewKeccakState()
defer h.release() defer crypto.ReturnKeccakState(h)
addrHash := h.hash(addr.Bytes()) addrHash := crypto.HashData(h, addr.Bytes())
blob, err := ctx.accountTrie.Get(addrHash.Bytes()) blob, err := ctx.accountTrie.Get(addrHash.Bytes())
if err != nil { if err != nil {
return err return err
@ -174,7 +175,7 @@ func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address)
} }
tkey := key tkey := key
if ctx.rawStorageKey { if ctx.rawStorageKey {
tkey = h.hash(key.Bytes()) tkey = crypto.HashData(h, key.Bytes())
} }
if err := st.Delete(tkey.Bytes()); err != nil { if err := st.Delete(tkey.Bytes()); err != nil {
return err 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) accounts = make(map[common.Hash][]byte)
storages = make(map[common.Hash]map[common.Hash][]byte) storages = make(map[common.Hash]map[common.Hash][]byte)
) )
defer crypto.ReturnKeccakState(buff)
for addr, blob := range h.accounts { for addr, blob := range h.accounts {
addrHash := crypto.HashData(buff, addr.Bytes()) addrHash := crypto.HashData(buff, addr.Bytes())
accounts[addrHash] = blob accounts[addrHash] = blob