From 6bcfab90129da1cfdd5350b1cb653538dcb80391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Tue, 9 Dec 2025 10:28:16 +0100 Subject: [PATCH 01/10] Put panics to all places (outside tests) that use statefull keccak unnecessarily --- cmd/devp2p/internal/ethtest/snap.go | 2 ++ cmd/geth/dbcmd.go | 6 ++++-- cmd/geth/snapshot.go | 5 ++++- cmd/workload/historytestgen.go | 4 +++- consensus/clique/clique.go | 4 +++- core/stateless/database.go | 4 ++-- core/types/bloom9.go | 1 + core/types/hashing.go | 2 ++ core/vm/evm.go | 1 + core/vm/instructions.go | 2 +- crypto/crypto.go | 1 + eth/protocols/snap/sync.go | 3 +++ trie/hasher.go | 2 ++ trie/stacktrie.go | 1 + 14 files changed, 30 insertions(+), 8 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/snap.go b/cmd/devp2p/internal/ethtest/snap.go index f4fce0931f..108a472715 100644 --- a/cmd/devp2p/internal/ethtest/snap.go +++ b/cmd/devp2p/internal/ethtest/snap.go @@ -907,6 +907,7 @@ func (s *Suite) snapGetByteCodes(t *utesting.T, tc *byteCodesTest) error { for i, j := 0, 0; i < len(bytecodes); i++ { // Find the next hash that we've been served, leaving misses with nils + panic("Used by replay.wasm") hasher.Reset() hasher.Write(bytecodes[i]) hasher.Read(hash) @@ -966,6 +967,7 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error { return fmt.Errorf("wrong trienode count, got %d, want %d", got, want) } for i, trienode := range trienodes { + panic("Used by replay.wasm") hasher.Reset() hasher.Write(trienode) hasher.Read(hash) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index c57add0656..286cee1c34 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -28,6 +28,9 @@ import ( "syscall" "time" + "github.com/olekukonko/tablewriter" + "github.com/urfave/cli/v2" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -41,8 +44,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" - "github.com/olekukonko/tablewriter" - "github.com/urfave/cli/v2" ) var ( @@ -365,6 +366,7 @@ func checkStateContent(ctx *cli.Context) error { count++ k := it.Key() v := it.Value() + panic("Used by replay.wasm") hasher.Reset() hasher.Write(v) hasher.Read(got) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index fe0bf97db8..c50fd66dd0 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -25,6 +25,8 @@ import ( "slices" "time" + "github.com/urfave/cli/v2" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -36,7 +38,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/urfave/cli/v2" ) var ( @@ -459,6 +460,7 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Missing trie node(account)", "hash", node) return errors.New("missing account") } + panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(got) @@ -500,6 +502,7 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Missing trie node(storage)", "hash", node) return errors.New("missing storage") } + panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(got) diff --git a/cmd/workload/historytestgen.go b/cmd/workload/historytestgen.go index b88c8208da..f85c6b7ff0 100644 --- a/cmd/workload/historytestgen.go +++ b/cmd/workload/historytestgen.go @@ -24,12 +24,13 @@ import ( "os" "path/filepath" + "github.com/urfave/cli/v2" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/rlp" - "github.com/urfave/cli/v2" ) var ( @@ -133,6 +134,7 @@ func generateHistoryTests(clictx *cli.Context) error { func calcReceiptsHash(rcpt []*types.Receipt) common.Hash { h := crypto.NewKeccakState() + panic("Used by replay.wasm") rlp.Encode(h, rcpt) return common.Hash(h.Sum(nil)) } diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 5128a9d9f4..24e805b714 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -27,6 +27,8 @@ import ( "sync" "time" + "golang.org/x/crypto/sha3" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/lru" @@ -42,7 +44,6 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "golang.org/x/crypto/sha3" ) const ( @@ -643,6 +644,7 @@ func (c *Clique) Close() error { // SealHash returns the hash of a block prior to it being sealed. func SealHash(header *types.Header) (hash common.Hash) { + panic("Used by replay.wasm") hasher := sha3.NewLegacyKeccak256() encodeSigHeader(hasher, header) hasher.(crypto.KeccakState).Read(hash[:]) diff --git a/core/stateless/database.go b/core/stateless/database.go index f54c123dda..197bca1c9d 100644 --- a/core/stateless/database.go +++ b/core/stateless/database.go @@ -46,7 +46,7 @@ func (w *Witness) MakeHashDB() ethdb.Database { // Inject all the bytecodes into the ephemeral database for code := range w.Codes { blob := []byte(code) - + panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(hash) @@ -56,7 +56,7 @@ func (w *Witness) MakeHashDB() ethdb.Database { // Inject all the MPT trie nodes into the ephemeral database for node := range w.State { blob := []byte(node) - + panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(hash) diff --git a/core/types/bloom9.go b/core/types/bloom9.go index 1d57e8e4bc..db0c1f206e 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -142,6 +142,7 @@ func Bloom9(data []byte) []byte { // bloomValues returns the bytes (index-value pairs) to set for the given data func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, byte) { sha := hasherPool.Get().(crypto.KeccakState) + panic("Used by replay.wasm") sha.Reset() sha.Write(data) sha.Read(hashbuf[:]) diff --git a/core/types/hashing.go b/core/types/hashing.go index 98fe64e15a..1111c53299 100644 --- a/core/types/hashing.go +++ b/core/types/hashing.go @@ -56,6 +56,7 @@ 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) + panic("Used by replay.wasm") defer hasherPool.Put(sha) sha.Reset() rlp.Encode(sha, x) @@ -67,6 +68,7 @@ func rlpHash(x interface{}) (h common.Hash) { // It's used for typed transactions. func prefixedRlpHash(prefix byte, x interface{}) (h common.Hash) { sha := hasherPool.Get().(crypto.KeccakState) + panic("Used by replay.wasm") defer hasherPool.Put(sha) sha.Reset() sha.Write([]byte{prefix}) diff --git a/core/vm/evm.go b/core/vm/evm.go index ef637e4daf..d88838d42b 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -703,6 +703,7 @@ 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, usedMultiGas multigas.MultiGas, err error) { + panic("Used by replay.wasm") inithash := crypto.HashData(evm.hasher, code) contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:]) return evm.create(caller, code, gas, endowment, contractAddr, CREATE2) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 44346af5e8..6ca15d257a 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -235,7 +235,7 @@ func opSAR(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { func opKeccak256(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { offset, size := scope.Stack.pop(), scope.Stack.peek() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) - + panic("Used by replay.wasm") evm.hasher.Reset() evm.hasher.Write(data) evm.hasher.Read(evm.hasherBuf[:]) diff --git a/crypto/crypto.go b/crypto/crypto.go index db6b6ee071..ddcb57d557 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -69,6 +69,7 @@ type KeccakState interface { // HashData hashes the provided data using the KeccakState and returns a 32 byte hash func HashData(kh KeccakState, data []byte) (h common.Hash) { + panic("Used by replay.wasm") kh.Reset() kh.Write(data) kh.Read(h[:]) diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index cf4e494645..4b3c54ab8f 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -2678,6 +2678,7 @@ func (s *Syncer) onByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error codes := make([][]byte, len(req.hashes)) for i, j := 0, 0; i < len(bytecodes); i++ { // Find the next hash that we've been served, leaving misses with nils + panic("Used by replay.wasm") hasher.Reset() hasher.Write(bytecodes[i]) hasher.Read(hash) @@ -2927,6 +2928,7 @@ func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error ) for i, j := 0, 0; i < len(trienodes); i++ { // Find the next hash that we've been served, leaving misses with nils + panic("Used by replay.wasm") hasher.Reset() hasher.Write(trienodes[i]) hasher.Read(hash) @@ -3032,6 +3034,7 @@ func (s *Syncer) onHealByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) e codes := make([][]byte, len(req.hashes)) for i, j := 0, 0; i < len(bytecodes); i++ { // Find the next hash that we've been served, leaving misses with nils + panic("Used by replay.wasm") hasher.Reset() hasher.Write(bytecodes[i]) hasher.Read(hash) diff --git a/trie/hasher.go b/trie/hasher.go index a2a1f5b662..5ce3657b70 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -189,6 +189,7 @@ func (h *hasher) encodedBytes() []byte { // hashData hashes the provided data. It is safe to modify the returned slice after // the function returns. func (h *hasher) hashData(data []byte) []byte { + panic("Used by replay.wasm") n := make([]byte, 32) h.sha.Reset() h.sha.Write(data) @@ -199,6 +200,7 @@ func (h *hasher) hashData(data []byte) []byte { // hashDataTo hashes the provided data to the given destination buffer. The caller // must ensure that the dst buffer is of appropriate size. func (h *hasher) hashDataTo(dst, data []byte) { + panic("Used by replay.wasm") h.sha.Reset() h.sha.Write(data) h.sha.Read(dst) diff --git a/trie/stacktrie.go b/trie/stacktrie.go index 18fe1eea78..7277b603cc 100644 --- a/trie/stacktrie.go +++ b/trie/stacktrie.go @@ -415,6 +415,7 @@ func (t *StackTrie) hash(st *stNode, path []byte) { // Write the hash to the 'val'. We allocate a new val here to not mutate // input values. st.val = bPool.getWithSize(32) + panic("Used by replay.wasm") t.h.hashDataTo(st.val, blob) // Invoke the callback it's provided. Notably, the path and blob slices are From e34ba0ff4eeb0cbd42fa13e7d4e1d67621dd3434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 14:06:32 +0100 Subject: [PATCH 02/10] Migrate to `crypto.Keccak` in core/types (essentially - use common hasher pool) --- core/types/bloom9.go | 8 ++------ core/types/hashing.go | 24 ++++-------------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/core/types/bloom9.go b/core/types/bloom9.go index db0c1f206e..a766b2e89a 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -141,12 +141,8 @@ func Bloom9(data []byte) []byte { // bloomValues returns the bytes (index-value pairs) to set for the given data func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, byte) { - sha := hasherPool.Get().(crypto.KeccakState) - panic("Used by replay.wasm") - sha.Reset() - sha.Write(data) - sha.Read(hashbuf[:]) - hasherPool.Put(sha) + hash := crypto.Keccak256(data) + copy(hashbuf[:], hash[:6]) // The actual bits to flip v1 := byte(1 << (hashbuf[1] & 0x7)) v2 := byte(1 << (hashbuf[3] & 0x7)) diff --git a/core/types/hashing.go b/core/types/hashing.go index 1111c53299..31e0e91e99 100644 --- a/core/types/hashing.go +++ b/core/types/hashing.go @@ -27,11 +27,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -// hasherPool holds LegacyKeccak256 buffer for rlpHash. -var hasherPool = sync.Pool{ - New: func() interface{} { return crypto.NewKeccakState() }, -} - // encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding. var encodeBufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, @@ -55,26 +50,15 @@ 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) - panic("Used by replay.wasm") - defer hasherPool.Put(sha) - sha.Reset() - rlp.Encode(sha, x) - sha.Read(h[:]) - return h + encoded, _ := rlp.EncodeToBytes(x) + return crypto.Keccak256Hash(encoded) } // 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) - panic("Used by replay.wasm") - defer hasherPool.Put(sha) - sha.Reset() - sha.Write([]byte{prefix}) - rlp.Encode(sha, x) - sha.Read(h[:]) - return h + encoded, _ := rlp.EncodeToBytes(x) + return crypto.Keccak256Hash([]byte{prefix}, encoded) } // ListHasher defines the interface for computing the hash of a derivable list. From b784a358e477dc0d8cfb4bc501e5375c2f25dabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 14:08:40 +0100 Subject: [PATCH 03/10] Migrate to `crypto.Keccak` in trie/hasher.go and trie/stacktrie.go --- trie/hasher.go | 26 ++------------------------ trie/stacktrie.go | 5 ++--- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/trie/hasher.go b/trie/hasher.go index 5ce3657b70..40e95c7afe 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -28,7 +28,6 @@ import ( // hasher is a type used for the trie Hash operation. A hasher has some // internal preallocated temp space type hasher struct { - sha crypto.KeccakState tmp []byte encbuf rlp.EncoderBuffer parallel bool // Whether to use parallel threads when hashing @@ -39,7 +38,6 @@ var hasherPool = sync.Pool{ New: func() any { return &hasher{ tmp: make([]byte, 0, 550), // cap is as large as a full fullNode. - sha: crypto.NewKeccakState(), encbuf: rlp.NewEncoderBuffer(nil), } }, @@ -74,7 +72,7 @@ func (h *hasher) hash(n node, force bool) []byte { copy(buf, enc) return buf } - hash := h.hashData(enc) + hash := crypto.Keccak256(enc) n.flags.hash = hash return hash @@ -89,7 +87,7 @@ func (h *hasher) hash(n node, force bool) []byte { copy(buf, enc) return buf } - hash := h.hashData(enc) + hash := crypto.Keccak256(enc) n.flags.hash = hash return hash @@ -186,26 +184,6 @@ func (h *hasher) encodedBytes() []byte { return h.tmp } -// hashData hashes the provided data. It is safe to modify the returned slice after -// the function returns. -func (h *hasher) hashData(data []byte) []byte { - panic("Used by replay.wasm") - n := make([]byte, 32) - h.sha.Reset() - h.sha.Write(data) - h.sha.Read(n) - return n -} - -// hashDataTo hashes the provided data to the given destination buffer. The caller -// must ensure that the dst buffer is of appropriate size. -func (h *hasher) hashDataTo(dst, data []byte) { - panic("Used by replay.wasm") - h.sha.Reset() - h.sha.Write(data) - h.sha.Read(dst) -} - // proofHash is used to construct trie proofs, returning the rlp-encoded node blobs. // Note, only resolved node (shortNode or fullNode) is expected for proofing. // diff --git a/trie/stacktrie.go b/trie/stacktrie.go index 7277b603cc..9d16b937e2 100644 --- a/trie/stacktrie.go +++ b/trie/stacktrie.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" ) var ( @@ -414,9 +415,7 @@ func (t *StackTrie) hash(st *stNode, path []byte) { } // Write the hash to the 'val'. We allocate a new val here to not mutate // input values. - st.val = bPool.getWithSize(32) - panic("Used by replay.wasm") - t.h.hashDataTo(st.val, blob) + st.val = crypto.Keccak256(blob) // Invoke the callback it's provided. Notably, the path and blob slices are // volatile, please deep-copy the slices in callback if the contents need From 7e136984dc1066b066c2f76f43e80faa7acbca7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 14:15:19 +0100 Subject: [PATCH 04/10] Migrate to `crypto.Keccak` in core/vm --- core/vm/evm.go | 7 +------ core/vm/instructions.go | 14 ++++++-------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index d88838d42b..864de09896 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -133,9 +133,6 @@ type EVM struct { // jumpDests stores results of JUMPDEST analysis. jumpDests JumpDestCache - hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes - hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes - readOnly bool // Whether to throw on stateful modifications returnData []byte // Last CALL's return data for subsequent reuse } @@ -152,7 +149,6 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time, blockCtx.ArbOSVersion), jumpDests: newMapJumpDests(), - hasher: crypto.NewKeccakState(), } evm.ProcessingHook = DefaultTxProcessor{evm: evm} evm.precompiles = activePrecompiledContracts(evm.chainRules) @@ -703,8 +699,7 @@ 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, usedMultiGas multigas.MultiGas, err error) { - panic("Used by replay.wasm") - inithash := crypto.HashData(evm.hasher, code) + inithash := crypto.Keccak256Hash(code) contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:]) return evm.create(caller, code, gas, endowment, contractAddr, CREATE2) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 6ca15d257a..2764637c40 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -20,13 +20,15 @@ import ( "math" "math/big" + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/arbitrum/multigas" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "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" ) func opAdd(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { @@ -235,15 +237,11 @@ func opSAR(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { func opKeccak256(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { offset, size := scope.Stack.pop(), scope.Stack.peek() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) - panic("Used by replay.wasm") - evm.hasher.Reset() - evm.hasher.Write(data) - evm.hasher.Read(evm.hasherBuf[:]) - + hasherBuf := crypto.Keccak256Hash(data) if evm.Config.EnablePreimageRecording { - evm.StateDB.AddPreimage(evm.hasherBuf, data) + evm.StateDB.AddPreimage(hasherBuf, data) } - size.SetBytes(evm.hasherBuf[:]) + size.SetBytes(hasherBuf[:]) return nil, nil } From e83a03fb86b20462786f9035a9f35c6e28bf8346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Wed, 10 Dec 2025 14:19:34 +0100 Subject: [PATCH 05/10] Remove the rest of panics --- cmd/devp2p/internal/ethtest/snap.go | 2 -- cmd/geth/dbcmd.go | 1 - cmd/geth/snapshot.go | 2 -- cmd/workload/historytestgen.go | 1 - consensus/clique/clique.go | 1 - core/stateless/database.go | 2 -- crypto/crypto.go | 1 - eth/protocols/snap/sync.go | 3 --- 8 files changed, 13 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/snap.go b/cmd/devp2p/internal/ethtest/snap.go index 108a472715..f4fce0931f 100644 --- a/cmd/devp2p/internal/ethtest/snap.go +++ b/cmd/devp2p/internal/ethtest/snap.go @@ -907,7 +907,6 @@ func (s *Suite) snapGetByteCodes(t *utesting.T, tc *byteCodesTest) error { for i, j := 0, 0; i < len(bytecodes); i++ { // Find the next hash that we've been served, leaving misses with nils - panic("Used by replay.wasm") hasher.Reset() hasher.Write(bytecodes[i]) hasher.Read(hash) @@ -967,7 +966,6 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error { return fmt.Errorf("wrong trienode count, got %d, want %d", got, want) } for i, trienode := range trienodes { - panic("Used by replay.wasm") hasher.Reset() hasher.Write(trienode) hasher.Read(hash) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 286cee1c34..200f8a1534 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -366,7 +366,6 @@ func checkStateContent(ctx *cli.Context) error { count++ k := it.Key() v := it.Value() - panic("Used by replay.wasm") hasher.Reset() hasher.Write(v) hasher.Read(got) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index c50fd66dd0..f14105f116 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -460,7 +460,6 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Missing trie node(account)", "hash", node) return errors.New("missing account") } - panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(got) @@ -502,7 +501,6 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Missing trie node(storage)", "hash", node) return errors.New("missing storage") } - panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(got) diff --git a/cmd/workload/historytestgen.go b/cmd/workload/historytestgen.go index f85c6b7ff0..8fe41fc36a 100644 --- a/cmd/workload/historytestgen.go +++ b/cmd/workload/historytestgen.go @@ -134,7 +134,6 @@ func generateHistoryTests(clictx *cli.Context) error { func calcReceiptsHash(rcpt []*types.Receipt) common.Hash { h := crypto.NewKeccakState() - panic("Used by replay.wasm") rlp.Encode(h, rcpt) return common.Hash(h.Sum(nil)) } diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 24e805b714..a202ab02b4 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -644,7 +644,6 @@ func (c *Clique) Close() error { // SealHash returns the hash of a block prior to it being sealed. func SealHash(header *types.Header) (hash common.Hash) { - panic("Used by replay.wasm") hasher := sha3.NewLegacyKeccak256() encodeSigHeader(hasher, header) hasher.(crypto.KeccakState).Read(hash[:]) diff --git a/core/stateless/database.go b/core/stateless/database.go index 197bca1c9d..747665efe8 100644 --- a/core/stateless/database.go +++ b/core/stateless/database.go @@ -46,7 +46,6 @@ func (w *Witness) MakeHashDB() ethdb.Database { // Inject all the bytecodes into the ephemeral database for code := range w.Codes { blob := []byte(code) - panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(hash) @@ -56,7 +55,6 @@ func (w *Witness) MakeHashDB() ethdb.Database { // Inject all the MPT trie nodes into the ephemeral database for node := range w.State { blob := []byte(node) - panic("Used by replay.wasm") hasher.Reset() hasher.Write(blob) hasher.Read(hash) diff --git a/crypto/crypto.go b/crypto/crypto.go index ddcb57d557..db6b6ee071 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -69,7 +69,6 @@ type KeccakState interface { // HashData hashes the provided data using the KeccakState and returns a 32 byte hash func HashData(kh KeccakState, data []byte) (h common.Hash) { - panic("Used by replay.wasm") kh.Reset() kh.Write(data) kh.Read(h[:]) diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index 4b3c54ab8f..cf4e494645 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -2678,7 +2678,6 @@ func (s *Syncer) onByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error codes := make([][]byte, len(req.hashes)) for i, j := 0, 0; i < len(bytecodes); i++ { // Find the next hash that we've been served, leaving misses with nils - panic("Used by replay.wasm") hasher.Reset() hasher.Write(bytecodes[i]) hasher.Read(hash) @@ -2928,7 +2927,6 @@ func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error ) for i, j := 0, 0; i < len(trienodes); i++ { // Find the next hash that we've been served, leaving misses with nils - panic("Used by replay.wasm") hasher.Reset() hasher.Write(trienodes[i]) hasher.Read(hash) @@ -3034,7 +3032,6 @@ func (s *Syncer) onHealByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) e codes := make([][]byte, len(req.hashes)) for i, j := 0, 0; i < len(bytecodes); i++ { // Find the next hash that we've been served, leaving misses with nils - panic("Used by replay.wasm") hasher.Reset() hasher.Write(bytecodes[i]) hasher.Read(hash) From 6a622f35a0e4f86f6aeb05ae6c21bc88d9fba2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 15 Dec 2025 11:37:22 +0100 Subject: [PATCH 06/10] Revert IDE changes to non-relevant files --- cmd/geth/dbcmd.go | 5 ++--- cmd/geth/snapshot.go | 3 +-- cmd/workload/historytestgen.go | 3 +-- consensus/clique/clique.go | 3 +-- core/stateless/database.go | 2 ++ 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 200f8a1534..c57add0656 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -28,9 +28,6 @@ import ( "syscall" "time" - "github.com/olekukonko/tablewriter" - "github.com/urfave/cli/v2" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -44,6 +41,8 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/olekukonko/tablewriter" + "github.com/urfave/cli/v2" ) var ( diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index f14105f116..fe0bf97db8 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -25,8 +25,6 @@ import ( "slices" "time" - "github.com/urfave/cli/v2" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -38,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" + "github.com/urfave/cli/v2" ) var ( diff --git a/cmd/workload/historytestgen.go b/cmd/workload/historytestgen.go index 8fe41fc36a..b88c8208da 100644 --- a/cmd/workload/historytestgen.go +++ b/cmd/workload/historytestgen.go @@ -24,13 +24,12 @@ import ( "os" "path/filepath" - "github.com/urfave/cli/v2" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/rlp" + "github.com/urfave/cli/v2" ) var ( diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index a202ab02b4..5128a9d9f4 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -27,8 +27,6 @@ import ( "sync" "time" - "golang.org/x/crypto/sha3" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/lru" @@ -44,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" + "golang.org/x/crypto/sha3" ) const ( diff --git a/core/stateless/database.go b/core/stateless/database.go index 747665efe8..f54c123dda 100644 --- a/core/stateless/database.go +++ b/core/stateless/database.go @@ -46,6 +46,7 @@ func (w *Witness) MakeHashDB() ethdb.Database { // Inject all the bytecodes into the ephemeral database for code := range w.Codes { blob := []byte(code) + hasher.Reset() hasher.Write(blob) hasher.Read(hash) @@ -55,6 +56,7 @@ func (w *Witness) MakeHashDB() ethdb.Database { // Inject all the MPT trie nodes into the ephemeral database for node := range w.State { blob := []byte(node) + hasher.Reset() hasher.Write(blob) hasher.Read(hash) From 5211893ff58114b9cd413bd5e663560fcb232810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 15 Dec 2025 12:21:32 +0100 Subject: [PATCH 07/10] Revert substitution at the call side --- core/types/bloom9.go | 7 +++++-- core/types/hashing.go | 22 ++++++++++++++++++---- core/vm/evm.go | 6 +++++- core/vm/instructions.go | 14 ++++++++------ trie/hasher.go | 24 ++++++++++++++++++++++-- trie/stacktrie.go | 4 ++-- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/core/types/bloom9.go b/core/types/bloom9.go index a766b2e89a..1d57e8e4bc 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -141,8 +141,11 @@ func Bloom9(data []byte) []byte { // bloomValues returns the bytes (index-value pairs) to set for the given data func bloomValues(data []byte, hashbuf *[6]byte) (uint, byte, uint, byte, uint, byte) { - hash := crypto.Keccak256(data) - copy(hashbuf[:], hash[:6]) + sha := hasherPool.Get().(crypto.KeccakState) + 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)) diff --git a/core/types/hashing.go b/core/types/hashing.go index 31e0e91e99..98fe64e15a 100644 --- a/core/types/hashing.go +++ b/core/types/hashing.go @@ -27,6 +27,11 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +// hasherPool holds LegacyKeccak256 buffer for rlpHash. +var hasherPool = sync.Pool{ + New: func() interface{} { return crypto.NewKeccakState() }, +} + // encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding. var encodeBufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, @@ -50,15 +55,24 @@ func getPooledBuffer(size uint64) ([]byte, *bytes.Buffer, error) { // rlpHash encodes x and hashes the encoded bytes. func rlpHash(x interface{}) (h common.Hash) { - encoded, _ := rlp.EncodeToBytes(x) - return crypto.Keccak256Hash(encoded) + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + rlp.Encode(sha, x) + sha.Read(h[:]) + return h } // 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) { - encoded, _ := rlp.EncodeToBytes(x) - return crypto.Keccak256Hash([]byte{prefix}, encoded) + sha := hasherPool.Get().(crypto.KeccakState) + defer hasherPool.Put(sha) + sha.Reset() + sha.Write([]byte{prefix}) + rlp.Encode(sha, x) + sha.Read(h[:]) + return h } // ListHasher defines the interface for computing the hash of a derivable list. diff --git a/core/vm/evm.go b/core/vm/evm.go index 864de09896..ef637e4daf 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -133,6 +133,9 @@ type EVM struct { // jumpDests stores results of JUMPDEST analysis. jumpDests JumpDestCache + hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes + hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes + readOnly bool // Whether to throw on stateful modifications returnData []byte // Last CALL's return data for subsequent reuse } @@ -149,6 +152,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time, blockCtx.ArbOSVersion), jumpDests: newMapJumpDests(), + hasher: crypto.NewKeccakState(), } evm.ProcessingHook = DefaultTxProcessor{evm: evm} evm.precompiles = activePrecompiledContracts(evm.chainRules) @@ -699,7 +703,7 @@ 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, usedMultiGas multigas.MultiGas, err error) { - inithash := crypto.Keccak256Hash(code) + inithash := crypto.HashData(evm.hasher, code) contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:]) return evm.create(caller, code, gas, endowment, contractAddr, CREATE2) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 2764637c40..44346af5e8 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -20,15 +20,13 @@ import ( "math" "math/big" - "github.com/holiman/uint256" - "github.com/ethereum/go-ethereum/arbitrum/multigas" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "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" ) func opAdd(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { @@ -237,11 +235,15 @@ func opSAR(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { func opKeccak256(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { offset, size := scope.Stack.pop(), scope.Stack.peek() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) - hasherBuf := crypto.Keccak256Hash(data) + + evm.hasher.Reset() + evm.hasher.Write(data) + evm.hasher.Read(evm.hasherBuf[:]) + if evm.Config.EnablePreimageRecording { - evm.StateDB.AddPreimage(hasherBuf, data) + evm.StateDB.AddPreimage(evm.hasherBuf, data) } - size.SetBytes(hasherBuf[:]) + size.SetBytes(evm.hasherBuf[:]) return nil, nil } diff --git a/trie/hasher.go b/trie/hasher.go index 40e95c7afe..a2a1f5b662 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -28,6 +28,7 @@ import ( // hasher is a type used for the trie Hash operation. A hasher has some // internal preallocated temp space type hasher struct { + sha crypto.KeccakState tmp []byte encbuf rlp.EncoderBuffer parallel bool // Whether to use parallel threads when hashing @@ -38,6 +39,7 @@ var hasherPool = sync.Pool{ New: func() any { return &hasher{ tmp: make([]byte, 0, 550), // cap is as large as a full fullNode. + sha: crypto.NewKeccakState(), encbuf: rlp.NewEncoderBuffer(nil), } }, @@ -72,7 +74,7 @@ func (h *hasher) hash(n node, force bool) []byte { copy(buf, enc) return buf } - hash := crypto.Keccak256(enc) + hash := h.hashData(enc) n.flags.hash = hash return hash @@ -87,7 +89,7 @@ func (h *hasher) hash(n node, force bool) []byte { copy(buf, enc) return buf } - hash := crypto.Keccak256(enc) + hash := h.hashData(enc) n.flags.hash = hash return hash @@ -184,6 +186,24 @@ func (h *hasher) encodedBytes() []byte { return h.tmp } +// hashData hashes the provided data. It is safe to modify the returned slice after +// the function returns. +func (h *hasher) hashData(data []byte) []byte { + n := make([]byte, 32) + h.sha.Reset() + h.sha.Write(data) + h.sha.Read(n) + return n +} + +// hashDataTo hashes the provided data to the given destination buffer. The caller +// must ensure that the dst buffer is of appropriate size. +func (h *hasher) hashDataTo(dst, data []byte) { + h.sha.Reset() + h.sha.Write(data) + h.sha.Read(dst) +} + // proofHash is used to construct trie proofs, returning the rlp-encoded node blobs. // Note, only resolved node (shortNode or fullNode) is expected for proofing. // diff --git a/trie/stacktrie.go b/trie/stacktrie.go index 9d16b937e2..18fe1eea78 100644 --- a/trie/stacktrie.go +++ b/trie/stacktrie.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" ) var ( @@ -415,7 +414,8 @@ func (t *StackTrie) hash(st *stNode, path []byte) { } // Write the hash to the 'val'. We allocate a new val here to not mutate // input values. - st.val = crypto.Keccak256(blob) + st.val = bPool.getWithSize(32) + t.h.hashDataTo(st.val, blob) // Invoke the callback it's provided. Notably, the path and blob slices are // volatile, please deep-copy the slices in callback if the contents need From 3d2a4dd98f443f69ab2f30ae186a48d966def9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 15 Dec 2025 12:49:15 +0100 Subject: [PATCH 08/10] Add a simple buffer as the Keccak state for wasm --- accounts/accounts.go | 4 +- cmd/evm/internal/t8ntool/execution.go | 7 ++-- consensus/clique/clique.go | 3 +- consensus/ethash/consensus.go | 4 +- core/rawdb/accessors_chain_test.go | 7 ++-- core/rlp_test.go | 3 +- core/state/snapshot/generate_test.go | 4 +- core/state_processor_test.go | 3 +- crypto/keccak.go | 5 +-- crypto/keccak_state.go | 9 +++++ crypto/keccak_state_wasm.go | 55 +++++++++++++++++++++++++++ eth/protocols/snap/sync_test.go | 8 ++-- internal/blocktest/test_hash.go | 4 +- p2p/dnsdisc/tree.go | 5 +-- p2p/enode/idscheme.go | 5 +-- p2p/rlpx/rlpx.go | 8 ++-- tests/state_test_util.go | 6 +-- trie/trie_test.go | 12 +++--- 18 files changed, 105 insertions(+), 47 deletions(-) create mode 100644 crypto/keccak_state.go create mode 100644 crypto/keccak_state_wasm.go diff --git a/accounts/accounts.go b/accounts/accounts.go index 7bd911577a..e95a0f515a 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -24,8 +24,8 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" - "golang.org/x/crypto/sha3" ) // Account represents an Ethereum account located at a specific location defined @@ -196,7 +196,7 @@ func TextHash(data []byte) []byte { // This gives context to the signed message and prevents signing of transactions. func TextAndHash(data []byte) ([]byte, string) { msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() hasher.Write([]byte(msg)) return hasher.Sum(nil), msg } diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 865a5944a7..1e5833885d 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -20,6 +20,8 @@ import ( "fmt" "math/big" + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" @@ -32,14 +34,13 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "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/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" - "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) type Prestate struct { @@ -403,7 +404,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB } func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewLegacyKeccak256() + hw := crypto.NewLegacyKeccak256() rlp.Encode(hw, x) hw.Sum(h[:0]) return h diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 5128a9d9f4..a83589e5e5 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -42,7 +42,6 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "golang.org/x/crypto/sha3" ) const ( @@ -643,7 +642,7 @@ func (c *Clique) Close() error { // SealHash returns the hash of a block prior to it being sealed. func SealHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() encodeSigHeader(hasher, header) hasher.(crypto.KeccakState).Read(hash[:]) return hash diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 9211197687..9f5c845a12 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -31,11 +31,11 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "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/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) // Ethash proof-of-work protocol constants. @@ -528,7 +528,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // SealHash returns the hash of a block prior to it being sealed. func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() enc := []interface{}{ header.ParentHash, diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 819788b4da..ed7f093824 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) // Tests block header storage and retrieval operations. @@ -53,7 +52,7 @@ func TestHeaderStorage(t *testing.T) { if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil { t.Fatalf("Stored header RLP not found") } else { - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() hasher.Write(entry) if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() { @@ -74,7 +73,7 @@ func TestBodyStorage(t *testing.T) { // Create a test body to move around the database and make sure it's really new body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() rlp.Encode(hasher, body) hash := common.BytesToHash(hasher.Sum(nil)) @@ -91,7 +90,7 @@ func TestBodyStorage(t *testing.T) { if entry := ReadBodyRLP(db, hash, 0); entry == nil { t.Fatalf("Stored body RLP not found") } else { - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() hasher.Write(entry) if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash { diff --git a/core/rlp_test.go b/core/rlp_test.go index 69efa82551..eab646e360 100644 --- a/core/rlp_test.go +++ b/core/rlp_test.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) func getBlock(transactions int, uncles int, dataSize int) *types.Block { @@ -147,7 +146,7 @@ func BenchmarkHashing(b *testing.B) { blockRlp, _ = rlp.EncodeToBytes(block) } var got common.Hash - var hasher = sha3.NewLegacyKeccak256() + var hasher = crypto.NewLegacyKeccak256() b.Run("iteratorhashing", func(b *testing.B) { for b.Loop() { var hash common.Hash diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index 63d6e930f9..ab5848aa49 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -34,11 +35,10 @@ import ( "github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) func hashData(input []byte) common.Hash { - var hasher = sha3.NewLegacyKeccak256() + var hasher = crypto.NewLegacyKeccak256() var hash common.Hash hasher.Reset() hasher.Write(input) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 64001e2857..e9e9151877 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -34,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) func u64(val uint64) *uint64 { return &val } @@ -399,7 +398,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr var receipts []*types.Receipt // The post-state result doesn't need to be correct (this is a bad block), but we do need something there // Preferably something unique. So let's use a combo of blocknum + txhash - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() hasher.Write(header.Number.Bytes()) var cumulativeGas uint64 var nBlobs int diff --git a/crypto/keccak.go b/crypto/keccak.go index 0ad79a63c1..1b14eee903 100644 --- a/crypto/keccak.go +++ b/crypto/keccak.go @@ -22,17 +22,16 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" - "golang.org/x/crypto/sha3" ) // NewKeccakState creates a new KeccakState func NewKeccakState() KeccakState { - return sha3.NewLegacyKeccak256().(KeccakState) + return NewLegacyKeccak256().(KeccakState) } var hasherPool = sync.Pool{ New: func() any { - return sha3.NewLegacyKeccak256().(KeccakState) + return NewLegacyKeccak256().(KeccakState) }, } diff --git a/crypto/keccak_state.go b/crypto/keccak_state.go new file mode 100644 index 0000000000..0868c2c238 --- /dev/null +++ b/crypto/keccak_state.go @@ -0,0 +1,9 @@ +//go:build !ziren && !wasm + +package crypto + +import "golang.org/x/crypto/sha3" + +func NewLegacyKeccak256() KeccakState { + return sha3.NewLegacyKeccak256().(KeccakState) +} diff --git a/crypto/keccak_state_wasm.go b/crypto/keccak_state_wasm.go new file mode 100644 index 0000000000..78a7813b12 --- /dev/null +++ b/crypto/keccak_state_wasm.go @@ -0,0 +1,55 @@ +//go:build !ziren && wasm + +package crypto + +import ( + "sync" + + "golang.org/x/crypto/sha3" +) + +func NewLegacyKeccak256() KeccakState { + return &simpleHashBuffer{} +} + +type simpleHashBuffer struct { + buffer []byte +} + +func (s *simpleHashBuffer) Write(p []byte) (n int, err error) { + s.buffer = append(s.buffer, p...) + return len(p), nil +} + +func (s *simpleHashBuffer) Sum(b []byte) []byte { + currentBufferHash := make([]byte, 32) + s.Read(currentBufferHash) + return append(b, currentBufferHash...) +} + +func (s *simpleHashBuffer) Reset() { + s.buffer = nil // Simply forget previous data. +} + +func (s *simpleHashBuffer) Size() int { + return 32 // Keccak256 produces 32-byte hashes +} + +func (s *simpleHashBuffer) BlockSize() int { + return (1600 - 512) / 8 // Keccak256 rate in bytes: sponge size 1600 bits - capacity 512 bits. Copied from sha3. +} + +var realHasherPool = sync.Pool{ + New: func() any { + return sha3.NewLegacyKeccak256().(KeccakState) + }, +} + +func (s *simpleHashBuffer) Read(bytes []byte) (int, error) { + d := realHasherPool.Get().(KeccakState) + defer realHasherPool.Put(d) + + d.Reset() + d.Write(s.buffer) + return d.Read(bytes) +} diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index 713b358ff8..3c0295fb65 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -28,6 +28,8 @@ import ( "testing" "time" + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -40,8 +42,6 @@ import ( "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/pathdb" - "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) func TestHashing(t *testing.T) { @@ -55,7 +55,7 @@ func TestHashing(t *testing.T) { } var want, got string var old = func() { - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() for i := 0; i < len(bytecodes); i++ { hasher.Reset() hasher.Write(bytecodes[i]) @@ -88,7 +88,7 @@ func BenchmarkHashing(b *testing.B) { bytecodes[i] = buf } var old = func() { - hasher := sha3.NewLegacyKeccak256() + hasher := crypto.NewLegacyKeccak256() for i := 0; i < len(bytecodes); i++ { hasher.Reset() hasher.Write(bytecodes[i]) diff --git a/internal/blocktest/test_hash.go b/internal/blocktest/test_hash.go index b3e7098e2b..e0b3853fda 100644 --- a/internal/blocktest/test_hash.go +++ b/internal/blocktest/test_hash.go @@ -27,7 +27,7 @@ import ( "hash" "github.com/ethereum/go-ethereum/common" - "golang.org/x/crypto/sha3" + "github.com/ethereum/go-ethereum/crypto" ) // testHasher is the helper tool for transaction/receipt list hashing. @@ -39,7 +39,7 @@ type testHasher struct { // NewHasher returns a new testHasher instance. func NewHasher() *testHasher { - return &testHasher{hasher: sha3.NewLegacyKeccak256()} + return &testHasher{hasher: crypto.NewLegacyKeccak256()} } // Reset resets the hash state. diff --git a/p2p/dnsdisc/tree.go b/p2p/dnsdisc/tree.go index a8295ac9eb..aad82ad11b 100644 --- a/p2p/dnsdisc/tree.go +++ b/p2p/dnsdisc/tree.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) // Tree is a merkle tree of node records. @@ -262,7 +261,7 @@ const ( ) func subdomain(e entry) string { - h := sha3.NewLegacyKeccak256() + h := crypto.NewLegacyKeccak256() io.WriteString(h, e.String()) return b32format.EncodeToString(h.Sum(nil)[:16]) } @@ -272,7 +271,7 @@ func (e *rootEntry) String() string { } func (e *rootEntry) sigHash() []byte { - h := sha3.NewLegacyKeccak256() + h := crypto.NewLegacyKeccak256() fmt.Fprintf(h, rootPrefix+" e=%s l=%s seq=%d", e.eroot, e.lroot, e.seq) return h.Sum(nil) } diff --git a/p2p/enode/idscheme.go b/p2p/enode/idscheme.go index db7841c047..e43c9d9051 100644 --- a/p2p/enode/idscheme.go +++ b/p2p/enode/idscheme.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) // ValidSchemes is a List of known secure identity schemes. @@ -49,7 +48,7 @@ func SignV4(r *enr.Record, privkey *ecdsa.PrivateKey) error { cpy.Set(enr.ID("v4")) cpy.Set(Secp256k1(privkey.PublicKey)) - h := sha3.NewLegacyKeccak256() + h := crypto.NewLegacyKeccak256() rlp.Encode(h, cpy.AppendElements(nil)) sig, err := crypto.Sign(h.Sum(nil), privkey) if err != nil { @@ -70,7 +69,7 @@ func (V4ID) Verify(r *enr.Record, sig []byte) error { return errors.New("invalid public key") } - h := sha3.NewLegacyKeccak256() + h := crypto.NewLegacyKeccak256() rlp.Encode(h, r.AppendElements(nil)) if !crypto.VerifySignature(entry, h.Sum(nil), sig) { return enr.ErrInvalidSig diff --git a/p2p/rlpx/rlpx.go b/p2p/rlpx/rlpx.go index c074534d4d..62aac8adbe 100644 --- a/p2p/rlpx/rlpx.go +++ b/p2p/rlpx/rlpx.go @@ -33,12 +33,12 @@ import ( "net" "time" + "github.com/golang/snappy" + "github.com/ethereum/go-ethereum/common/bitutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/rlp" - "github.com/golang/snappy" - "golang.org/x/crypto/sha3" ) // Conn is an RLPx network connection. It wraps a low-level network connection. The @@ -486,10 +486,10 @@ func (h *handshakeState) secrets(auth, authResp []byte) (Secrets, error) { } // setup sha3 instances for the MACs - mac1 := sha3.NewLegacyKeccak256() + mac1 := crypto.NewLegacyKeccak256() mac1.Write(xor(s.MAC, h.respNonce)) mac1.Write(auth) - mac2 := sha3.NewLegacyKeccak256() + mac2 := crypto.NewLegacyKeccak256() mac2.Write(xor(s.MAC, h.initNonce)) mac2.Write(authResp) if h.initiator { diff --git a/tests/state_test_util.go b/tests/state_test_util.go index cc02b9feea..c49e71ef36 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -25,6 +25,8 @@ import ( "strconv" "strings" + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/arbitrum/multigas" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -45,8 +47,6 @@ import ( "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/pathdb" - "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) // StateTest checks transaction processing without block context. @@ -488,7 +488,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess } func rlpHash(x interface{}) (h common.Hash) { - hw := sha3.NewLegacyKeccak256() + hw := crypto.NewLegacyKeccak256() rlp.Encode(hw, x) hw.Sum(h[:0]) return h diff --git a/trie/trie_test.go b/trie/trie_test.go index b8b8edb33e..cd3150413c 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -31,6 +31,8 @@ import ( "testing/quick" "github.com/davecgh/go-spew/spew" + "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -39,8 +41,6 @@ import ( "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/holiman/uint256" - "golang.org/x/crypto/sha3" ) func init() { @@ -968,7 +968,7 @@ func TestCommitSequenceStackTrie(t *testing.T) { prng := rand.New(rand.NewSource(int64(count))) // This spongeDb is used to check the sequence of disk-db-writes s := &spongeDb{ - sponge: sha3.NewLegacyKeccak256(), + sponge: crypto.NewLegacyKeccak256(), id: "a", values: make(map[string]string), } @@ -977,7 +977,7 @@ func TestCommitSequenceStackTrie(t *testing.T) { // Another sponge is used for the stacktrie commits stackTrieSponge := &spongeDb{ - sponge: sha3.NewLegacyKeccak256(), + sponge: crypto.NewLegacyKeccak256(), id: "b", values: make(map[string]string), } @@ -1040,7 +1040,7 @@ func TestCommitSequenceStackTrie(t *testing.T) { // not fit into 32 bytes, rlp-encoded. However, it's still the correct thing to do. func TestCommitSequenceSmallRoot(t *testing.T) { s := &spongeDb{ - sponge: sha3.NewLegacyKeccak256(), + sponge: crypto.NewLegacyKeccak256(), id: "a", values: make(map[string]string), } @@ -1049,7 +1049,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) { // Another sponge is used for the stacktrie commits stackTrieSponge := &spongeDb{ - sponge: sha3.NewLegacyKeccak256(), + sponge: crypto.NewLegacyKeccak256(), id: "b", values: make(map[string]string), } From 30459f2eb267accd5b7e91959315848f32a826c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 15 Dec 2025 13:16:55 +0100 Subject: [PATCH 09/10] Revert import reordering --- cmd/evm/internal/t8ntool/execution.go | 3 +-- eth/protocols/snap/sync_test.go | 3 +-- p2p/rlpx/rlpx.go | 3 +-- tests/state_test_util.go | 3 +-- trie/trie_test.go | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 1e5833885d..313a3a9114 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -20,8 +20,6 @@ import ( "fmt" "math/big" - "github.com/holiman/uint256" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" @@ -41,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" + "github.com/holiman/uint256" ) type Prestate struct { diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index 3c0295fb65..79d43323ce 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -28,8 +28,6 @@ import ( "testing" "time" - "github.com/holiman/uint256" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -42,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/holiman/uint256" ) func TestHashing(t *testing.T) { diff --git a/p2p/rlpx/rlpx.go b/p2p/rlpx/rlpx.go index 62aac8adbe..cdb32537aa 100644 --- a/p2p/rlpx/rlpx.go +++ b/p2p/rlpx/rlpx.go @@ -33,12 +33,11 @@ import ( "net" "time" - "github.com/golang/snappy" - "github.com/ethereum/go-ethereum/common/bitutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/rlp" + "github.com/golang/snappy" ) // Conn is an RLPx network connection. It wraps a low-level network connection. The diff --git a/tests/state_test_util.go b/tests/state_test_util.go index c49e71ef36..49646a38e6 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -25,8 +25,6 @@ import ( "strconv" "strings" - "github.com/holiman/uint256" - "github.com/ethereum/go-ethereum/arbitrum/multigas" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -47,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/hashdb" "github.com/ethereum/go-ethereum/triedb/pathdb" + "github.com/holiman/uint256" ) // StateTest checks transaction processing without block context. diff --git a/trie/trie_test.go b/trie/trie_test.go index cd3150413c..047d995ce9 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -31,7 +31,6 @@ import ( "testing/quick" "github.com/davecgh/go-spew/spew" - "github.com/holiman/uint256" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -41,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/holiman/uint256" ) func init() { From 3decc7ccf381108bfceeb347d831b9110b317f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Miko=C5=82ajczyk?= Date: Mon, 15 Dec 2025 13:17:47 +0100 Subject: [PATCH 10/10] Empty line --- trie/trie_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/trie/trie_test.go b/trie/trie_test.go index 047d995ce9..fc68ca7f72 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -31,7 +31,6 @@ import ( "testing/quick" "github.com/davecgh/go-spew/spew" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types"