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  }