diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 49c115087f33..c944e910abc0 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -107,7 +107,7 @@ func TestBodyStorage(t *testing.T) { WriteBody(db, hash, 0, body) if entry := ReadBody(db, hash, 0); entry == nil { t.Fatalf("Stored body not found") - } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { + } else if types.DeriveSha(types.Transactions(entry.Transactions), newTestHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newTestHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) } if entry := ReadBodyRLP(db, hash, 0); entry == nil { @@ -158,7 +158,7 @@ func TestBlockStorage(t *testing.T) { } if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil { t.Fatalf("Stored body not found") - } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(block.Transactions(), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { + } else if types.DeriveSha(types.Transactions(entry.Transactions), newTestHasher()) != types.DeriveSha(types.Transactions(block.Transactions()), newTestHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body()) } // Delete the block and verify the execution diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index 542be224aed8..ee588429dbe3 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -17,41 +17,17 @@ package rawdb import ( - "hash" "math/big" "testing" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/crypto/keccak" "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/internal/blocktest" "github.com/XinFinOrg/XDPoSChain/rlp" ) -// testHasher is the helper tool for transaction/receipt list hashing. -// The original hasher is trie, in order to get rid of import cycle, -// use the testing hasher instead. -type testHasher struct { - hasher hash.Hash -} - -func newHasher() *testHasher { - return &testHasher{hasher: keccak.NewLegacyKeccak256()} -} - -func (h *testHasher) Reset() { - h.hasher.Reset() -} - -func (h *testHasher) Update(key, val []byte) error { - h.hasher.Write(key) - h.hasher.Write(val) - return nil -} - -func (h *testHasher) Hash() common.Hash { - return common.BytesToHash(h.hasher.Sum(nil)) -} +var newTestHasher = blocktest.NewHasher // Tests that positional lookup metadata can be stored and retrieved. func TestLookupStorage(t *testing.T) { @@ -98,7 +74,7 @@ func TestLookupStorage(t *testing.T) { tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) txs := []*types.Transaction{tx1, tx2, tx3} - block := types.NewBlock(&types.Header{Root: types.EmptyRootHash, Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newHasher()) + block := types.NewBlock(&types.Header{Root: types.EmptyRootHash, Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher()) // Check that no transactions entries are in a pristine database for i, tx := range txs { diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index 376aca8acf14..f41d31b94f43 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -18,11 +18,9 @@ package rawdb import ( "fmt" - "sync" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/keccak" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" ) @@ -46,45 +44,20 @@ const HashScheme = "hashScheme" // on extra state diffs to survive deep reorg. const PathScheme = "pathScheme" -// nodeHasher used to derive the hash of trie node. -type nodeHasher struct{ sha crypto.KeccakState } - -var hasherPool = sync.Pool{ - New: func() interface{} { return &nodeHasher{sha: keccak.NewLegacyKeccak256().(crypto.KeccakState)} }, -} - -func newNodeHasher() *nodeHasher { return hasherPool.Get().(*nodeHasher) } -func returnHasherToPool(h *nodeHasher) { hasherPool.Put(h) } - -func (h *nodeHasher) hashData(data []byte) (n common.Hash) { - h.sha.Reset() - h.sha.Write(data) - h.sha.Read(n[:]) - return n -} - -// ReadAccountTrieNode retrieves the account trie node and the associated node -// hash with the specified node path. -func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) { - data, err := db.Get(accountTrieNodeKey(path)) - if err != nil { - return nil, common.Hash{} - } - hasher := newNodeHasher() - defer returnHasherToPool(hasher) - return data, hasher.hashData(data) +// ReadAccountTrieNode retrieves the account trie node with the specified node path. +func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte { + data, _ := db.Get(accountTrieNodeKey(path)) + return data } -// HasAccountTrieNode checks the account trie node presence with the specified -// node path and the associated node hash. -func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash) bool { - data, err := db.Get(accountTrieNodeKey(path)) +// HasAccountTrieNode checks the presence of the account trie node with the +// specified node path, regardless of the node hash. +func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool { + has, err := db.Has(accountTrieNodeKey(path)) if err != nil { return false } - hasher := newNodeHasher() - defer returnHasherToPool(hasher) - return hasher.hashData(data) == hash + return has } // WriteAccountTrieNode writes the provided account trie node into database. @@ -101,28 +74,20 @@ func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) { } } -// ReadStorageTrieNode retrieves the storage trie node and the associated node -// hash with the specified node path. -func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) { - data, err := db.Get(storageTrieNodeKey(accountHash, path)) - if err != nil { - return nil, common.Hash{} - } - hasher := newNodeHasher() - defer returnHasherToPool(hasher) - return data, hasher.hashData(data) +// ReadStorageTrieNode retrieves the storage trie node with the specified node path. +func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) []byte { + data, _ := db.Get(storageTrieNodeKey(accountHash, path)) + return data } -// HasStorageTrieNode checks the storage trie node presence with the provided -// node path and the associated node hash. -func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte, hash common.Hash) bool { - data, err := db.Get(storageTrieNodeKey(accountHash, path)) +// HasStorageTrieNode checks the presence of the storage trie node with the +// specified account hash and node path, regardless of the node hash. +func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool { + has, err := db.Has(storageTrieNodeKey(accountHash, path)) if err != nil { return false } - hasher := newNodeHasher() - defer returnHasherToPool(hasher) - return hasher.hashData(data) == hash + return has } // WriteStorageTrieNode writes the provided storage trie node into database. @@ -176,10 +141,16 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c case HashScheme: return HasLegacyTrieNode(db, hash) case PathScheme: + var blob []byte if owner == (common.Hash{}) { - return HasAccountTrieNode(db, path, hash) + blob = ReadAccountTrieNode(db, path) + } else { + blob = ReadStorageTrieNode(db, owner, path) + } + if len(blob) == 0 { + return false } - return HasStorageTrieNode(db, owner, path, hash) + return crypto.Keccak256Hash(blob) == hash // exist and match default: panic(fmt.Sprintf("Unknown scheme %v", scheme)) } @@ -187,43 +158,33 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c // ReadTrieNode retrieves the trie node from database with the provided node info // and associated node hash. -// hashScheme-based lookup requires the following: -// - hash -// -// pathScheme-based lookup requires the following: -// - owner -// - path func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) []byte { switch scheme { case HashScheme: return ReadLegacyTrieNode(db, hash) case PathScheme: - var ( - blob []byte - nHash common.Hash - ) + var blob []byte if owner == (common.Hash{}) { - blob, nHash = ReadAccountTrieNode(db, path) + blob = ReadAccountTrieNode(db, path) } else { - blob, nHash = ReadStorageTrieNode(db, owner, path) + blob = ReadStorageTrieNode(db, owner, path) } - if nHash != hash { + if len(blob) == 0 { return nil } + if crypto.Keccak256Hash(blob) != hash { + return nil // exist but not match + } return blob default: panic(fmt.Sprintf("Unknown scheme %v", scheme)) } } -// WriteTrieNode writes the trie node into database with the provided node info -// and associated node hash. -// hashScheme-based lookup requires the following: -// - hash +// WriteTrieNode writes the trie node into database with the provided node info. // -// pathScheme-based lookup requires the following: -// - owner -// - path +// hash-scheme requires the node hash as the identifier. +// path-scheme requires the node owner and path as the identifier. func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, node []byte, scheme string) { switch scheme { case HashScheme: @@ -239,14 +200,10 @@ func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash } } -// DeleteTrieNode deletes the trie node from database with the provided node info -// and associated node hash. -// hashScheme-based lookup requires the following: -// - hash +// DeleteTrieNode deletes the trie node from database with the provided node info. // -// pathScheme-based lookup requires the following: -// - owner -// - path +// hash-scheme requires the node hash as the identifier. +// path-scheme requires the node owner and path as the identifier. func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, scheme string) { switch scheme { case HashScheme: diff --git a/core/types/block_test.go b/core/types/block_test.go index 8f9d317cff1c..2afeef96c081 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -18,7 +18,6 @@ package types import ( "bytes" - "hash" "math/big" "reflect" "testing" @@ -26,7 +25,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/keccak" + "github.com/XinFinOrg/XDPoSChain/internal/blocktest" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" ) @@ -148,31 +147,6 @@ func BenchmarkEncodeBlock(b *testing.B) { } } -// testHasher is the helper tool for transaction/receipt list hashing. -// The original hasher is trie, in order to get rid of import cycle, -// use the testing hasher instead. -type testHasher struct { - hasher hash.Hash -} - -func newHasher() *testHasher { - return &testHasher{hasher: keccak.NewLegacyKeccak256()} -} - -func (h *testHasher) Reset() { - h.hasher.Reset() -} - -func (h *testHasher) Update(key, val []byte) error { - h.hasher.Write(key) - h.hasher.Write(val) - return nil -} - -func (h *testHasher) Hash() common.Hash { - return common.BytesToHash(h.hasher.Sum(nil)) -} - func makeBenchBlock() *Block { var ( key, _ = crypto.GenerateKey() @@ -211,5 +185,5 @@ func makeBenchBlock() *Block { Extra: []byte("benchmark uncle"), } } - return NewBlock(header, &Body{Transactions: txs, Uncles: uncles}, receipts, newHasher()) + return NewBlock(header, &Body{Transactions: txs, Uncles: uncles}, receipts, blocktest.NewHasher()) } diff --git a/core/vm/evm.go b/core/vm/evm.go index 9960850c4bba..c80553f59ca1 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -125,9 +125,6 @@ type EVM struct { // the life cycle of EVM. jumpDests map[common.Hash]bitvec - 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 } @@ -145,7 +142,6 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, tradingStateDB *tradingstate chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber), jumpDests: make(map[common.Hash]bitvec), - hasher: crypto.NewKeccakState(), } evm.precompiles = activePrecompiledContracts(evm.chainRules) @@ -567,7 +563,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.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 4d16f6ca859b..a8228e92087a 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -22,6 +22,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/tracing" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" "github.com/holiman/uint256" ) @@ -233,14 +234,12 @@ 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()) - evm.hasher.Reset() - evm.hasher.Write(data) - evm.hasher.Read(evm.hasherBuf[:]) + hash := crypto.Keccak256Hash(data) if evm.Config.EnablePreimageRecording { - evm.StateDB.AddPreimage(evm.hasherBuf, data) + evm.StateDB.AddPreimage(hash, data) } - size.SetBytes(evm.hasherBuf[:]) + size.SetBytes(hash[:]) return nil, nil } diff --git a/internal/blocktest/test_hash.go b/internal/blocktest/test_hash.go new file mode 100644 index 000000000000..c42a2810680f --- /dev/null +++ b/internal/blocktest/test_hash.go @@ -0,0 +1,59 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package blocktest provides internal test helpers for block-related tests. +// +// It includes a lightweight hasher used for transaction and receipt list +// hashing in tests, avoiding an import cycle on the trie hasher. + +package blocktest + +import ( + "bytes" + "hash" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/crypto/keccak" +) + +// testHasher is the helper tool for transaction/receipt list hashing. +// The original hasher is trie, in order to get rid of import cycle, +// use the testing hasher instead. +type testHasher struct { + hasher hash.Hash +} + +// NewHasher returns a new testHasher instance. +func NewHasher() *testHasher { + return &testHasher{hasher: keccak.NewLegacyKeccak256()} +} + +// Reset resets the hash state. +func (h *testHasher) Reset() { + h.hasher.Reset() +} + +// Update updates the hash state with the given key and value. +func (h *testHasher) Update(key, val []byte) error { + h.hasher.Write(bytes.Clone(key)) + h.hasher.Write(bytes.Clone(val)) + return nil +} + +// Hash returns the hash value. +func (h *testHasher) Hash() common.Hash { + return common.BytesToHash(h.hasher.Sum(nil)) +} diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 56188054da8d..52c6d0416b84 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -21,7 +21,6 @@ import ( "encoding/json" "errors" "fmt" - "hash" "math/big" "strings" "testing" @@ -40,9 +39,9 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/keccak" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/ethdb" + "github.com/XinFinOrg/XDPoSChain/internal/blocktest" "github.com/XinFinOrg/XDPoSChain/internal/ethapi/override" "github.com/XinFinOrg/XDPoSChain/p2p" "github.com/XinFinOrg/XDPoSChain/params" @@ -51,30 +50,7 @@ import ( "github.com/stretchr/testify/require" ) -// testHasher is the helper tool for transaction/receipt list hashing. -// The original hasher is trie, in order to get rid of import cycle, -// use the testing hasher instead. -type testHasher struct { - hasher hash.Hash -} - -func newHasher() *testHasher { - return &testHasher{hasher: keccak.NewLegacyKeccak256()} -} - -func (h *testHasher) Reset() { - h.hasher.Reset() -} - -func (h *testHasher) Update(key, val []byte) error { - h.hasher.Write(key) - h.hasher.Write(val) - return nil -} - -func (h *testHasher) Hash() common.Hash { - return common.BytesToHash(h.hasher.Sum(nil)) -} +var newHasher = blocktest.NewHasher func TestRPCMarshalBlock(t *testing.T) { var ( diff --git a/trie/trienode/node.go b/trie/trienode/node.go index fe316a6a9294..639ce3dcba44 100644 --- a/trie/trienode/node.go +++ b/trie/trienode/node.go @@ -152,16 +152,6 @@ func (set *NodeSet) Size() (int, int) { return set.updates, set.deletes } -// Hashes returns the hashes of all updated nodes. TODO(rjl493456442) how can -// we get rid of it? -func (set *NodeSet) Hashes() []common.Hash { - var ret []common.Hash - for _, node := range set.Nodes { - ret = append(ret, node.Hash) - } - return ret -} - // Summary returns a string-representation of the NodeSet. func (set *NodeSet) Summary() string { var out = new(strings.Builder)