github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/backend/internal/enodes/val_enode_db.go (about) 1 // Copyright 2017 The Celo Authors 2 // This file is part of the celo library. 3 // 4 // The celo 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 celo 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 celo library. If not, see <http://www.gnu.org/licenses/>. 16 17 package enodes 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "fmt" 23 "io" 24 "os" 25 "strings" 26 "sync" 27 28 "github.com/syndtr/goleveldb/leveldb" 29 lvlerrors "github.com/syndtr/goleveldb/leveldb/errors" 30 "github.com/syndtr/goleveldb/leveldb/opt" 31 "github.com/syndtr/goleveldb/leveldb/storage" 32 "github.com/syndtr/goleveldb/leveldb/util" 33 34 "github.com/ethereum/go-ethereum/common" 35 "github.com/ethereum/go-ethereum/consensus/istanbul" 36 "github.com/ethereum/go-ethereum/log" 37 "github.com/ethereum/go-ethereum/p2p/enode" 38 "github.com/ethereum/go-ethereum/rlp" 39 ) 40 41 // Keys in the node database. 42 const ( 43 dbVersionKey = "version" // Version of the database to flush if changes 44 dbAddressPrefix = "address:" // Identifier to prefix node entries with 45 dbNodeIDPrefix = "nodeid:" 46 ) 47 48 const ( 49 // dbNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped. 50 // dbCleanupCycle = time.Hour // Time period for running the expiration task. 51 dbVersion = 2 52 ) 53 54 // ValidatorEnodeHandler is handler to Add/Remove events. Events execute within write lock 55 type ValidatorEnodeHandler interface { 56 // AddValidatorPeer adds a validator peer 57 AddValidatorPeer(node *enode.Node, address common.Address) 58 59 // RemoveValidatorPeer removes a validator peer 60 RemoveValidatorPeer(node *enode.Node) 61 62 // ReplaceValidatorPeers replace all validator peers for new list of enodeURLs 63 ReplaceValidatorPeers(newNodes []*enode.Node) 64 65 // Clear all validator peers 66 ClearValidatorPeers() 67 } 68 69 func addressKey(address common.Address) []byte { 70 return append([]byte(dbAddressPrefix), address.Bytes()...) 71 } 72 73 func nodeIDKey(nodeID enode.ID) []byte { 74 return append([]byte(dbNodeIDPrefix), nodeID.Bytes()...) 75 } 76 77 // AddressEntry is an entry for the valEnodeTable 78 type AddressEntry struct { 79 Node *enode.Node 80 Timestamp uint 81 } 82 83 func (ve *AddressEntry) String() string { 84 return fmt.Sprintf("{enodeURL: %v, timestamp: %v}", ve.Node.String(), ve.Timestamp) 85 } 86 87 // Implement RLP Encode/Decode interface 88 type rlpEntry struct { 89 EnodeURL string 90 Timestamp uint 91 } 92 93 // EncodeRLP serializes AddressEntry into the Ethereum RLP format. 94 func (ve *AddressEntry) EncodeRLP(w io.Writer) error { 95 return rlp.Encode(w, rlpEntry{ve.Node.String(), ve.Timestamp}) 96 } 97 98 // DecodeRLP implements rlp.Decoder, and load the AddressEntry fields from a RLP stream. 99 func (ve *AddressEntry) DecodeRLP(s *rlp.Stream) error { 100 var entry rlpEntry 101 if err := s.Decode(&entry); err != nil { 102 return err 103 } 104 105 node, err := enode.ParseV4(entry.EnodeURL) 106 if err != nil { 107 return err 108 } 109 110 *ve = AddressEntry{Node: node, Timestamp: entry.Timestamp} 111 return nil 112 } 113 114 // ValidatorEnodeDB represents a Map that can be accessed either 115 // by address or enode 116 type ValidatorEnodeDB struct { 117 db *leveldb.DB //the actual DB 118 lock sync.RWMutex 119 handler ValidatorEnodeHandler 120 logger log.Logger 121 } 122 123 // OpenValidatorEnodeDB opens a validator enode database for storing and retrieving infos about validator 124 // enodes. If no path is given an in-memory, temporary database is constructed. 125 func OpenValidatorEnodeDB(path string, handler ValidatorEnodeHandler) (*ValidatorEnodeDB, error) { 126 var db *leveldb.DB 127 var err error 128 129 logger := log.New() 130 131 if path == "" { 132 db, err = newMemoryDB() 133 } else { 134 db, err = newPersistentDB(path, logger) 135 } 136 137 if err != nil { 138 return nil, err 139 } 140 return &ValidatorEnodeDB{ 141 db: db, 142 handler: handler, 143 logger: logger, 144 }, nil 145 } 146 147 // newMemoryDB creates a new in-memory node database without a persistent backend. 148 func newMemoryDB() (*leveldb.DB, error) { 149 db, err := leveldb.Open(storage.NewMemStorage(), nil) 150 if err != nil { 151 return nil, err 152 } 153 return db, nil 154 } 155 156 // newPersistentNodeDB creates/opens a leveldb backed persistent node database, 157 // also flushing its contents in case of a version mismatch. 158 func newPersistentDB(path string, logger log.Logger) (*leveldb.DB, error) { 159 opts := &opt.Options{OpenFilesCacheCapacity: 5} 160 db, err := leveldb.OpenFile(path, opts) 161 if _, iscorrupted := err.(*lvlerrors.ErrCorrupted); iscorrupted { 162 db, err = leveldb.RecoverFile(path, nil) 163 } 164 if err != nil { 165 return nil, err 166 } 167 // The nodes contained in the cache correspond to a certain protocol version. 168 // Flush all nodes if the version doesn't match. 169 currentVer := make([]byte, binary.MaxVarintLen64) 170 currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))] 171 172 blob, err := db.Get([]byte(dbVersionKey), nil) 173 switch err { 174 case leveldb.ErrNotFound: 175 // Version not found (i.e. empty cache), insert it 176 if err := db.Put([]byte(dbVersionKey), currentVer, nil); err != nil { 177 db.Close() 178 return nil, err 179 } 180 181 case nil: 182 // Version present, flush if different 183 if !bytes.Equal(blob, currentVer) { 184 oldVersion, _ := binary.Varint(blob) 185 newVersion, _ := binary.Varint(currentVer) 186 logger.Info("Val Enode DB version has changed. Creating a new leveldb.", "old version", oldVersion, "new version", newVersion) 187 db.Close() 188 if err = os.RemoveAll(path); err != nil { 189 return nil, err 190 } 191 return newPersistentDB(path, logger) 192 } 193 } 194 return db, nil 195 } 196 197 // Close flushes and closes the database files. 198 func (vet *ValidatorEnodeDB) Close() error { 199 return vet.db.Close() 200 } 201 202 func (vet *ValidatorEnodeDB) String() string { 203 vet.lock.RLock() 204 defer vet.lock.RUnlock() 205 var b strings.Builder 206 b.WriteString("ValEnodeTable:") 207 208 err := vet.iterateOverAddressEntries(func(address common.Address, entry *AddressEntry) error { 209 fmt.Fprintf(&b, " [%s => %s]", address.String(), entry.String()) 210 return nil 211 }) 212 213 if err != nil { 214 vet.logger.Error("ValidatorEnodeDB.String error", "err", err) 215 } 216 217 return b.String() 218 } 219 220 // GetEnodeURLFromAddress will return the enodeURL for an address if it's known 221 func (vet *ValidatorEnodeDB) GetNodeFromAddress(address common.Address) (*enode.Node, error) { 222 vet.lock.RLock() 223 defer vet.lock.RUnlock() 224 entry, err := vet.getAddressEntry(address) 225 if err != nil { 226 return nil, err 227 } 228 return entry.Node, nil 229 } 230 231 // GetTimestampFromAddress will return the timestamp for an address if it's known 232 func (vet *ValidatorEnodeDB) GetTimestampFromAddress(address common.Address) (uint, error) { 233 vet.lock.RLock() 234 defer vet.lock.RUnlock() 235 entry, err := vet.getAddressEntry(address) 236 if err != nil { 237 return 0, err 238 } 239 return entry.Timestamp, nil 240 } 241 242 // GetAddressFromNodeID will return the address for an nodeID if it's known 243 func (vet *ValidatorEnodeDB) GetAddressFromNodeID(nodeID enode.ID) (common.Address, error) { 244 vet.lock.RLock() 245 defer vet.lock.RUnlock() 246 247 rawEntry, err := vet.db.Get(nodeIDKey(nodeID), nil) 248 if err != nil { 249 return common.ZeroAddress, err 250 } 251 return common.BytesToAddress(rawEntry), nil 252 } 253 254 // GetAllValEnodes will return all entries in the valEnodeDB 255 func (vet *ValidatorEnodeDB) GetAllValEnodes() (map[common.Address]*AddressEntry, error) { 256 vet.lock.RLock() 257 defer vet.lock.RUnlock() 258 var entries = make(map[common.Address]*AddressEntry) 259 260 err := vet.iterateOverAddressEntries(func(address common.Address, entry *AddressEntry) error { 261 entries[address] = entry 262 return nil 263 }) 264 265 if err != nil { 266 vet.logger.Error("ValidatorEnodeDB.GetAllAddressEntries error", "err", err) 267 return nil, err 268 } 269 270 return entries, nil 271 } 272 273 // Upsert will update or insert a validator enode entry; given that the existing entry 274 // is older (determined by timestamp parameter) than the new one 275 // TODO - In addition to modifying the val_enode_db, this function also will disconnect 276 // and/or connect the corresponding validator connenctions. The validator connections 277 // should be managed be a separate thread (see https://github.com/celo-org/celo-blockchain/issues/607) 278 func (vet *ValidatorEnodeDB) Upsert(valEnodeEntries map[common.Address]*AddressEntry) error { 279 vet.lock.Lock() 280 defer vet.lock.Unlock() 281 282 batch := new(leveldb.Batch) 283 peersToRemove := make([]*enode.Node, 0, len(valEnodeEntries)) 284 peersToAdd := make(map[common.Address]*enode.Node) 285 286 for remoteAddress, addressEntry := range valEnodeEntries { 287 currentEntry, err := vet.getAddressEntry(remoteAddress) 288 isNew := err == leveldb.ErrNotFound 289 290 // Check errors 291 if !isNew && err != nil { 292 return err 293 } 294 295 // new entry 296 rawEntry, err := rlp.EncodeToBytes(addressEntry) 297 if err != nil { 298 return err 299 } 300 301 // If it's an old message, ignore it 302 if !isNew && addressEntry.Timestamp < currentEntry.Timestamp { 303 vet.logger.Trace("Ignoring the entry because its timestamp is older than what is stored in the val enode db", 304 "entryAddress", remoteAddress, "newEntry", addressEntry.String(), "currentEntry", currentEntry.String()) 305 continue 306 } 307 308 hasOldValueChanged := !isNew && currentEntry.Node.String() != addressEntry.Node.String() 309 310 if hasOldValueChanged { 311 batch.Delete(nodeIDKey(currentEntry.Node.ID())) 312 batch.Put(nodeIDKey(addressEntry.Node.ID()), remoteAddress.Bytes()) 313 peersToRemove = append(peersToRemove, currentEntry.Node) 314 } else if isNew { 315 batch.Put(nodeIDKey(addressEntry.Node.ID()), remoteAddress.Bytes()) 316 } 317 batch.Put(addressKey(remoteAddress), rawEntry) 318 peersToAdd[remoteAddress] = addressEntry.Node 319 320 vet.logger.Trace("Going to upsert an entry in the valEnodeTable", "address", remoteAddress, "enodeURL", addressEntry.Node.String()) 321 } 322 323 if batch.Len() > 0 { 324 err := vet.db.Write(batch, nil) 325 if err != nil { 326 return err 327 } else { 328 for _, node := range peersToRemove { 329 vet.handler.RemoveValidatorPeer(node) 330 } 331 332 for address, node := range peersToAdd { 333 vet.handler.AddValidatorPeer(node, address) 334 } 335 } 336 } 337 338 return nil 339 } 340 341 // RemoveEntry will remove an entry from the table 342 func (vet *ValidatorEnodeDB) RemoveEntry(address common.Address) error { 343 vet.lock.Lock() 344 defer vet.lock.Unlock() 345 batch := new(leveldb.Batch) 346 err := vet.addDeleteToBatch(batch, address) 347 if err != nil { 348 return err 349 } 350 return vet.db.Write(batch, nil) 351 } 352 353 // PruneEntries will remove entries for all address not present in addressesToKeep 354 func (vet *ValidatorEnodeDB) PruneEntries(addressesToKeep map[common.Address]bool) error { 355 vet.lock.Lock() 356 defer vet.lock.Unlock() 357 batch := new(leveldb.Batch) 358 err := vet.iterateOverAddressEntries(func(address common.Address, entry *AddressEntry) error { 359 if !addressesToKeep[address] { 360 vet.logger.Trace("Deleting entry from valEnodeTable", "address", address) 361 return vet.addDeleteToBatch(batch, address) 362 } 363 return nil 364 }) 365 366 if err != nil { 367 return err 368 } 369 return vet.db.Write(batch, nil) 370 } 371 372 func (vet *ValidatorEnodeDB) RefreshValPeers(valset istanbul.ValidatorSet, ourAddress common.Address) { 373 // We use a R lock since we don't modify levelDB table 374 vet.lock.RLock() 375 defer vet.lock.RUnlock() 376 377 if valset.ContainsByAddress(ourAddress) { 378 // transform address to enodeURLs 379 newNodes := []*enode.Node{} 380 for _, val := range valset.List() { 381 entry, err := vet.getAddressEntry(val.Address()) 382 if err == nil { 383 newNodes = append(newNodes, entry.Node) 384 } else if err != leveldb.ErrNotFound { 385 vet.logger.Error("Error reading valEnodeTable: GetEnodeURLFromAddress", "err", err) 386 } 387 } 388 389 vet.handler.ReplaceValidatorPeers(newNodes) 390 } else { 391 // Disconnect all validator peers if this node is not in the valset 392 vet.handler.ClearValidatorPeers() 393 } 394 } 395 396 func (vet *ValidatorEnodeDB) addDeleteToBatch(batch *leveldb.Batch, address common.Address) error { 397 entry, err := vet.getAddressEntry(address) 398 if err != nil { 399 return err 400 } 401 402 batch.Delete(addressKey(address)) 403 batch.Delete(nodeIDKey(entry.Node.ID())) 404 if vet.handler != nil { 405 vet.handler.RemoveValidatorPeer(entry.Node) 406 } 407 return nil 408 } 409 410 func (vet *ValidatorEnodeDB) getAddressEntry(address common.Address) (*AddressEntry, error) { 411 var entry AddressEntry 412 rawEntry, err := vet.db.Get(addressKey(address), nil) 413 if err != nil { 414 return nil, err 415 } 416 417 if err = rlp.DecodeBytes(rawEntry, &entry); err != nil { 418 return nil, err 419 } 420 return &entry, nil 421 } 422 423 func (vet *ValidatorEnodeDB) iterateOverAddressEntries(onEntry func(common.Address, *AddressEntry) error) error { 424 iter := vet.db.NewIterator(util.BytesPrefix([]byte(dbAddressPrefix)), nil) 425 defer iter.Release() 426 427 for iter.Next() { 428 var entry AddressEntry 429 address := common.BytesToAddress(iter.Key()[len(dbAddressPrefix):]) 430 rlp.DecodeBytes(iter.Value(), &entry) 431 432 err := onEntry(address, &entry) 433 if err != nil { 434 return err 435 } 436 } 437 return iter.Error() 438 } 439 440 type ValEnodeEntryInfo struct { 441 Enode string `json:"enode"` 442 Timestamp uint `json:"timestamp"` 443 } 444 445 func (vet *ValidatorEnodeDB) ValEnodeTableInfo() (map[string]*ValEnodeEntryInfo, error) { 446 valEnodeTableInfo := make(map[string]*ValEnodeEntryInfo) 447 448 valEnodeTable, err := vet.GetAllValEnodes() 449 if err == nil { 450 for address, valEnodeEntry := range valEnodeTable { 451 valEnodeTableInfo[address.Hex()] = &ValEnodeEntryInfo{Enode: valEnodeEntry.Node.String(), 452 Timestamp: valEnodeEntry.Timestamp} 453 } 454 } 455 456 return valEnodeTableInfo, err 457 }