mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
core/state, trie: add account existential bloom filter
This commit is contained in:
parent
ad16f46cfb
commit
d73330919f
3 changed files with 87 additions and 5 deletions
|
|
@ -50,6 +50,9 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||||
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if bytes.HasPrefix(key, []byte("accounts-bloom")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if _, ok := hashes[common.BytesToHash(key)]; !ok {
|
if _, ok := hashes[common.BytesToHash(key)]; !ok {
|
||||||
t.Errorf("state entry not reported %x", key)
|
t.Errorf("state entry not reported %x", key)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,10 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -28,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
boom "github.com/tylertreat/BoomFilters"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The starting nonce determines the default nonce when new accounts are being
|
// The starting nonce determines the default nonce when new accounts are being
|
||||||
|
|
@ -46,6 +49,11 @@ type StateDB struct {
|
||||||
// This map caches canon state accounts.
|
// This map caches canon state accounts.
|
||||||
all map[common.Address]Account
|
all map[common.Address]Account
|
||||||
|
|
||||||
|
// This filter is used to quickly check if an account exists,
|
||||||
|
// avoiding expensive database lookups.
|
||||||
|
accountBloom *boom.ScalableBloomFilter
|
||||||
|
accountBloomDirty bool
|
||||||
|
|
||||||
// This map holds 'live' objects, which will get modified
|
// This map holds 'live' objects, which will get modified
|
||||||
// while processing a state transition.
|
// while processing a state transition.
|
||||||
stateObjects map[common.Address]*StateObject
|
stateObjects map[common.Address]*StateObject
|
||||||
|
|
@ -94,6 +102,7 @@ func (self *StateDB) Reset(root common.Hash) error {
|
||||||
db: self.db,
|
db: self.db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
all: all,
|
all: all,
|
||||||
|
accountBloom: self.accountBloom,
|
||||||
stateObjects: make(map[common.Address]*StateObject),
|
stateObjects: make(map[common.Address]*StateObject),
|
||||||
refund: new(big.Int),
|
refund: new(big.Int),
|
||||||
logs: make(map[common.Hash]vm.Logs),
|
logs: make(map[common.Hash]vm.Logs),
|
||||||
|
|
@ -270,14 +279,16 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use cached account data from the canon state if possible.
|
// Use cached account data from the canon state if possible.
|
||||||
if data, ok := self.all[addr]; ok {
|
if data, ok := self.all[addr]; ok {
|
||||||
obj := NewObject(addr, data)
|
obj := NewObject(addr, data)
|
||||||
self.SetStateObject(obj)
|
self.SetStateObject(obj)
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
// Avoid surely non-existing accounts
|
||||||
|
if !self.getAccountBloom().Test(self.trie.HashKey(addr[:])) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
// Load the object from the database.
|
// Load the object from the database.
|
||||||
enc := self.trie.Get(addr[:])
|
enc := self.trie.Get(addr[:])
|
||||||
if len(enc) == 0 {
|
if len(enc) == 0 {
|
||||||
|
|
@ -321,6 +332,9 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
|
||||||
obj.dirty = true
|
obj.dirty = true
|
||||||
obj.SetNonce(StartingNonce)
|
obj.SetNonce(StartingNonce)
|
||||||
self.stateObjects[addr] = obj
|
self.stateObjects[addr] = obj
|
||||||
|
if !self.getAccountBloom().TestAndAdd(self.trie.HashKey(addr[:])) {
|
||||||
|
self.accountBloomDirty = true
|
||||||
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,7 +349,6 @@ func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
||||||
if so != nil {
|
if so != nil {
|
||||||
newSo.data.Balance = so.data.Balance
|
newSo.data.Balance = so.data.Balance
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSo
|
return newSo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,6 +365,8 @@ func (self *StateDB) Copy() *StateDB {
|
||||||
state, _ := New(common.Hash{}, self.db)
|
state, _ := New(common.Hash{}, self.db)
|
||||||
state.trie = self.trie
|
state.trie = self.trie
|
||||||
state.all = self.all
|
state.all = self.all
|
||||||
|
state.accountBloom = self.accountBloom
|
||||||
|
state.accountBloomDirty = self.accountBloomDirty
|
||||||
for addr, stateObject := range self.stateObjects {
|
for addr, stateObject := range self.stateObjects {
|
||||||
if stateObject.dirty {
|
if stateObject.dirty {
|
||||||
state.stateObjects[addr] = stateObject.Copy(self.db)
|
state.stateObjects[addr] = stateObject.Copy(self.db)
|
||||||
|
|
@ -373,6 +388,8 @@ func (self *StateDB) Set(state *StateDB) {
|
||||||
self.trie = state.trie
|
self.trie = state.trie
|
||||||
self.stateObjects = state.stateObjects
|
self.stateObjects = state.stateObjects
|
||||||
self.all = state.all
|
self.all = state.all
|
||||||
|
self.accountBloom = state.accountBloom
|
||||||
|
self.accountBloomDirty = state.accountBloomDirty
|
||||||
|
|
||||||
self.refund = state.refund
|
self.refund = state.refund
|
||||||
self.logs = state.logs
|
self.logs = state.logs
|
||||||
|
|
@ -471,10 +488,67 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error)
|
||||||
}
|
}
|
||||||
stateObject.dirty = false
|
stateObject.dirty = false
|
||||||
}
|
}
|
||||||
// Write trie changes.
|
// Write account bloom and trie changes
|
||||||
|
if s.accountBloomDirty {
|
||||||
|
if err := s.writeAccountsBloom(dbw); err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
s.accountBloomDirty = false
|
||||||
|
}
|
||||||
return s.trie.CommitTo(dbw)
|
return s.trie.CommitTo(dbw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Refunds() *big.Int {
|
func (self *StateDB) Refunds() *big.Int {
|
||||||
return self.refund
|
return self.refund
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var accountBloomKey = []byte("accounts-bloom")
|
||||||
|
|
||||||
|
// getAccountBloom returns the account existential bloom filter, lazy loading it
|
||||||
|
// from disk if needed.
|
||||||
|
func (self *StateDB) getAccountBloom() *boom.ScalableBloomFilter {
|
||||||
|
// Short circuit lookup if already loaded from the database
|
||||||
|
if self.accountBloom != nil {
|
||||||
|
return self.accountBloom
|
||||||
|
}
|
||||||
|
// If the filter's not yet cached, load it from the database
|
||||||
|
bloom, _ := self.db.Get(accountBloomKey)
|
||||||
|
if len(bloom) > 0 {
|
||||||
|
filter := new(boom.ScalableBloomFilter)
|
||||||
|
if _, err := filter.ReadFrom(bytes.NewReader(bloom)); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.accountBloom = filter
|
||||||
|
return self.accountBloom
|
||||||
|
}
|
||||||
|
// If the database does not yet contain a bloom filter, generate a new one
|
||||||
|
glog.V(logger.Info).Infof("generating accounts bloom, please wait...")
|
||||||
|
|
||||||
|
filter := boom.NewDefaultScalableBloomFilter(0.01)
|
||||||
|
start, pref := time.Now(), byte(0x00)
|
||||||
|
|
||||||
|
it := self.trie.Iterator()
|
||||||
|
for it.Next() {
|
||||||
|
if pref != it.Key[0] {
|
||||||
|
pref = it.Key[0]
|
||||||
|
glog.V(logger.Info).Infof("generating accounts bloom, at %.2f%%, please wait...", float64(pref)/2.56)
|
||||||
|
}
|
||||||
|
filter.Add(it.Key)
|
||||||
|
}
|
||||||
|
glog.V(logger.Info).Infof("accounts bloom generated in %v", time.Since(start))
|
||||||
|
|
||||||
|
self.accountBloom, self.accountBloomDirty = filter, true
|
||||||
|
return self.accountBloom
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeAccountsBloom serializes the account bloom filter used for existence checks.
|
||||||
|
func (self *StateDB) writeAccountsBloom(db trie.DatabaseWriter) error {
|
||||||
|
if self.accountBloom == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
if _, err := self.accountBloom.WriteTo(buffer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return db.Put(accountBloomKey, buffer.Bytes())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ type SecureTrie struct {
|
||||||
secKeyCache map[string][]byte
|
secKeyCache map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSecure creates a trie with an existing root node from db.
|
// NewSecure creFGetates a trie with an existing root node from db.
|
||||||
//
|
//
|
||||||
// If root is the zero hash or the sha3 hash of an empty string, the
|
// If root is the zero hash or the sha3 hash of an empty string, the
|
||||||
// trie is initially empty. Otherwise, New will panic if db is nil
|
// trie is initially empty. Otherwise, New will panic if db is nil
|
||||||
|
|
@ -138,6 +138,11 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte {
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashKey returns the sha3 image of a key.
|
||||||
|
func (t *SecureTrie) HashKey(key []byte) []byte {
|
||||||
|
return common.CopyBytes(t.hashKey(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.
|
||||||
// Nodes are stored with their sha3 hash as the key.
|
// Nodes are stored with their sha3 hash as the key.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue