core, trie: store bytecodes under a separate database prefix

This commit is contained in:
Péter Szilágyi 2019-02-08 14:05:10 +02:00
parent fcf2b3d88e
commit 672d3afd2b
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
7 changed files with 96 additions and 39 deletions

View file

@ -694,7 +694,15 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
// 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 (or code hash)
// 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(common.Hash{}, hash) // TODO(karalabe): make this work again // Attempt to satisfy this request with a trie node
if blob, err := bc.stateCache.TrieDB().Node(hash); blob != nil && err == nil {
return blob, nil
}
// Trie node not found, it may be a bytecode
if blob := rawdb.ReadCode(bc.db, hash); blob != nil {
return blob, nil
}
return nil, errors.New("not found")
} }
// Stop stops the blockchain service. If any imports are currently in progress // Stop stops the blockchain service. If any imports are currently in progress

View file

@ -0,0 +1,42 @@
// Copyright 2019 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/log"
)
// ReadCode retrieves the bytecode associated with a given hash.
func ReadCode(db DatabaseReader, hash common.Hash) []byte {
code, _ := db.Get(codeKey(hash))
return code
}
// WriteCode stores the bytecode associated with a given hash.
func WriteCode(db DatabaseWriter, hash common.Hash, code []byte) {
if err := db.Put(codeKey(hash), code); err != nil {
log.Crit("Failed to store bytecode", "err", err)
}
}
// DeleteCode removes the bytecode associated with a given hash.
func DeleteCode(db DatabaseDeleter, hash common.Hash) {
if err := db.Delete(codeKey(hash)); err != nil {
log.Crit("Failed to delete bytecode", "err", err)
}
}

View file

