github.com/codingfuture/orig-energi3@v0.8.4/core/state/database.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package state 18 19 import ( 20 "fmt" 21 "sync" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/ethdb" 25 "github.com/ethereum/go-ethereum/trie" 26 lru "github.com/hashicorp/golang-lru" 27 ) 28 29 // Trie cache generation limit after which to evict trie nodes from memory. 30 var MaxTrieCacheGen = uint16(120) 31 32 const ( 33 // Number of past tries to keep. This value is chosen such that 34 // reasonable chain reorg depths will hit an existing trie. 35 maxPastTries = 12 36 37 // Number of codehash->size associations to keep. 38 codeSizeCacheSize = 100000 39 ) 40 41 // Database wraps access to tries and contract code. 42 type Database interface { 43 // OpenTrie opens the main account trie. 44 OpenTrie(root common.Hash) (Trie, error) 45 46 // OpenStorageTrie opens the storage trie of an account. 47 OpenStorageTrie(addrHash, root common.Hash) (Trie, error) 48 49 // CopyTrie returns an independent copy of the given trie. 50 CopyTrie(Trie) Trie 51 52 // ContractCode retrieves a particular contract's code. 53 ContractCode(addrHash, codeHash common.Hash) ([]byte, error) 54 55 // ContractCodeSize retrieves a particular contracts code's size. 56 ContractCodeSize(addrHash, codeHash common.Hash) (int, error) 57 58 // TrieDB retrieves the low level trie database used for data storage. 59 TrieDB() *trie.Database 60 } 61 62 // Trie is a Ethereum Merkle Trie. 63 type Trie interface { 64 TryGet(key []byte) ([]byte, error) 65 TryUpdate(key, value []byte) error 66 TryDelete(key []byte) error 67 Commit(onleaf trie.LeafCallback) (common.Hash, error) 68 Hash() common.Hash 69 NodeIterator(startKey []byte) trie.NodeIterator 70 GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed 71 Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error 72 } 73 74 // NewDatabase creates a backing store for state. The returned database is safe for 75 // concurrent use and retains a few recent expanded trie nodes in memory. To keep 76 // more historical state in memory, use the NewDatabaseWithCache constructor. 77 func NewDatabase(db ethdb.Database) Database { 78 return NewDatabaseWithCache(db, 0) 79 } 80 81 // NewDatabase creates a backing store for state. The returned database is safe for 82 // concurrent use and retains both a few recent expanded trie nodes in memory, as 83 // well as a lot of collapsed RLP trie nodes in a large memory cache. 84 func NewDatabaseWithCache(db ethdb.Database, cache int) Database { 85 csc, _ := lru.New(codeSizeCacheSize) 86 return &cachingDB{ 87 db: trie.NewDatabaseWithCache(db, cache), 88 codeSizeCache: csc, 89 } 90 } 91 92 type cachingDB struct { 93 db *trie.Database 94 mu sync.Mutex 95 pastTries []*trie.SecureTrie 96 codeSizeCache *lru.Cache 97 } 98 99 // OpenTrie opens the main account trie. 100 func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { 101 db.mu.Lock() 102 defer db.mu.Unlock() 103 104 for i := len(db.pastTries) - 1; i >= 0; i-- { 105 if db.pastTries[i].Hash() == root { 106 return cachedTrie{db.pastTries[i].Copy(), db}, nil 107 } 108 } 109 tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen) 110 if err != nil { 111 return nil, err 112 } 113 return cachedTrie{tr, db}, nil 114 } 115 116 func (db *cachingDB) pushTrie(t *trie.SecureTrie) { 117 db.mu.Lock() 118 defer db.mu.Unlock() 119 120 if len(db.pastTries) >= maxPastTries { 121 copy(db.pastTries, db.pastTries[1:]) 122 db.pastTries[len(db.pastTries)-1] = t 123 } else { 124 db.pastTries = append(db.pastTries, t) 125 } 126 } 127 128 // OpenStorageTrie opens the storage trie of an account. 129 func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { 130 return trie.NewSecure(root, db.db, 0) 131 } 132 133 // CopyTrie returns an independent copy of the given trie. 134 func (db *cachingDB) CopyTrie(t Trie) Trie { 135 switch t := t.(type) { 136 case cachedTrie: 137 return cachedTrie{t.SecureTrie.Copy(), db} 138 case *trie.SecureTrie: 139 return t.Copy() 140 default: 141 panic(fmt.Errorf("unknown trie type %T", t)) 142 } 143 } 144 145 // ContractCode retrieves a particular contract's code. 146 func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { 147 code, err := db.db.Node(codeHash) 148 if err == nil { 149 db.codeSizeCache.Add(codeHash, len(code)) 150 } 151 return code, err 152 } 153 154 // ContractCodeSize retrieves a particular contracts code's size. 155 func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) { 156 if cached, ok := db.codeSizeCache.Get(codeHash); ok { 157 return cached.(int), nil 158 } 159 code, err := db.ContractCode(addrHash, codeHash) 160 return len(code), err 161 } 162 163 // TrieDB retrieves any intermediate trie-node caching layer. 164 func (db *cachingDB) TrieDB() *trie.Database { 165 return db.db 166 } 167 168 // cachedTrie inserts its trie into a cachingDB on commit. 169 type cachedTrie struct { 170 *trie.SecureTrie 171 db *cachingDB 172 } 173 174 func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) { 175 root, err := m.SecureTrie.Commit(onleaf) 176 if err == nil { 177 m.db.pushTrie(m.SecureTrie) 178 } 179 return root, err 180 } 181 182 func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error { 183 return m.SecureTrie.Prove(key, fromLevel, proofDb) 184 }