github.com/gochain-io/gochain@v2.2.26+incompatible/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/gochain-io/gochain/common" 24 "github.com/gochain-io/gochain/trie" 25 lru "github.com/hashicorp/golang-lru" 26 ) 27 28 // Trie cache generation limit after which to evict trie nodes from memory. 29 var MaxTrieCacheGen = uint16(120) 30 31 const ( 32 // Number of past tries to keep. This value is chosen such that 33 // reasonable chain reorg depths will hit an existing trie. 34 maxPastTries = 12 35 36 // Number of codehash->size associations to keep. 37 codeSizeCacheSize = 100000 38 ) 39 40 // Database wraps access to tries and contract code. 41 type Database interface { 42 // OpenTrie opens the main account trie. 43 OpenTrie(root common.Hash) (Trie, error) 44 45 // OpenStorageTrie opens the storage trie of an account. 46 OpenStorageTrie(addrHash, root common.Hash) (Trie, error) 47 48 // CopyTrie returns an independent copy of the given trie. 49 CopyTrie(Trie) Trie 50 51 // ContractCode retrieves a particular contract's code. 52 ContractCode(addrHash, codeHash common.Hash) ([]byte, error) 53 54 // ContractCodeSize retrieves a particular contracts code's size. 55 ContractCodeSize(addrHash, codeHash common.Hash) (int, error) 56 57 // TrieDB retrieves the low level trie database used for data storage. 58 TrieDB() *trie.Database 59 } 60 61 // Trie is a Ethereum Merkle Trie. 62 type Trie interface { 63 TryGet(key []byte) ([]byte, error) 64 TryUpdate(key, value []byte) error 65 TryDelete(key []byte) error 66 Commit(onleaf trie.LeafCallback) (common.Hash, error) 67 Hash() common.Hash 68 NodeIterator(startKey []byte) trie.NodeIterator 69 GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed 70 Prove(key []byte, fromLevel uint, proofDb common.Putter) error 71 } 72 73 // NewDatabase creates a backing store for state. The returned database is safe for 74 // concurrent use and retains a few recent expanded trie nodes in memory. To keep 75 // more historical state in memory, use the NewDatabaseWithCache constructor. 76 func NewDatabase(db common.Database) Database { 77 return NewDatabaseWithCache(db, 0) 78 } 79 80 // NewDatabase creates a backing store for state. The returned database is safe for 81 // concurrent use and retains both a few recent expanded trie nodes in memory, as 82 // well as a lot of collapsed RLP trie nodes in a large memory cache. 83 func NewDatabaseWithCache(db common.Database, cache int) Database { 84 csc, _ := lru.New(codeSizeCacheSize) 85 return &cachingDB{ 86 db: trie.NewDatabaseWithCache(db.GlobalTable(), cache), 87 codeSizeCache: csc, 88 } 89 } 90 91 type cachingDB struct { 92 db *trie.Database 93 mu sync.Mutex 94 pastTries []*trie.SecureTrie 95 codeSizeCache *lru.Cache 96 } 97 98 // OpenTrie opens the main account trie. 99 func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { 100 db.mu.Lock() 101 defer db.mu.Unlock() 102 103 for i := len(db.pastTries) - 1; i >= 0; i-- { 104 if db.pastTries[i].Hash() == root { 105 return cachedTrie{db.pastTries[i].Copy(), db}, nil 106 } 107 } 108 tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen) 109 if err != nil { 110 return nil, err 111 } 112 return cachedTrie{tr, db}, nil 113 } 114 115 func (db *cachingDB) pushTrie(t *trie.SecureTrie) { 116 db.mu.Lock() 117 defer db.mu.Unlock() 118 119 if len(db.pastTries) >= maxPastTries { 120 copy(db.pastTries, db.pastTries[1:]) 121 db.pastTries[len(db.pastTries)-1] = t 122 } else { 123 db.pastTries = append(db.pastTries, t) 124 } 125 } 126 127 // OpenStorageTrie opens the storage trie of an account. 128 func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { 129 return trie.NewSecure(root, db.db, 0) 130 } 131 132 // CopyTrie returns an independent copy of the given trie. 133 func (db *cachingDB) CopyTrie(t Trie) Trie { 134 switch t := t.(type) { 135 case cachedTrie: 136 return cachedTrie{t.SecureTrie.Copy(), db} 137 case *trie.SecureTrie: 138 return t.Copy() 139 default: 140 panic(fmt.Errorf("unknown trie type %T", t)) 141 } 142 } 143 144 // ContractCode retrieves a particular contract's code. 145 func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { 146 code, err := db.db.Node(codeHash) 147 if err == nil { 148 db.codeSizeCache.Add(codeHash, len(code)) 149 } 150 return code, err 151 } 152 153 // ContractCodeSize retrieves a particular contracts code's size. 154 func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) { 155 if cached, ok := db.codeSizeCache.Get(codeHash); ok { 156 return cached.(int), nil 157 } 158 code, err := db.ContractCode(addrHash, codeHash) 159 return len(code), err 160 } 161 162 // TrieDB retrieves any intermediate trie-node caching layer. 163 func (db *cachingDB) TrieDB() *trie.Database { 164 return db.db 165 } 166 167 // cachedTrie inserts its trie into a cachingDB on commit. 168 type cachedTrie struct { 169 *trie.SecureTrie 170 db *cachingDB 171 } 172 173 func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) { 174 root, err := m.SecureTrie.Commit(onleaf) 175 if err == nil { 176 m.db.pushTrie(m.SecureTrie) 177 } 178 return root, err 179 } 180 181 func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb common.Putter) error { 182 return m.SecureTrie.Prove(key, fromLevel, proofDb) 183 }