@ -53,6 +53,8 @@ var (
txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
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
codePrefix = []byte("c") // codePrefix + hash -> bytecode
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
@ -123,6 +125,11 @@ func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte {
return key return key
} }
// codeKey = codePrefix + hash
func codeKey(hash common.Hash) []byte {
return append(codePrefix, hash.Bytes()...)
}
// preimageKey = preimagePrefix + hash // preimageKey = preimagePrefix + hash
func preimageKey(hash common.Hash) []byte { func preimageKey(hash common.Hash) []byte {
return append(preimagePrefix, hash.Bytes()...) return append(preimagePrefix, hash.Bytes()...)

View file

@ -17,10 +17,12 @@
package state package state
import ( import (
"errors"
"fmt" "fmt"
"sync" "sync"
"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"
@ -144,11 +146,11 @@ 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(common.Hash{}, codeHash) if code := rawdb.ReadCode(db.db.DiskDB().(ethdb.Database), codeHash); code != nil {
if err == nil {
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.

View file

@ -24,6 +24,7 @@ import (
"sort" "sort"
"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/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/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -636,7 +637,8 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
case isDirty: case isDirty:
// Write any contract code associated with the state object // Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode { if stateObject.code != nil && stateObject.dirtyCode {
s.db.TrieDB().DiskDB().(ethdb.Database).Put(stateObject.CodeHash(), stateObject.code) rawdb.WriteCode(s.db.TrieDB().DiskDB().(ethdb.Database), common.BytesToHash(stateObject.CodeHash()), stateObject.code)
//s.db.TrieDB().DiskDB().(ethdb.Database).Put(stateObject.CodeHash(), stateObject.code)
stateObject.dirtyCode = false stateObject.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.

View file

@ -631,7 +631,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err != nil { if err != nil {
continue continue
} }
code, _ := statedb.Database().TrieDB().Node(common.Hash{}, common.BytesToHash(account.CodeHash)) // TODO(karalabe): make this work again code := rawdb.ReadCode(pm.chainDb, common.BytesToHash(account.CodeHash))
data = append(data, code) data = append(data, code)
if bytes += len(code); bytes >= softResponseLimit { if bytes += len(code); bytes >= softResponseLimit {

View file

@ -74,7 +74,7 @@ func makeNodeKey(owner common.Hash, hash common.Hash) string {
if owner == (common.Hash{}) { if owner == (common.Hash{}) {
return string(hash[:]) return string(hash[:])
} }
return string(append(owner[:], hash[:]...)) return string(append(hash[:], owner[:]...))
} }
// splitNodeKey returns the composing hashes of a trie node key. // splitNodeKey returns the composing hashes of a trie node key.
@ -87,7 +87,7 @@ func splitNodeKey(key string) (common.Hash, common.Hash) {
return common.Hash{}, common.BytesToHash([]byte(key)) return common.Hash{}, common.BytesToHash([]byte(key))
case 2 * common.HashLength: case 2 * common.HashLength:
return common.BytesToHash([]byte(key[:common.HashLength])), common.BytesToHash([]byte(key[common.HashLength:])) return common.BytesToHash([]byte(key[common.HashLength:])), common.BytesToHash([]byte(key[:common.HashLength]))
default: default:
panic(fmt.Sprintf("invalid node key: %s", key)) panic(fmt.Sprintf("invalid node key: %s", key))
@ -432,18 +432,6 @@ func (db *Database) DiskDB() DatabaseReader {
return db.diskdb return db.diskdb
} }
// InsertBlob writes a new reference tracked blob to the memory database if it's
// yet unknown. This method should only be used for non-trie nodes that require
// reference counting, since trie nodes are garbage collected directly through
// their embedded children.
func (db *Database) InsertBlob(owner common.Hash, hash common.Hash, blob []byte) {
db.lock.Lock()
defer db.lock.Unlock()
db.DiskDB().(ethdb.Database).Put([]byte(makeNodeKey(owner, hash)), blob)
//db.insert(owner, hash, blob, rawNode(blob))
}
// insert inserts a collapsed trie node into the memory database. This method is // insert inserts a collapsed trie node into the memory database. This method is
// a more generic version of InsertBlob, supporting both raw blob insertions as // a more generic version of InsertBlob, supporting both raw blob insertions as
// well ex trie node insertions. The blob must always be specified to allow proper // well ex trie node insertions. The blob must always be specified to allow proper
@ -526,9 +514,7 @@ func (db *Database) node(owner common.Hash, hash common.Hash, cachegen uint16) n
// Node retrieves an encoded cached trie node from memory. If it cannot be found // Node retrieves an encoded cached trie node from memory. If it cannot be found
// cached, the method queries the persistent database for the content. // cached, the method queries the persistent database for the content.
func (db *Database) Node(owner common.Hash, hash common.Hash) ([]byte, error) { func (db *Database) Node(hash common.Hash) ([]byte, error) {
key := makeNodeKey(owner, hash)
// Retrieve the node from the clean cache if available // Retrieve the node from the clean cache if available
if db.cleans != nil { if db.cleans != nil {
if enc, err := db.cleans.Get(string(hash[:])); err == nil && enc != nil { if enc, err := db.cleans.Get(string(hash[:])); err == nil && enc != nil {
@ -537,7 +523,15 @@ func (db *Database) Node(owner common.Hash, hash common.Hash) ([]byte, error) {
return enc, nil return enc, nil
} }
} }
// TODO(karalabe): We need 2 new retrieval mechanisms:
// - We need to retrieve from the dirty cache, needs some data struct extension (no owner)
// - We need to retrieve from the database, needs prefix iteration support (just needs the interface ext)
//
// The code below is what's needed to work, just without the 'owner' being available
/*
// Retrieve the node from the dirty cache if available // Retrieve the node from the dirty cache if available
key := makeNodeKey(owner, hash)
db.lock.RLock() db.lock.RLock()
dirty := db.dirties[key] dirty := db.dirties[key]
db.lock.RUnlock() db.lock.RUnlock()
@ -555,6 +549,8 @@ func (db *Database) Node(owner common.Hash, hash common.Hash) ([]byte, error) {
} }
} }
return enc, err return enc, err
*/
return nil, nil
} }
// preimage retrieves a cached trie node pre-image from memory. If it cannot be // preimage retrieves a cached trie node pre-image from memory. If it cannot be