github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/roundstate_db.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 core 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "math/big" 23 "os" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/common/task" 28 "github.com/ethereum/go-ethereum/consensus/istanbul" 29 "github.com/ethereum/go-ethereum/log" 30 "github.com/ethereum/go-ethereum/rlp" 31 "github.com/syndtr/goleveldb/leveldb" 32 lvlerrors "github.com/syndtr/goleveldb/leveldb/errors" 33 "github.com/syndtr/goleveldb/leveldb/opt" 34 "github.com/syndtr/goleveldb/leveldb/storage" 35 "github.com/syndtr/goleveldb/leveldb/util" 36 ) 37 38 const ( 39 dbVersion = 2 40 dbVersionKey = "version" // Version of the database to flush if changes 41 lastViewKey = "lastView" // Last View that we know of 42 rsKey = "rs" // Database Key Pefix for RoundState 43 ) 44 45 type RoundStateDB interface { 46 GetLastView() (*istanbul.View, error) 47 // GetOldestValidView returns the oldest valid view that can be stored on the db 48 // it might or might not be present on the db 49 GetOldestValidView() (*istanbul.View, error) 50 GetRoundStateFor(view *istanbul.View) (RoundState, error) 51 UpdateLastRoundState(rs RoundState) error 52 Close() error 53 } 54 55 // RoundStateDBOptions are the options for a RoundStateDB instance 56 type RoundStateDBOptions struct { 57 withGarbageCollector bool 58 garbageCollectorPeriod time.Duration 59 sequencesToSave uint64 60 } 61 62 type roundStateDBImpl struct { 63 db *leveldb.DB 64 stopGarbageCollector task.StopFn 65 opts RoundStateDBOptions 66 logger log.Logger 67 } 68 69 var defaultRoundStateDBOptions = RoundStateDBOptions{ 70 withGarbageCollector: true, 71 sequencesToSave: 100, 72 garbageCollectorPeriod: 2 * time.Minute, 73 } 74 75 func coerceOptions(opts *RoundStateDBOptions) RoundStateDBOptions { 76 if opts == nil { 77 return defaultRoundStateDBOptions 78 } 79 80 options := *opts 81 if options.sequencesToSave == 0 { 82 options.sequencesToSave = defaultRoundStateDBOptions.sequencesToSave 83 } 84 if options.withGarbageCollector && options.garbageCollectorPeriod == 0 { 85 options.garbageCollectorPeriod = defaultRoundStateDBOptions.garbageCollectorPeriod 86 } 87 return options 88 } 89 90 func newRoundStateDB(path string, opts *RoundStateDBOptions) (RoundStateDB, error) { 91 logger := log.New("func", "newRoundStateDB", "type", "roundStateDB", "rsdb_path", path) 92 93 logger.Info("Open roundstate db") 94 var db *leveldb.DB 95 var err error 96 if path == "" { 97 db, err = newMemoryDB() 98 } else { 99 db, err = newPersistentDB(path) 100 } 101 102 if err != nil { 103 logger.Error("Failed to open roundstate db", "err", err) 104 return nil, err 105 } 106 107 rsdb := &roundStateDBImpl{ 108 db: db, 109 opts: coerceOptions(opts), 110 logger: logger, 111 } 112 113 if rsdb.opts.withGarbageCollector { 114 rsdb.stopGarbageCollector = task.RunTaskRepeateadly(rsdb.garbageCollectEntries, rsdb.opts.garbageCollectorPeriod) 115 } 116 117 return rsdb, nil 118 } 119 120 // newMemoryDB creates a new in-memory node database without a persistent backend. 121 func newMemoryDB() (*leveldb.DB, error) { 122 db, err := leveldb.Open(storage.NewMemStorage(), nil) 123 if err != nil { 124 return nil, err 125 } 126 return db, nil 127 } 128 129 // newPersistentNodeDB creates/opens a leveldb backed persistent node database, 130 // also flushing its contents in case of a version mismatch. 131 func newPersistentDB(path string) (*leveldb.DB, error) { 132 opts := &opt.Options{OpenFilesCacheCapacity: 5} 133 db, err := leveldb.OpenFile(path, opts) 134 if _, iscorrupted := err.(*lvlerrors.ErrCorrupted); iscorrupted { 135 db, err = leveldb.RecoverFile(path, nil) 136 } 137 if err != nil { 138 return nil, err 139 } 140 // The nodes contained in the cache correspond to a certain protocol version. 141 // Flush all nodes if the version doesn't match. 142 currentVer := make([]byte, binary.MaxVarintLen64) 143 currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))] 144 145 blob, err := db.Get([]byte(dbVersionKey), nil) 146 switch err { 147 case leveldb.ErrNotFound: 148 // Version not found (i.e. empty cache), insert it 149 if err := db.Put([]byte(dbVersionKey), currentVer, nil); err != nil { 150 db.Close() 151 return nil, err 152 } 153 154 case nil: 155 // Version present, flush if different 156 if !bytes.Equal(blob, currentVer) { 157 db.Close() 158 if err = os.RemoveAll(path); err != nil { 159 return nil, err 160 } 161 return newPersistentDB(path) 162 } 163 } 164 return db, nil 165 } 166 167 // storeRoundState will store the currentRoundState in a Map<view, roundState> schema. 168 func (rsdb *roundStateDBImpl) UpdateLastRoundState(rs RoundState) error { 169 // We store the roundState for each view; since we'll need this 170 // information to allow the node to have evidence to show that 171 // a validator did a "valid" double signing 172 logger := rsdb.logger.New("func", "UpdateLastRoundState") 173 viewKey := view2Key(rs.View()) 174 175 entryBytes, err := rlp.EncodeToBytes(rs) 176 if err != nil { 177 logger.Error("Failed to save roundState", "reason", "rlp encoding", "err", err) 178 return err 179 } 180 181 batch := new(leveldb.Batch) 182 batch.Put([]byte(lastViewKey), viewKey) 183 batch.Put(viewKey, entryBytes) 184 185 err = rsdb.db.Write(batch, nil) 186 if err != nil { 187 logger.Error("Failed to save roundState", "reason", "levelDB write", "err", err, "func") 188 } 189 190 return err 191 } 192 193 func (rsdb *roundStateDBImpl) GetLastView() (*istanbul.View, error) { 194 rawEntry, err := rsdb.db.Get([]byte(lastViewKey), nil) 195 if err != nil { 196 return nil, err 197 } 198 199 return key2View(rawEntry), nil 200 } 201 202 func (rsdb *roundStateDBImpl) GetOldestValidView() (*istanbul.View, error) { 203 lastView, err := rsdb.GetLastView() 204 // If nothing stored all views are valid 205 if err == leveldb.ErrNotFound { 206 return &istanbul.View{Sequence: common.Big0, Round: common.Big0}, nil 207 } else if err != nil { 208 return nil, err 209 } 210 211 oldestValidSequence := new(big.Int).Sub(lastView.Sequence, new(big.Int).SetUint64(rsdb.opts.sequencesToSave)) 212 if oldestValidSequence.Cmp(common.Big0) < 0 { 213 oldestValidSequence = common.Big0 214 } 215 216 return &istanbul.View{Sequence: oldestValidSequence, Round: common.Big0}, nil 217 } 218 219 func (rsdb *roundStateDBImpl) GetRoundStateFor(view *istanbul.View) (RoundState, error) { 220 viewKey := view2Key(view) 221 rawEntry, err := rsdb.db.Get(viewKey, nil) 222 if err != nil { 223 return nil, err 224 } 225 226 var entry roundStateImpl 227 if err = rlp.DecodeBytes(rawEntry, &entry); err != nil { 228 return nil, err 229 } 230 return &entry, nil 231 } 232 233 func (rsdb *roundStateDBImpl) Close() error { 234 if rsdb.opts.withGarbageCollector { 235 rsdb.stopGarbageCollector() 236 } 237 return rsdb.db.Close() 238 } 239 240 func (rsdb *roundStateDBImpl) garbageCollectEntries() { 241 logger := rsdb.logger.New("func", "garbageCollectEntries") 242 243 oldestValidView, err := rsdb.GetOldestValidView() 244 if err != nil { 245 logger.Error("Aborting RoundStateDB GarbageCollect: Failed to fetch oldestValidView", "err", err) 246 return 247 } 248 249 logger.Debug("Pruning entries from old views", "oldestValidView", oldestValidView) 250 count, err := rsdb.deleteEntriesOlderThan(oldestValidView) 251 if err != nil { 252 logger.Error("Aborting RoundStateDB GarbageCollect: Failed to remove entries", "entries_removed", count, "err", err) 253 return 254 } 255 256 logger.Debug("Finished RoundStateDB GarbageCollect", "removed_entries", count) 257 } 258 259 func (rsdb *roundStateDBImpl) deleteEntriesOlderThan(lastView *istanbul.View) (int, error) { 260 fromViewKey := view2Key(&istanbul.View{Sequence: common.Big0, Round: common.Big0}) 261 toViewKey := view2Key(lastView) 262 263 iter := rsdb.db.NewIterator(&util.Range{Start: fromViewKey, Limit: toViewKey}, nil) 264 defer iter.Release() 265 266 counter := 0 267 for iter.Next() { 268 rawKey := iter.Key() 269 err := rsdb.db.Delete(rawKey, nil) 270 if err != nil { 271 return counter, err 272 } 273 counter++ 274 } 275 return counter, nil 276 } 277 278 // view2Key will encode a view in binary format 279 // so that the binary format maintains the sort order for the view 280 func view2Key(view *istanbul.View) []byte { 281 // leveldb sorts entries by key 282 // keys are sorted with their binary representation, so we need a binary representation 283 // that mantains the key order 284 // The key format is [ prefix . BigEndian(Sequence) . BigEndian(Round)] 285 // We use BigEndian so to maintain order in binary format 286 // And we want to sort by (seq, round); since seq had higher precedence than round 287 prefix := []byte(rsKey) 288 buff := make([]byte, len(prefix)+16) 289 290 copy(buff, prefix) 291 // TODO (mcortesi) Support Seq/Round bigger than 64bits 292 binary.BigEndian.PutUint64(buff[len(prefix):], view.Sequence.Uint64()) 293 binary.BigEndian.PutUint64(buff[len(prefix)+8:], view.Round.Uint64()) 294 295 return buff 296 } 297 298 func key2View(key []byte) *istanbul.View { 299 prefixLen := len([]byte(rsKey)) 300 seq := binary.BigEndian.Uint64(key[prefixLen : prefixLen+8]) 301 round := binary.BigEndian.Uint64(key[prefixLen+8:]) 302 return &istanbul.View{ 303 Sequence: new(big.Int).SetUint64(seq), 304 Round: new(big.Int).SetUint64(round), 305 } 306 }