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  }