Unverified Commit 87c0ba92 authored by gary rong's avatar gary rong Committed by GitHub

core, eth, les, trie: add a prefix to contract code (#21080)

parent b68929ca
...@@ -34,6 +34,7 @@ import ( ...@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
...@@ -220,8 +221,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, ...@@ -220,8 +221,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
} }
execRs := &ExecutionResult{ execRs := &ExecutionResult{
StateRoot: root, StateRoot: root,
TxRoot: types.DeriveSha(includedTxs), TxRoot: types.DeriveSha(includedTxs, new(trie.Trie)),
ReceiptRoot: types.DeriveSha(receipts), ReceiptRoot: types.DeriveSha(receipts, new(trie.Trie)),
Bloom: types.CreateBloom(receipts), Bloom: types.CreateBloom(receipts),
LogsHash: rlpHash(statedb.Logs()), LogsHash: rlpHash(statedb.Logs()),
Receipts: receipts, Receipts: receipts,
......
...@@ -248,7 +248,7 @@ func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, ...@@ -248,7 +248,7 @@ func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader,
header.Root = statedb.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = statedb.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return // Header seems complete, assemble into a block and return
return types.NewBlock(header, txs, uncles, receipts), nil return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie)), nil
} }
} }
......
...@@ -39,6 +39,7 @@ import ( ...@@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
...@@ -565,7 +566,7 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * ...@@ -565,7 +566,7 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
header.UncleHash = types.CalcUncleHash(nil) header.UncleHash = types.CalcUncleHash(nil)
// Assemble and return the final block for sealing // Assemble and return the final block for sealing
return types.NewBlock(header, txs, nil, receipts), nil return types.NewBlock(header, txs, nil, receipts, new(trie.Trie)), nil
} }
// Authorize injects a private key into the consensus engine to mint new blocks // Authorize injects a private key into the consensus engine to mint new blocks
......
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
...@@ -583,7 +584,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea ...@@ -583,7 +584,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return // Header seems complete, assemble into a block and return
return types.NewBlock(header, txs, uncles, receipts), nil return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie)), nil
} }
// SealHash returns the hash of a block prior to it being sealed. // SealHash returns the hash of a block prior to it being sealed.
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
// BlockValidator is responsible for validating block headers, uncles and // BlockValidator is responsible for validating block headers, uncles and
...@@ -61,7 +62,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { ...@@ -61,7 +62,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash) return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
} }
if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash { if hash := types.DeriveSha(block.Transactions(), new(trie.Trie)); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
} }
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
...@@ -89,7 +90,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD ...@@ -89,7 +90,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
} }
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
receiptSha := types.DeriveSha(receipts) receiptSha := types.DeriveSha(receipts, new(trie.Trie))
if receiptSha != header.ReceiptHash { if receiptSha != header.ReceiptHash {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
} }
......
...@@ -112,7 +112,10 @@ const ( ...@@ -112,7 +112,10 @@ const (
// - Version 7 // - Version 7
// The following incompatible database changes were added: // The following incompatible database changes were added:
// * Use freezer as the ancient database to maintain all ancient data // * Use freezer as the ancient database to maintain all ancient data
BlockChainVersion uint64 = 7 // - Version 8
// The following incompatible database changes were added:
// * New scheme for contract code in order to separate the codes and trie nodes
BlockChainVersion uint64 = 8
) )
// CacheConfig contains the configuration values for the trie caching/pruning // CacheConfig contains the configuration values for the trie caching/pruning
...@@ -895,12 +898,30 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types. ...@@ -895,12 +898,30 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
return uncles return uncles
} }
// TrieNode retrieves a blob of data associated with a trie node (or code hash) // TrieNode retrieves a blob of data associated with a trie node
// either from ephemeral in-memory cache, or from persistent storage. // either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) { func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
return bc.stateCache.TrieDB().Node(hash) return bc.stateCache.TrieDB().Node(hash)
} }
// ContractCode retrieves a blob of data associated with a contract hash
// either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) {
return bc.stateCache.ContractCode(common.Hash{}, hash)
}
// ContractCodeWithPrefix retrieves a blob of data associated with a contract
// hash either from ephemeral in-memory cache, or from persistent storage.
//
// If the code doesn't exist in the in-memory cache, check the storage with
// new code scheme.
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
type codeReader interface {
ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error)
}
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash)
}
// Stop stops the blockchain service. If any imports are currently in progress // Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt. // it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() { func (bc *BlockChain) Stop() {
......
...@@ -36,6 +36,7 @@ import ( ...@@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
// So we can deterministically seed different blockchains // So we can deterministically seed different blockchains
...@@ -681,12 +682,12 @@ func TestFastVsFullChains(t *testing.T) { ...@@ -681,12 +682,12 @@ func TestFastVsFullChains(t *testing.T) {
} }
if fblock, arblock, anblock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash), ancient.GetBlockByHash(hash); fblock.Hash() != arblock.Hash() || anblock.Hash() != arblock.Hash() { if fblock, arblock, anblock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash), ancient.GetBlockByHash(hash); fblock.Hash() != arblock.Hash() || anblock.Hash() != arblock.Hash() {
t.Errorf("block #%d [%x]: block mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock, anblock, arblock) t.Errorf("block #%d [%x]: block mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock, anblock, arblock)
} else if types.DeriveSha(fblock.Transactions()) != types.DeriveSha(arblock.Transactions()) || types.DeriveSha(anblock.Transactions()) != types.DeriveSha(arblock.Transactions()) { } else if types.DeriveSha(fblock.Transactions(), new(trie.Trie)) != types.DeriveSha(arblock.Transactions(), new(trie.Trie)) || types.DeriveSha(anblock.Transactions(), new(trie.Trie)) != types.DeriveSha(arblock.Transactions(), new(trie.Trie)) {
t.Errorf("block #%d [%x]: transactions mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Transactions(), anblock.Transactions(), arblock.Transactions()) t.Errorf("block #%d [%x]: transactions mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Transactions(), anblock.Transactions(), arblock.Transactions())
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) { } else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) {
t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles()) t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles())
} }
if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) { if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts, new(trie.Trie)) != types.DeriveSha(areceipts, new(trie.Trie)) {
t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts) t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts)
} }
} }
......
...@@ -36,6 +36,7 @@ import ( ...@@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
) )
//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go //go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
...@@ -287,7 +288,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { ...@@ -287,7 +288,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
statedb.Commit(false) statedb.Commit(false)
statedb.Database().TrieDB().Commit(root, true, nil) statedb.Database().TrieDB().Commit(root, true, nil)
return types.NewBlock(head, nil, nil, nil) return types.NewBlock(head, nil, nil, nil, new(trie.Trie))
} }
// Commit writes the block and state of a genesis specification to the database. // Commit writes the block and state of a genesis specification to the database.
......
...@@ -84,7 +84,7 @@ func TestBodyStorage(t *testing.T) { ...@@ -84,7 +84,7 @@ func TestBodyStorage(t *testing.T) {
WriteBody(db, hash, 0, body) WriteBody(db, hash, 0, body)
if entry := ReadBody(db, hash, 0); entry == nil { if entry := ReadBody(db, hash, 0); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
} }
if entry := ReadBodyRLP(db, hash, 0); entry == nil { if entry := ReadBodyRLP(db, hash, 0); entry == nil {
...@@ -138,7 +138,7 @@ func TestBlockStorage(t *testing.T) { ...@@ -138,7 +138,7 @@ func TestBlockStorage(t *testing.T) {
} }
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil { if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { } else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(block.Transactions(), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body()) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
} }
// Delete the block and verify the execution // Delete the block and verify the execution
......
...@@ -18,6 +18,7 @@ package rawdb ...@@ -18,6 +18,7 @@ package rawdb
import ( import (
"bytes" "bytes"
"hash"
"math/big" "math/big"
"testing" "testing"
...@@ -26,8 +27,33 @@ import ( ...@@ -26,8 +27,33 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
) )
// 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: sha3.NewLegacyKeccak256()}
}
func (h *testHasher) Reset() {
h.hasher.Reset()
}
func (h *testHasher) Update(key, val []byte) {
h.hasher.Write(key)
h.hasher.Write(val)
}
func (h *testHasher) Hash() common.Hash {
return common.BytesToHash(h.hasher.Sum(nil))
}
// Tests that positional lookup metadata can be stored and retrieved. // Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) { func TestLookupStorage(t *testing.T) {
tests := []struct { tests := []struct {
...@@ -73,7 +99,7 @@ func TestLookupStorage(t *testing.T) { ...@@ -73,7 +99,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}) 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} txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newHasher())
// Check that no transactions entries are in a pristine database // Check that no transactions entries are in a pristine database
for i, tx := range txs { for i, tx := range txs {
......
...@@ -79,20 +79,3 @@ func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, cfg *params.Cha ...@@ -79,20 +79,3 @@ func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, cfg *params.Cha
log.Crit("Failed to store chain config", "err", err) log.Crit("Failed to store chain config", "err", err)
} }
} }
// ReadPreimage retrieves a single preimage of the provided hash.
func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(preimageKey(hash))
return data
}
// WritePreimages writes the provided set of preimages to the database.
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
for hash, preimage := range preimages {
if err := db.Put(preimageKey(hash), preimage); err != nil {
log.Crit("Failed to store trie preimage", "err", err)
}
}
preimageCounter.Inc(int64(len(preimages)))
preimageHitCounter.Inc(int64(len(preimages)))
}
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
package rawdb
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
// ReadPreimage retrieves a single preimage of the provided hash.
func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(preimageKey(hash))
return data
}
// WritePreimages writes the provided set of preimages to the database.
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
for hash, preimage := range preimages {
if err := db.Put(preimageKey(hash), preimage); err != nil {
log.Crit("Failed to store trie preimage", "err", err)
}
}
preimageCounter.Inc(int64(len(preimages)))
preimageHitCounter.Inc(int64(len(preimages)))
}
// ReadCode retrieves the contract code of the provided code hash.
func ReadCode(db ethdb.KeyValueReader, hash common.Hash) []byte {
// Try with the legacy code scheme first, if not then try with current
// scheme. Since most of the code will be found with legacy scheme.
//
// todo(rjl493456442) change the order when we forcibly upgrade the code
// scheme with snapshot.
data, _ := db.Get(hash[:])
if len(data) != 0 {
return data
}
return ReadCodeWithPrefix(db, hash)
}
// ReadCodeWithPrefix retrieves the contract code of the provided code hash.
// The main difference between this function and ReadCode is this function
// will only check the existence with latest scheme(with prefix).
func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(codeKey(hash))
return data
}
// WriteCode writes the provided contract code database.
func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
if err := db.Put(codeKey(hash), code); err != nil {
log.Crit("Failed to store contract code", "err", err)
}
}
// DeleteCode deletes the specified contract code from the database.
func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(codeKey(hash)); err != nil {
log.Crit("Failed to delete contract code", "err", err)
}
}
// ReadTrieNode retrieves the trie node of the provided hash.
func ReadTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(hash.Bytes())
return data
}
// WriteTrieNode writes the provided trie node database.
func WriteTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) {
if err := db.Put(hash.Bytes(), node); err != nil {
log.Crit("Failed to store trie node", "err", err)
}
}
// DeleteTrieNode deletes the specified trie node from the database.
func DeleteTrieNode(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(hash.Bytes()); err != nil {
log.Crit("Failed to delete trie node", "err", err)
}
}
...@@ -34,11 +34,11 @@ func TestChainIterator(t *testing.T) { ...@@ -34,11 +34,11 @@ func TestChainIterator(t *testing.T) {
var txs []*types.Transaction var txs []*types.Transaction
for i := uint64(0); i <= 10; i++ { for i := uint64(0); i <= 10; i++ {
if i == 0 { if i == 0 {
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil) // Empty genesis block block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil, newHasher()) // Empty genesis block
} else { } else {
tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
txs = append(txs, tx) txs = append(txs, tx)
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil) block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher())
} }
WriteBlock(chainDb, block) WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
......
...@@ -258,6 +258,7 @@ func InspectDatabase(db ethdb.Database) error { ...@@ -258,6 +258,7 @@ func InspectDatabase(db ethdb.Database) error {
numHashPairing common.StorageSize numHashPairing common.StorageSize
hashNumPairing common.StorageSize hashNumPairing common.StorageSize
trieSize common.StorageSize trieSize common.StorageSize
codeSize common.StorageSize
txlookupSize common.StorageSize txlookupSize common.StorageSize
accountSnapSize common.StorageSize accountSnapSize common.StorageSize
storageSnapSize common.StorageSize storageSnapSize common.StorageSize
...@@ -316,6 +317,8 @@ func InspectDatabase(db ethdb.Database) error { ...@@ -316,6 +317,8 @@ func InspectDatabase(db ethdb.Database) error {
chtTrieNodes += size chtTrieNodes += size
case bytes.HasPrefix(key, []byte("blt-")) && len(key) == 4+common.HashLength: case bytes.HasPrefix(key, []byte("blt-")) && len(key) == 4+common.HashLength:
bloomTrieNodes += size bloomTrieNodes += size
case bytes.HasPrefix(key, codePrefix) && len(key) == len(codePrefix)+common.HashLength:
codeSize += size
case len(key) == common.HashLength: case len(key) == common.HashLength:
trieSize += size trieSize += size
default: default:
...@@ -355,6 +358,7 @@ func InspectDatabase(db ethdb.Database) error { ...@@ -355,6 +358,7 @@ func InspectDatabase(db ethdb.Database) error {
{"Key-Value store", "Block hash->number", hashNumPairing.String()}, {"Key-Value store", "Block hash->number", hashNumPairing.String()},
{"Key-Value store", "Transaction index", txlookupSize.String()}, {"Key-Value store", "Transaction index", txlookupSize.String()},
{"Key-Value store", "Bloombit index", bloomBitsSize.String()}, {"Key-Value store", "Bloombit index", bloomBitsSize.String()},
{"Key-Value store", "Contract codes", codeSize.String()},
{"Key-Value store", "Trie nodes", trieSize.String()}, {"Key-Value store", "Trie nodes", trieSize.String()},
{"Key-Value store", "Trie preimages", preimageSize.String()}, {"Key-Value store", "Trie preimages", preimageSize.String()},
{"Key-Value store", "Account snapshot", accountSnapSize.String()}, {"Key-Value store", "Account snapshot", accountSnapSize.String()},
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
package rawdb package rawdb
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -69,6 +70,7 @@ var ( ...@@ -69,6 +70,7 @@ var (
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value
SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value
codePrefix = []byte("c") // codePrefix + code hash -> account code
preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
configPrefix = []byte("ethereum-config-") // config prefix for the db configPrefix = []byte("ethereum-config-") // config prefix for the db
...@@ -192,6 +194,20 @@ func preimageKey(hash common.Hash) []byte { ...@@ -192,6 +194,20 @@ func preimageKey(hash common.Hash) []byte {
return append(preimagePrefix, hash.Bytes()...) return append(preimagePrefix, hash.Bytes()...)
} }
// codeKey = codePrefix + hash
func codeKey(hash common.Hash) []byte {
return append(codePrefix, hash.Bytes()...)
}
// IsCodeKey reports whether the given byte slice is the key of contract code,
// if so return the raw code hash as well.
func IsCodeKey(key []byte) (bool, []byte) {
if bytes.HasPrefix(key, codePrefix) && len(key) == common.HashLength+len(codePrefix) {
return true, key[len(codePrefix):]
}
return false, nil
}
// configKey = configPrefix + hash // configKey = configPrefix + hash
func configKey(hash common.Hash) []byte { func configKey(hash common.Hash) []byte {
return append(configPrefix, hash.Bytes()...) return append(configPrefix, hash.Bytes()...)
......
...@@ -17,9 +17,12 @@ ...@@ -17,9 +17,12 @@
package state package state
import ( import (
"errors"
"fmt" "fmt"
"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
...@@ -28,6 +31,9 @@ import ( ...@@ -28,6 +31,9 @@ import (
const ( const (
// Number of codehash->size associations to keep. // Number of codehash->size associations to keep.
codeSizeCacheSize = 100000 codeSizeCacheSize = 100000
// Cache size granted for caching clean code.
codeCacheSize = 64 * 1024 * 1024
) )
// Database wraps access to tries and contract code. // Database wraps access to tries and contract code.
...@@ -111,12 +117,14 @@ func NewDatabaseWithCache(db ethdb.Database, cache int, journal string) Database ...@@ -111,12 +117,14 @@ func NewDatabaseWithCache(db ethdb.Database, cache int, journal string) Database
return &cachingDB{ return &cachingDB{
db: trie.NewDatabaseWithCache(db, cache, journal), db: trie.NewDatabaseWithCache(db, cache, journal),
codeSizeCache: csc, codeSizeCache: csc,
codeCache: fastcache.New(codeCacheSize),
} }
} }
type cachingDB struct { type cachingDB struct {
db *trie.Database db *trie.Database
codeSizeCache *lru.Cache codeSizeCache *lru.Cache
codeCache *fastcache.Cache
} }
// OpenTrie opens the main account trie at a specific root hash. // OpenTrie opens the main account trie at a specific root hash.
...@@ -141,11 +149,32 @@ func (db *cachingDB) CopyTrie(t Trie) Trie { ...@@ -141,11 +149,32 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
// ContractCode retrieves a particular contract's code. // ContractCode retrieves a particular contract's code.
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
code, err := db.db.Node(codeHash) if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
if err == nil { return code, nil
}
code := rawdb.ReadCode(db.db.DiskDB(), codeHash)
if len(code) > 0 {
db.codeCache.Set(codeHash.Bytes(), code)
db.codeSizeCache.Add(codeHash, len(code))
return code, nil
}
return nil, errors.New("not found")
}
// ContractCodeWithPrefix retrieves a particular contract's code. If the
// code can't be found in the cache, then check the existence with **new**
// db scheme.
func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) {
if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
return code, nil
}
code := rawdb.ReadCodeWithPrefix(db.db.DiskDB(), codeHash)
if len(code) > 0 {
db.codeCache.Set(codeHash.Bytes(), code)
db.codeSizeCache.Add(codeHash, len(code)) db.codeSizeCache.Add(codeHash, len(code))
return code, nil
} }
return code, err return nil, errors.New("not found")
} }
// ContractCodeSize retrieves a particular contracts code's size. // ContractCodeSize retrieves a particular contracts code's size.
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
func TestNodeIteratorCoverage(t *testing.T) { func TestNodeIteratorCoverage(t *testing.T) {
// Create some arbitrary test state to iterate // Create some arbitrary test state to iterate
db, root, _ := makeTestState() db, root, _ := makeTestState()
db.TrieDB().Commit(root, false, nil)
state, err := New(root, db, nil) state, err := New(root, db, nil)
if err != nil { if err != nil {
...@@ -42,7 +43,10 @@ func TestNodeIteratorCoverage(t *testing.T) { ...@@ -42,7 +43,10 @@ func TestNodeIteratorCoverage(t *testing.T) {
} }
// Cross check the iterated hashes and the database/nodepool content // Cross check the iterated hashes and the database/nodepool content
for hash := range hashes { for hash := range hashes {
if _, err := db.TrieDB().Node(hash); err != nil { if _, err = db.TrieDB().Node(hash); err != nil {
_, err = db.ContractCode(common.Hash{}, hash)
}
if err != nil {
t.Errorf("failed to retrieve reported node %x", hash) t.Errorf("failed to retrieve reported node %x", hash)
} }
} }
......
...@@ -25,6 +25,7 @@ import ( ...@@ -25,6 +25,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/state/snapshot"
"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/crypto"
...@@ -42,9 +43,6 @@ type revision struct { ...@@ -42,9 +43,6 @@ type revision struct {
var ( var (
// emptyRoot is the known root hash of an empty trie. // emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// emptyCode is the known hash of the empty EVM bytecode.
emptyCode = crypto.Keccak256Hash(nil)
) )
type proofList [][]byte type proofList [][]byte
...@@ -589,7 +587,10 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) ...@@ -589,7 +587,10 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct}) s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct})
} }
s.setStateObject(newobj) s.setStateObject(newobj)
return newobj, prev if prev != nil && !prev.deleted {
return newobj, prev
}
return newobj, nil
} }
// CreateAccount explicitly creates a state object. If a state object with the address // CreateAccount explicitly creates a state object. If a state object with the address
...@@ -816,11 +817,12 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { ...@@ -816,11 +817,12 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
s.IntermediateRoot(deleteEmptyObjects) s.IntermediateRoot(deleteEmptyObjects)
// Commit objects to the trie, measuring the elapsed time // Commit objects to the trie, measuring the elapsed time
codeWriter := s.db.TrieDB().DiskDB().NewBatch()
for addr := range s.stateObjectsDirty { for addr := range s.stateObjectsDirty {
if obj := s.stateObjects[addr]; !obj.deleted { if obj := s.stateObjects[addr]; !obj.deleted {
// Write any contract code associated with the state object // Write any contract code associated with the state object
if obj.code != nil && obj.dirtyCode { if obj.code != nil && obj.dirtyCode {
s.db.TrieDB().InsertBlob(common.BytesToHash(obj.CodeHash()), obj.code) rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code)
obj.dirtyCode = false obj.dirtyCode = false
} }
// Write any storage changes in the state object to its storage trie // Write any storage changes in the state object to its storage trie
...@@ -832,6 +834,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { ...@@ -832,6 +834,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
if len(s.stateObjectsDirty) > 0 { if len(s.stateObjectsDirty) > 0 {
s.stateObjectsDirty = make(map[common.Address]struct{}) s.stateObjectsDirty = make(map[common.Address]struct{})
} }
if codeWriter.ValueSize() > 0 {
if err := codeWriter.Write(); err != nil {
log.Crit("Failed to commit dirty codes", "error", err)
}
}
// Write the account trie changes, measuing the amount of wasted time // Write the account trie changes, measuing the amount of wasted time
var start time.Time var start time.Time
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
...@@ -847,10 +854,6 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { ...@@ -847,10 +854,6 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
if account.Root != emptyRoot { if account.Root != emptyRoot {
s.db.TrieDB().Reference(account.Root, parent) s.db.TrieDB().Reference(account.Root, parent)
} }
code := common.BytesToHash(account.CodeHash)
if code != emptyCode {
s.db.TrieDB().Reference(code, parent)
}
return nil return nil
}) })
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
......
...@@ -34,7 +34,7 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.S ...@@ -34,7 +34,7 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.S
return err return err
} }
syncer.AddSubTrie(obj.Root, 64, parent, nil) syncer.AddSubTrie(obj.Root, 64, parent, nil)
syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent) syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), 64, parent)
return nil return nil
} }
syncer = trie.NewSync(root, database, callback, bloom) syncer = trie.NewSync(root, database, callback, bloom)
......
...@@ -133,13 +133,17 @@ func TestEmptyStateSync(t *testing.T) { ...@@ -133,13 +133,17 @@ func TestEmptyStateSync(t *testing.T) {
// Tests that given a root hash, a state can sync iteratively on a single thread, // Tests that given a root hash, a state can sync iteratively on a single thread,
// requesting retrieval tasks and returning all of them in one go. // requesting retrieval tasks and returning all of them in one go.
func TestIterativeStateSyncIndividual(t *testing.T) { testIterativeStateSync(t, 1) } func TestIterativeStateSyncIndividual(t *testing.T) { testIterativeStateSync(t, 1, false) }
func TestIterativeStateSyncBatched(t *testing.T) { testIterativeStateSync(t, 100) } func TestIterativeStateSyncBatched(t *testing.T) { testIterativeStateSync(t, 100, false) }
func TestIterativeStateSyncIndividualFromDisk(t *testing.T) { testIterativeStateSync(t, 1, true) }
func TestIterativeStateSyncBatchedFromDisk(t *testing.T) { testIterativeStateSync(t, 100, true) }
func testIterativeStateSync(t *testing.T, count int) { func testIterativeStateSync(t *testing.T, count int, commit bool) {
// Create a random state to copy // Create a random state to copy
srcDb, srcRoot, srcAccounts := makeTestState() srcDb, srcRoot, srcAccounts := makeTestState()
if commit {
srcDb.TrieDB().Commit(srcRoot, false, nil)
}
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase() dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb)) sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
...@@ -149,13 +153,18 @@ func testIterativeStateSync(t *testing.T, count int) { ...@@ -149,13 +153,18 @@ func testIterativeStateSync(t *testing.T, count int) {
results := make([]trie.SyncResult, len(queue)) results := make([]trie.SyncResult, len(queue))
for i, hash := range queue { for i, hash := range queue {
data, err := srcDb.TrieDB().Node(hash) data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results[i] = trie.SyncResult{Hash: hash, Data: data} results[i] = trie.SyncResult{Hash: hash, Data: data}
} }
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := dstDb.NewBatch() batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -184,13 +193,18 @@ func TestIterativeDelayedStateSync(t *testing.T) { ...@@ -184,13 +193,18 @@ func TestIterativeDelayedStateSync(t *testing.T) {
results := make([]trie.SyncResult, len(queue)/2+1) results := make([]trie.SyncResult, len(queue)/2+1)
for i, hash := range queue[:len(results)] { for i, hash := range queue[:len(results)] {
data, err := srcDb.TrieDB().Node(hash) data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results[i] = trie.SyncResult{Hash: hash, Data: data} results[i] = trie.SyncResult{Hash: hash, Data: data}
} }
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := dstDb.NewBatch() batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -226,14 +240,19 @@ func testIterativeRandomStateSync(t *testing.T, count int) { ...@@ -226,14 +240,19 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
results := make([]trie.SyncResult, 0, len(queue)) results := make([]trie.SyncResult, 0, len(queue))
for hash := range queue { for hash := range queue {
data, err := srcDb.TrieDB().Node(hash) data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results = append(results, trie.SyncResult{Hash: hash, Data: data}) results = append(results, trie.SyncResult{Hash: hash, Data: data})
} }
// Feed the retrieved results back and queue new tasks // Feed the retrieved results back and queue new tasks
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := dstDb.NewBatch() batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -270,6 +289,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) { ...@@ -270,6 +289,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
delete(queue, hash) delete(queue, hash)
data, err := srcDb.TrieDB().Node(hash) data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
...@@ -280,8 +302,10 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) { ...@@ -280,8 +302,10 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
} }
} }
// Feed the retrieved results back and queue new tasks // Feed the retrieved results back and queue new tasks
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := dstDb.NewBatch() batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -302,6 +326,15 @@ func TestIncompleteStateSync(t *testing.T) { ...@@ -302,6 +326,15 @@ func TestIncompleteStateSync(t *testing.T) {
// Create a random state to copy // Create a random state to copy
srcDb, srcRoot, srcAccounts := makeTestState() srcDb, srcRoot, srcAccounts := makeTestState()
// isCode reports whether the hash is contract code hash.
isCode := func(hash common.Hash) bool {
for _, acc := range srcAccounts {
if hash == crypto.Keccak256Hash(acc.code) {
return true
}
}
return false
}
checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot) checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler
...@@ -315,14 +348,19 @@ func TestIncompleteStateSync(t *testing.T) { ...@@ -315,14 +348,19 @@ func TestIncompleteStateSync(t *testing.T) {
results := make([]trie.SyncResult, len(queue)) results := make([]trie.SyncResult, len(queue))
for i, hash := range queue { for i, hash := range queue {
data, err := srcDb.TrieDB().Node(hash) data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results[i] = trie.SyncResult{Hash: hash, Data: data} results[i] = trie.SyncResult{Hash: hash, Data: data}
} }
// Process each of the state nodes // Process each of the state nodes
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := dstDb.NewBatch() batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -333,12 +371,9 @@ func TestIncompleteStateSync(t *testing.T) { ...@@ -333,12 +371,9 @@ func TestIncompleteStateSync(t *testing.T) {
added = append(added, result.Hash) added = append(added, result.Hash)
} }
// Check that all known sub-tries added so far are complete or missing entirely. // Check that all known sub-tries added so far are complete or missing entirely.
checkSubtries:
for _, hash := range added { for _, hash := range added {
for _, acc := range srcAccounts { if isCode(hash) {
if hash == crypto.Keccak256Hash(acc.code) { continue
continue checkSubtries // skip trie check of code nodes.
}
} }
// Can't use checkStateConsistency here because subtrie keys may have odd // Can't use checkStateConsistency here because subtrie keys may have odd
// length and crash in LeafKey. // length and crash in LeafKey.
...@@ -351,13 +386,25 @@ func TestIncompleteStateSync(t *testing.T) { ...@@ -351,13 +386,25 @@ func TestIncompleteStateSync(t *testing.T) {
} }
// Sanity check that removing any node from the database is detected // Sanity check that removing any node from the database is detected
for _, node := range added[1:] { for _, node := range added[1:] {
key := node.Bytes() var (
value, _ := dstDb.Get(key) key = node.Bytes()
code = isCode(node)
dstDb.Delete(key) val []byte
)
if code {
val = rawdb.ReadCode(dstDb, node)
rawdb.DeleteCode(dstDb, node)
} else {
val = rawdb.ReadTrieNode(dstDb, node)
rawdb.DeleteTrieNode(dstDb, node)
}
if err := checkStateConsistency(dstDb, added[0]); err == nil { if err := checkStateConsistency(dstDb, added[0]); err == nil {
t.Fatalf("trie inconsistency not caught, missing: %x", key) t.Fatalf("trie inconsistency not caught, missing: %x", key)
} }
dstDb.Put(key, value) if code {
rawdb.WriteCode(dstDb, node, val)
} else {
rawdb.WriteTrieNode(dstDb, node, val)
}
} }
} }
...@@ -34,6 +34,7 @@ import ( ...@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
// testTxPoolConfig is a transaction pool configuration without stateful disk // testTxPoolConfig is a transaction pool configuration without stateful disk
...@@ -54,7 +55,7 @@ type testBlockChain struct { ...@@ -54,7 +55,7 @@ type testBlockChain struct {
func (bc *testBlockChain) CurrentBlock() *types.Block { func (bc *testBlockChain) CurrentBlock() *types.Block {
return types.NewBlock(&types.Header{ return types.NewBlock(&types.Header{
GasLimit: bc.gasLimit, GasLimit: bc.gasLimit,
}, nil, nil, nil) }, nil, nil, nil, new(trie.Trie))
} }
func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
......
...@@ -35,7 +35,7 @@ import ( ...@@ -35,7 +35,7 @@ import (
) )
var ( var (
EmptyRootHash = DeriveSha(Transactions{}) EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
EmptyUncleHash = rlpHash([]*Header(nil)) EmptyUncleHash = rlpHash([]*Header(nil))
) )
...@@ -221,14 +221,14 @@ type storageblock struct { ...@@ -221,14 +221,14 @@ type storageblock struct {
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header // The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, uncles // are ignored and set to values derived from the given txs, uncles
// and receipts. // and receipts.
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher Hasher) *Block {
b := &Block{header: CopyHeader(header), td: new(big.Int)} b := &Block{header: CopyHeader(header), td: new(big.Int)}
// TODO: panic if len(txs) != len(receipts) // TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 { if len(txs) == 0 {
b.header.TxHash = EmptyRootHash b.header.TxHash = EmptyRootHash
} else { } else {
b.header.TxHash = DeriveSha(Transactions(txs)) b.header.TxHash = DeriveSha(Transactions(txs), hasher)
b.transactions = make(Transactions, len(txs)) b.transactions = make(Transactions, len(txs))
copy(b.transactions, txs) copy(b.transactions, txs)
} }
...@@ -236,7 +236,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []* ...@@ -236,7 +236,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
if len(receipts) == 0 { if len(receipts) == 0 {
b.header.ReceiptHash = EmptyRootHash b.header.ReceiptHash = EmptyRootHash
} else { } else {
b.header.ReceiptHash = DeriveSha(Receipts(receipts)) b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
b.header.Bloom = CreateBloom(receipts) b.header.Bloom = CreateBloom(receipts)
} }
......
...@@ -18,6 +18,7 @@ package types ...@@ -18,6 +18,7 @@ package types
import ( import (
"bytes" "bytes"
"hash"
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
...@@ -27,6 +28,7 @@ import ( ...@@ -27,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
) )
// from bcValidBlockTest.json, "SimpleTx" // from bcValidBlockTest.json, "SimpleTx"
...@@ -90,6 +92,30 @@ func BenchmarkEncodeBlock(b *testing.B) { ...@@ -90,6 +92,30 @@ 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: sha3.NewLegacyKeccak256()}
}
func (h *testHasher) Reset() {
h.hasher.Reset()
}
func (h *testHasher) Update(key, val []byte) {
h.hasher.Write(key)
h.hasher.Write(val)
}
func (h *testHasher) Hash() common.Hash {
return common.BytesToHash(h.hasher.Sum(nil))
}
func makeBenchBlock() *Block { func makeBenchBlock() *Block {
var ( var (
key, _ = crypto.GenerateKey() key, _ = crypto.GenerateKey()
...@@ -128,5 +154,5 @@ func makeBenchBlock() *Block { ...@@ -128,5 +154,5 @@ func makeBenchBlock() *Block {
Extra: []byte("benchmark uncle"), Extra: []byte("benchmark uncle"),
} }
} }
return NewBlock(header, txs, uncles, receipts) return NewBlock(header, txs, uncles, receipts, newHasher())
} }
...@@ -21,21 +21,28 @@ import ( ...@@ -21,21 +21,28 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
) )
// DerivableList is the interface which can derive the hash.
type DerivableList interface { type DerivableList interface {
Len() int Len() int
GetRlp(i int) []byte GetRlp(i int) []byte
} }
func DeriveSha(list DerivableList) common.Hash { // Hasher is the tool used to calculate the hash of derivable list.
type Hasher interface {
Reset()
Update([]byte, []byte)
Hash() common.Hash
}
func DeriveSha(list DerivableList, hasher Hasher) common.Hash {
hasher.Reset()
keybuf := new(bytes.Buffer) keybuf := new(bytes.Buffer)
trie := new(trie.Trie)
for i := 0; i < list.Len(); i++ { for i := 0; i < list.Len(); i++ {
keybuf.Reset() keybuf.Reset()
rlp.Encode(keybuf, uint(i)) rlp.Encode(keybuf, uint(i))
trie.Update(keybuf.Bytes(), list.GetRlp(i)) hasher.Update(keybuf.Bytes(), list.GetRlp(i))
} }
return trie.Hash() return hasher.Hash()
} }
...@@ -109,7 +109,7 @@ type Downloader struct { ...@@ -109,7 +109,7 @@ type Downloader struct {
peers *peerSet // Set of active peers from which download can proceed peers *peerSet // Set of active peers from which download can proceed
stateDB ethdb.Database // Database to state sync into (and deduplicate via) stateDB ethdb.Database // Database to state sync into (and deduplicate via)
stateBloom *trie.SyncBloom // Bloom filter for fast trie node existence checks stateBloom *trie.SyncBloom // Bloom filter for fast trie node and contract code existence checks
// Statistics // Statistics
syncStatsChainOrigin uint64 // Origin block number where syncing started at syncStatsChainOrigin uint64 // Origin block number where syncing started at
......
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/trie"
) )
const ( const (
...@@ -771,7 +772,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLi ...@@ -771,7 +772,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLi
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
validate := func(index int, header *types.Header) error { validate := func(index int, header *types.Header) error {
if types.DeriveSha(types.Transactions(txLists[index])) != header.TxHash { if types.DeriveSha(types.Transactions(txLists[index]), new(trie.Trie)) != header.TxHash {
return errInvalidBody return errInvalidBody
} }
if types.CalcUncleHash(uncleLists[index]) != header.UncleHash { if types.CalcUncleHash(uncleLists[index]) != header.UncleHash {
...@@ -796,7 +797,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, ...@@ -796,7 +797,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int,
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
validate := func(index int, header *types.Header) error { validate := func(index int, header *types.Header) error {
if types.DeriveSha(types.Receipts(receiptList[index])) != header.ReceiptHash { if types.DeriveSha(types.Receipts(receiptList[index]), new(trie.Trie)) != header.ReceiptHash {
return errInvalidReceipt return errInvalidReceipt
} }
return nil return nil
......
...@@ -474,7 +474,7 @@ func (s *stateSync) process(req *stateReq) (int, error) { ...@@ -474,7 +474,7 @@ func (s *stateSync) process(req *stateReq) (int, error) {
// Iterate over all the delivered data and inject one-by-one into the trie // Iterate over all the delivered data and inject one-by-one into the trie
for _, blob := range req.response { for _, blob := range req.response {
_, hash, err := s.processNodeData(blob) hash, err := s.processNodeData(blob)
switch err { switch err {
case nil: case nil:
s.numUncommitted++ s.numUncommitted++
...@@ -512,13 +512,13 @@ func (s *stateSync) process(req *stateReq) (int, error) { ...@@ -512,13 +512,13 @@ func (s *stateSync) process(req *stateReq) (int, error) {
// processNodeData tries to inject a trie node data blob delivered from a remote // processNodeData tries to inject a trie node data blob delivered from a remote
// peer into the state trie, returning whether anything useful was written or any // peer into the state trie, returning whether anything useful was written or any
// error occurred. // error occurred.
func (s *stateSync) processNodeData(blob []byte) (bool, common.Hash, error) { func (s *stateSync) processNodeData(blob []byte) (common.Hash, error) {
res := trie.SyncResult{Data: blob} res := trie.SyncResult{Data: blob}
s.keccak.Reset() s.keccak.Reset()
s.keccak.Write(blob) s.keccak.Write(blob)
s.keccak.Sum(res.Hash[:0]) s.keccak.Sum(res.Hash[:0])
committed, _, err := s.sched.Process([]trie.SyncResult{res}) err := s.sched.Process(res)
return committed, res.Hash, err return res.Hash, err
} }
// updateStats bumps the various state sync progress counters and displays a log // updateStats bumps the various state sync progress counters and displays a log
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/trie"
) )
const ( const (
...@@ -540,7 +541,7 @@ func (f *BlockFetcher) loop() { ...@@ -540,7 +541,7 @@ func (f *BlockFetcher) loop() {
announce.time = task.time announce.time = task.time
// If the block is empty (header only), short circuit into the final import queue // If the block is empty (header only), short circuit into the final import queue
if header.TxHash == types.DeriveSha(types.Transactions{}) && header.UncleHash == types.CalcUncleHash([]*types.Header{}) { if header.TxHash == types.EmptyRootHash && header.UncleHash == types.EmptyUncleHash {
log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash()) log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash())
block := types.NewBlockWithHeader(header) block := types.NewBlockWithHeader(header)
...@@ -619,7 +620,7 @@ func (f *BlockFetcher) loop() { ...@@ -619,7 +620,7 @@ func (f *BlockFetcher) loop() {
continue continue
} }
if txnHash == (common.Hash{}) { if txnHash == (common.Hash{}) {
txnHash = types.DeriveSha(types.Transactions(task.transactions[i])) txnHash = types.DeriveSha(types.Transactions(task.transactions[i]), new(trie.Trie))
} }
if txnHash != announce.header.TxHash { if txnHash != announce.header.TxHash {
continue continue
......
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
"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/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
var ( var (
...@@ -38,7 +39,7 @@ var ( ...@@ -38,7 +39,7 @@ var (
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testAddress = crypto.PubkeyToAddress(testKey.PublicKey) testAddress = crypto.PubkeyToAddress(testKey.PublicKey)
genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000)) genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit}, nil, nil, nil) unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit}, nil, nil, nil, new(trie.Trie))
) )
// makeChain creates a chain of n blocks starting at and including parent. // makeChain creates a chain of n blocks starting at and including parent.
......
...@@ -608,7 +608,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { ...@@ -608,7 +608,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested state entry, stopping if enough was found // Retrieve the requested state entry, stopping if enough was found
if entry, err := pm.blockchain.TrieNode(hash); err == nil { // todo now the code and trienode is mixed in the protocol level,
// separate these two types.
entry, err := pm.blockchain.TrieNode(hash)
if len(entry) == 0 || err != nil {
// Read the contract code with prefix only to save unnecessary lookups.
entry, err = pm.blockchain.ContractCodeWithPrefix(hash)
}
if err == nil && len(entry) > 0 {
data = append(data, entry) data = append(data, entry)
bytes += len(entry) bytes += len(entry)
} }
...@@ -703,7 +710,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { ...@@ -703,7 +710,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
log.Warn("Propagated block has invalid uncles", "have", hash, "exp", request.Block.UncleHash()) log.Warn("Propagated block has invalid uncles", "have", hash, "exp", request.Block.UncleHash())
break // TODO(karalabe): return error eventually, but wait a few releases break // TODO(karalabe): return error eventually, but wait a few releases
} }
if hash := types.DeriveSha(request.Block.Transactions()); hash != request.Block.TxHash() { if hash := types.DeriveSha(request.Block.Transactions(), new(trie.Trie)); hash != request.Block.TxHash() {
log.Warn("Propagated block has invalid body", "have", hash, "exp", request.Block.TxHash()) log.Warn("Propagated block has invalid body", "have", hash, "exp", request.Block.TxHash())
break // TODO(karalabe): return error eventually, but wait a few releases break // TODO(karalabe): return error eventually, but wait a few releases
} }
......
...@@ -116,7 +116,7 @@ func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error { ...@@ -116,7 +116,7 @@ func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error {
if r.Header == nil { if r.Header == nil {
return errHeaderUnavailable return errHeaderUnavailable
} }
if r.Header.TxHash != types.DeriveSha(types.Transactions(body.Transactions)) { if r.Header.TxHash != types.DeriveSha(types.Transactions(body.Transactions), new(trie.Trie)) {
return errTxHashMismatch return errTxHashMismatch
} }
if r.Header.UncleHash != types.CalcUncleHash(body.Uncles) { if r.Header.UncleHash != types.CalcUncleHash(body.Uncles) {
...@@ -174,7 +174,7 @@ func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { ...@@ -174,7 +174,7 @@ func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error {
if r.Header == nil { if r.Header == nil {
return errHeaderUnavailable return errHeaderUnavailable
} }
if r.Header.ReceiptHash != types.DeriveSha(receipt) { if r.Header.ReceiptHash != types.DeriveSha(receipt, new(trie.Trie)) {
return errReceiptHashMismatch return errReceiptHashMismatch
} }
// Validations passed, store and return // Validations passed, store and return
......
...@@ -489,7 +489,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error { ...@@ -489,7 +489,7 @@ func (h *serverHandler) handleMsg(p *clientPeer, wg *sync.WaitGroup) error {
p.bumpInvalid() p.bumpInvalid()
continue continue
} }
code, err := triedb.Node(common.BytesToHash(account.CodeHash)) code, err := h.blockchain.StateCache().ContractCode(common.BytesToHash(request.AccKey), common.BytesToHash(account.CodeHash))
if err != nil { if err != nil {
p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err) p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err)
continue continue
......
...@@ -101,7 +101,7 @@ type CodeRequest struct { ...@@ -101,7 +101,7 @@ type CodeRequest struct {
// StoreResult stores the retrieved data in local database // StoreResult stores the retrieved data in local database
func (req *CodeRequest) StoreResult(db ethdb.Database) { func (req *CodeRequest) StoreResult(db ethdb.Database) {
db.Put(req.Hash[:], req.Data) rawdb.WriteCode(db, req.Hash, req.Data)
} }
// BlockRequest is the ODR request type for retrieving block bodies // BlockRequest is the ODR request type for retrieving block bodies
......
...@@ -87,7 +87,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error { ...@@ -87,7 +87,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
t.Prove(req.Key, 0, nodes) t.Prove(req.Key, 0, nodes)
req.Proof = nodes req.Proof = nodes
case *CodeRequest: case *CodeRequest:
req.Data, _ = odr.sdb.Get(req.Hash[:]) req.Data = rawdb.ReadCode(odr.sdb, req.Hash)
} }
req.StoreResult(odr.ldb) req.StoreResult(odr.ldb)
return nil return nil
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"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/crypto"
...@@ -70,7 +71,8 @@ func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, err ...@@ -70,7 +71,8 @@ func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, err
if codeHash == sha3Nil { if codeHash == sha3Nil {
return nil, nil return nil, nil
} }
if code, err := db.backend.Database().Get(codeHash[:]); err == nil { code := rawdb.ReadCode(db.backend.Database(), codeHash)
if len(code) != 0 {
return code, nil return code, nil
} }
id := *db.id id := *db.id
......
...@@ -34,6 +34,7 @@ import ( ...@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
const ( const (
...@@ -711,6 +712,7 @@ func (w *worker) updateSnapshot() { ...@@ -711,6 +712,7 @@ func (w *worker) updateSnapshot() {
w.current.txs, w.current.txs,
uncles, uncles,
w.current.receipts, w.current.receipts,
new(trie.Trie),
) )
w.snapshotState = w.current.state.Copy() w.snapshotState = w.current.state.Copy()
......
This diff is collapsed.
...@@ -130,8 +130,7 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte { ...@@ -130,8 +130,7 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte {
if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { if key, ok := t.getSecKeyCache()[string(shaKey)]; ok {
return key return key
} }
key, _ := t.trie.db.preimage(common.BytesToHash(shaKey)) return t.trie.db.preimage(common.BytesToHash(shaKey))
return key
} }
// Commit writes all nodes and the secure hash pre-images to the trie's database. // Commit writes all nodes and the secure hash pre-images to the trie's database.
......
This diff is collapsed.
...@@ -25,6 +25,7 @@ import ( ...@@ -25,6 +25,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
...@@ -41,8 +42,8 @@ var ( ...@@ -41,8 +42,8 @@ var (
) )
// syncBloomHasher is a wrapper around a byte blob to satisfy the interface API // syncBloomHasher is a wrapper around a byte blob to satisfy the interface API
// requirements of the bloom library used. It's used to convert a trie hash into // requirements of the bloom library used. It's used to convert a trie hash or
// a 64 bit mini hash. // contract code hash into a 64 bit mini hash.
type syncBloomHasher []byte type syncBloomHasher []byte
func (f syncBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") } func (f syncBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
...@@ -53,9 +54,9 @@ func (f syncBloomHasher) Size() int { return 8 } ...@@ -53,9 +54,9 @@ func (f syncBloomHasher) Size() int { return 8 }
func (f syncBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) } func (f syncBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) }
// SyncBloom is a bloom filter used during fast sync to quickly decide if a trie // SyncBloom is a bloom filter used during fast sync to quickly decide if a trie
// node already exists on disk or not. It self populates from the provided disk // node or contract code already exists on disk or not. It self populates from the
// database on creation in a background thread and will only start returning live // provided disk database on creation in a background thread and will only start
// results once that's finished. // returning live results once that's finished.
type SyncBloom struct { type SyncBloom struct {
bloom *bloomfilter.Filter bloom *bloomfilter.Filter
inited uint32 inited uint32
...@@ -107,10 +108,16 @@ func (b *SyncBloom) init(database ethdb.Iteratee) { ...@@ -107,10 +108,16 @@ func (b *SyncBloom) init(database ethdb.Iteratee) {
) )
for it.Next() && atomic.LoadUint32(&b.closed) == 0 { for it.Next() && atomic.LoadUint32(&b.closed) == 0 {
// If the database entry is a trie node, add it to the bloom // If the database entry is a trie node, add it to the bloom
if key := it.Key(); len(key) == common.HashLength { key := it.Key()
if len(key) == common.HashLength {
b.bloom.Add(syncBloomHasher(key)) b.bloom.Add(syncBloomHasher(key))
bloomLoadMeter.Mark(1) bloomLoadMeter.Mark(1)
} }
// If the database entry is a contract code, add it to the bloom
if ok, hash := rawdb.IsCodeKey(key); ok {
b.bloom.Add(syncBloomHasher(hash))
bloomLoadMeter.Mark(1)
}
// If enough time elapsed since the last iterator swap, restart // If enough time elapsed since the last iterator swap, restart
if time.Since(swap) > 8*time.Second { if time.Since(swap) > 8*time.Second {
key := common.CopyBytes(it.Key()) key := common.CopyBytes(it.Key())
......
...@@ -124,8 +124,10 @@ func testIterativeSync(t *testing.T, count int) { ...@@ -124,8 +124,10 @@ func testIterativeSync(t *testing.T, count int) {
} }
results[i] = SyncResult{hash, data} results[i] = SyncResult{hash, data}
} }
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := diskdb.NewBatch() batch := diskdb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -160,8 +162,10 @@ func TestIterativeDelayedSync(t *testing.T) { ...@@ -160,8 +162,10 @@ func TestIterativeDelayedSync(t *testing.T) {
} }
results[i] = SyncResult{hash, data} results[i] = SyncResult{hash, data}
} }
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := diskdb.NewBatch() batch := diskdb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -204,8 +208,10 @@ func testIterativeRandomSync(t *testing.T, count int) { ...@@ -204,8 +208,10 @@ func testIterativeRandomSync(t *testing.T, count int) {
results = append(results, SyncResult{hash, data}) results = append(results, SyncResult{hash, data})
} }
// Feed the retrieved results back and queue new tasks // Feed the retrieved results back and queue new tasks
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := diskdb.NewBatch() batch := diskdb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -251,8 +257,10 @@ func TestIterativeRandomDelayedSync(t *testing.T) { ...@@ -251,8 +257,10 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
} }
} }
// Feed the retrieved results back and queue new tasks // Feed the retrieved results back and queue new tasks
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := diskdb.NewBatch() batch := diskdb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -298,8 +306,10 @@ func TestDuplicateAvoidanceSync(t *testing.T) { ...@@ -298,8 +306,10 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
results[i] = SyncResult{hash, data} results[i] = SyncResult{hash, data}
} }
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := diskdb.NewBatch() batch := diskdb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
...@@ -336,8 +346,10 @@ func TestIncompleteSync(t *testing.T) { ...@@ -336,8 +346,10 @@ func TestIncompleteSync(t *testing.T) {
results[i] = SyncResult{hash, data} results[i] = SyncResult{hash, data}
} }
// Process each of the trie nodes // Process each of the trie nodes
if _, index, err := sched.Process(results); err != nil { for _, result := range results {
t.Fatalf("failed to process result #%d: %v", index, err) if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
} }
batch := diskdb.NewBatch() batch := diskdb.NewBatch()
if err := sched.Commit(batch); err != nil { if err := sched.Commit(batch); err != nil {
......
...@@ -473,3 +473,9 @@ func (t *Trie) hashRoot(db *Database) (node, node, error) { ...@@ -473,3 +473,9 @@ func (t *Trie) hashRoot(db *Database) (node, node, error) {
t.unhashed = 0 t.unhashed = 0
return hashed, cached, nil return hashed, cached, nil
} }
// Reset drops the referenced root node and cleans all internal state.
func (t *Trie) Reset() {
t.root = nil
t.unhashed = 0
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment