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