diff --git a/core/blockchain.go b/core/blockchain.go index 521413ebdc..eebec4ca2f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -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) // either from ephemeral in-memory cache, or from persistent storage. 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 diff --git a/core/rawdb/accessors_state.go b/core/rawdb/accessors_state.go new file mode 100644 index 0000000000..dc75169aab --- /dev/null +++ b/core/rawdb/accessors_state.go @@ -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 . + +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) + } +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 8a9921ef40..66ea8af41c 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -53,6 +53,8 @@ var ( txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata 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 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 } +// codeKey = codePrefix + hash +func codeKey(hash common.Hash) []byte { + return append(codePrefix, hash.Bytes()...) +} + // preimageKey = preimagePrefix + hash func preimageKey(hash common.Hash) []byte { return append(preimagePrefix, hash.Bytes()...) diff --git a/core/state/database.go b/core/state/database.go index 600d0a7cf3..5fe4e98616 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -17,10 +17,12 @@ package state import ( + "errors" "fmt" "sync" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" lru "github.com/hashicorp/golang-lru" @@ -144,11 +146,11 @@ func (db *cachingDB) CopyTrie(t Trie) Trie { // ContractCode retrieves a particular contract's code. func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { - code, err := db.db.Node(common.Hash{}, codeHash) - if err == nil { + if code := rawdb.ReadCode(db.db.DiskDB().(ethdb.Database), codeHash); code != nil { 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. diff --git a/core/state/statedb.go b/core/state/statedb.go index 852eab8f53..c80d30b454 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -24,6 +24,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -636,7 +637,8 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) case isDirty: // Write any contract code associated with the state object 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 } // Write any storage changes in the state object to its storage trie. diff --git a/les/handler.go b/les/handler.go index 7bfaf39fcc..3fb5ff99dd 100644 --- a/les/handler.go +++ b/les/handler.go @@ -631,7 +631,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if err != nil { 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) if bytes += len(code); bytes >= softResponseLimit { diff --git a/trie/database.go b/trie/database.go index de86e482ee..9a15aa6cc7 100644 --- a/trie/database.go +++ b/trie/database.go @@ -74,7 +74,7 @@ func makeNodeKey(owner common.Hash, hash common.Hash) string { if owner == (common.Hash{}) { return string(hash[:]) } - return string(append(owner[:], hash[:]...)) + return string(append(hash[:], owner[:]...)) } // 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)) 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: panic(fmt.Sprintf("invalid node key: %s", key)) @@ -432,18 +432,6 @@ func (db *Database) DiskDB() DatabaseReader { 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 // 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 @@ -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 // cached, the method queries the persistent database for the content. -func (db *Database) Node(owner common.Hash, hash common.Hash) ([]byte, error) { - key := makeNodeKey(owner, hash) - +func (db *Database) Node(hash common.Hash) ([]byte, error) { // Retrieve the node from the clean cache if available if db.cleans != nil { if enc, err := db.cleans.Get(string(hash[:])); err == nil && enc != nil { @@ -537,24 +523,34 @@ func (db *Database) Node(owner common.Hash, hash common.Hash) ([]byte, error) { return enc, nil } } - // Retrieve the node from the dirty cache if available - db.lock.RLock() - dirty := db.dirties[key] - db.lock.RUnlock() + // 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 + key := makeNodeKey(owner, hash) - if dirty != nil { - return dirty.rlp(), nil - } - // Content unavailable in memory, attempt to retrieve from disk - enc, err := db.diskdb.Get([]byte(key)) - if err == nil && enc != nil { - if db.cleans != nil { - db.cleans.Set(string(hash[:]), enc) - memcacheCleanMissMeter.Mark(1) - memcacheCleanWriteMeter.Mark(int64(len(enc))) + db.lock.RLock() + dirty := db.dirties[key] + db.lock.RUnlock() + + if dirty != nil { + return dirty.rlp(), nil } - } - return enc, err + // Content unavailable in memory, attempt to retrieve from disk + enc, err := db.diskdb.Get([]byte(key)) + if err == nil && enc != nil { + if db.cleans != nil { + db.cleans.Set(string(hash[:]), enc) + memcacheCleanMissMeter.Mark(1) + memcacheCleanWriteMeter.Mark(int64(len(enc))) + } + } + return enc, err + */ + return nil, nil } // preimage retrieves a cached trie node pre-image from memory. If it cannot be