github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/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/NebulousLabs/bolt" 16 ) 17 18 var ( 19 errRepeatInsert = errors.New("attempting to add an already existing item to the consensus set") 20 errNilBucket = errors.New("using a bucket that does not exist") 21 errNilItem = errors.New("requested item does not exist") 22 errDBInconsistent = errors.New("database guard indicates inconsistency within database") 23 errNonEmptyBucket = errors.New("cannot remove a map with objects still in it") 24 25 dbMetadata = persist.Metadata{ 26 Header: "Consensus Set Database", 27 Version: "0.5.0", 28 } 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 // dbInitialized returns true if the database appears to be initialized, false 87 // if not. Checking for the existence of the siafund pool bucket is typically 88 // sufficient to determine whether the database has gone through the 89 // initialization process. 90 func dbInitialized(tx *bolt.Tx) bool { 91 return tx.Bucket(SiafundPool) != nil 92 } 93 94 // initDB is run if there is no existing consensus database, creating a 95 // database with all the required buckets and sane initial values. 96 func (cs *ConsensusSet) initDB(tx *bolt.Tx) error { 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 // inconsistencyDetected indicates whether inconsistency has been detected 117 // within the database. 118 func inconsistencyDetected(tx *bolt.Tx) (detected bool) { 119 inconsistencyBytes := tx.Bucket(Consistency).Get(Consistency) 120 err := encoding.Unmarshal(inconsistencyBytes, &detected) 121 if build.DEBUG && err != nil { 122 panic(err) 123 } 124 return detected 125 } 126 127 // markInconsistency flags the database to indicate that inconsistency has been 128 // detected. 129 func markInconsistency(tx *bolt.Tx) { 130 // Place a 'true' in the consistency bucket to indicate that 131 // inconsistencies have been found. 132 err := tx.Bucket(Consistency).Put(Consistency, encoding.Marshal(true)) 133 if build.DEBUG && err != nil { 134 panic(err) 135 } 136 137 }