github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/database.go (about) 1 package consensus 2 3 // database.go contains functions to initialize the database and report 4 // inconsistencies. All of the database-specific logic belongs here. 5 6 import ( 7 "errors" 8 "fmt" 9 "os" 10 11 "github.com/NebulousLabs/Sia/build" 12 "github.com/NebulousLabs/Sia/encoding" 13 "github.com/NebulousLabs/Sia/persist" 14 15 "github.com/coreos/bbolt" 16 ) 17 18 var ( 19 dbMetadata = persist.Metadata{ 20 Header: "Consensus Set Database", 21 Version: "0.5.0", 22 } 23 24 errDBInconsistent = errors.New("database guard indicates inconsistency within database") 25 errNilBucket = errors.New("using a bucket that does not exist") 26 errNilItem = errors.New("requested item does not exist") 27 errNonEmptyBucket = errors.New("cannot remove a map with objects still in it") 28 errRepeatInsert = errors.New("attempting to add an already existing item to the consensus set") 29 ) 30 31 type ( 32 // dbBucket represents a collection of key/value pairs inside the database. 33 dbBucket interface { 34 Get(key []byte) []byte 35 } 36 37 // dbTx represents a read-only transaction on the database that can be used 38 // for retrieving values. 39 dbTx interface { 40 Bucket(name []byte) dbBucket 41 } 42 43 // boltTxWrapper wraps a bolt.Tx so that it matches the dbTx interface. The 44 // wrap is necessary because bolt.Tx.Bucket() returns a fixed type 45 // (bolt.Bucket), but we want it to return an interface (dbBucket). 46 boltTxWrapper struct { 47 tx *bolt.Tx 48 } 49 ) 50 51 // Bucket returns the dbBucket associated with the given bucket name. 52 func (b boltTxWrapper) Bucket(name []byte) dbBucket { 53 return b.tx.Bucket(name) 54 } 55 56 // replaceDatabase backs up the existing database and creates a new one. 57 func (cs *ConsensusSet) replaceDatabase(filename string) error { 58 // Rename the existing database and create a new one. 59 fmt.Println("Outdated consensus database... backing up and replacing") 60 err := os.Rename(filename, filename+".bck") 61 if err != nil { 62 return errors.New("error while backing up consensus database: " + err.Error()) 63 } 64 65 // Try again to create a new database, this time without checking for an 66 // outdated database error. 67 cs.db, err = persist.OpenDatabase(dbMetadata, filename) 68 if err != nil { 69 return errors.New("error opening consensus database: " + err.Error()) 70 } 71 return nil 72 } 73 74 // openDB loads the set database and populates it with the necessary buckets 75 func (cs *ConsensusSet) openDB(filename string) (err error) { 76 cs.db, err = persist.OpenDatabase(dbMetadata, filename) 77 if err == persist.ErrBadVersion { 78 return cs.replaceDatabase(filename) 79 } 80 if err != nil { 81 return errors.New("error opening consensus database: " + err.Error()) 82 } 83 return nil 84 } 85 86 // initDB is run if there is no existing consensus database, creating a 87 // database with all the required buckets and sane initial values. 88 func (cs *ConsensusSet) initDB(tx *bolt.Tx) error { 89 // If the database has already been initialized, there is nothing to do. 90 // Initialization can be detected by looking for the presence of the siafund 91 // pool bucket. (legacy design chioce - ultimately probably not the best way 92 // ot tell). 93 if tx.Bucket(SiafundPool) != nil { 94 return nil 95 } 96 97 // Create the compononents of the database. 98 err := cs.createConsensusDB(tx) 99 if err != nil { 100 return err 101 } 102 err = cs.createChangeLog(tx) 103 if err != nil { 104 return err 105 } 106 107 // Place a 'false' in the consistency bucket to indicate that no 108 // inconsistencies have been found. 109 err = tx.Bucket(Consistency).Put(Consistency, encoding.Marshal(false)) 110 if err != nil { 111 return err 112 } 113 return nil 114 } 115 116 // markInconsistency flags the database to indicate that inconsistency has been 117 // detected. 118 func markInconsistency(tx *bolt.Tx) { 119 // Place a 'true' in the consistency bucket to indicate that 120 // inconsistencies have been found. 121 err := tx.Bucket(Consistency).Put(Consistency, encoding.Marshal(true)) 122 if build.DEBUG && err != nil { 123 panic(err) 124 } 125 126 }