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