diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index 57a867d2a..31f158a1b 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -8,6 +8,8 @@ import ( "errors" "fmt" "math" + "os" + "path/filepath" "reflect" "strings" "testing" @@ -19,6 +21,7 @@ import ( "github.com/daglabs/btcd/dagconfig" "github.com/daglabs/btcd/dagconfig/daghash" + "github.com/daglabs/btcd/database" "github.com/daglabs/btcd/txscript" "github.com/daglabs/btcd/util" "github.com/daglabs/btcd/util/subnetworkid" @@ -864,6 +867,49 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF } } +func TestNew(t *testing.T) { + // Create the root directory for test databases. + if !fileExists(testDbRoot) { + if err := os.MkdirAll(testDbRoot, 0700); err != nil { + t.Fatalf("unable to create test db "+ + "root: %s", err) + } + } + + dbPath := filepath.Join(testDbRoot, "TestNew") + _ = os.RemoveAll(dbPath) + db, err := database.Create(testDbType, dbPath, blockDataNet) + if err != nil { + t.Fatalf("error creating db: %s", err) + } + defer func() { + db.Close() + os.RemoveAll(dbPath) + os.RemoveAll(testDbRoot) + }() + config := &Config{ + DAGParams: &dagconfig.SimNetParams, + SubnetworkID: subnetworkid.SubnetworkIDSupportsAll, + DB: db, + TimeSource: NewMedianTime(), + SigCache: txscript.NewSigCache(1000), + } + _, err = New(config) + if err != nil { + t.Fatalf("failed to create dag instance: %s", err) + } + + config.SubnetworkID = &subnetworkid.SubnetworkID{0xff} + _, err = New(config) + expectedErrorMessage := fmt.Sprintf("Cannot start btcd with subnetwork ID %s because"+ + " its database is already built with subnetwork ID %s. If you"+ + " want to switch to a new database, please reset the"+ + " database by starting btcd with --reset-db flag", config.SubnetworkID, subnetworkid.SubnetworkIDSupportsAll) + if err.Error() != expectedErrorMessage { + t.Errorf("Unexpected error. Expected error '%s' but got '%s'", expectedErrorMessage, err) + } +} + func TestValidateFeeTransaction(t *testing.T) { params := dagconfig.SimNetParams params.K = 1 diff --git a/blockdag/dagio.go b/blockdag/dagio.go index 4fcf2477b..58bdfef4a 100644 --- a/blockdag/dagio.go +++ b/blockdag/dagio.go @@ -14,6 +14,7 @@ import ( "github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/database" "github.com/daglabs/btcd/util" + "github.com/daglabs/btcd/util/subnetworkid" "github.com/daglabs/btcd/wire" ) @@ -57,6 +58,10 @@ var ( // subnetwork registry. subnetworksBucketName = []byte("subnetworks") + // localSubnetworkKeyName is the name of the db key used to store the + // node's local subnetwork ID. + localSubnetworkKeyName = []byte("localsubnetworkidkey") + // byteOrder is the preferred byte order used for serializing numeric // fields for storage in the database. byteOrder = binary.LittleEndian @@ -480,6 +485,10 @@ func (dag *BlockDAG) createDAGState() error { if err != nil { return err } + + if err := dbPutLocalSubnetworkID(dbTx, dag.subnetworkID); err != nil { + return err + } return nil }) @@ -489,15 +498,30 @@ func (dag *BlockDAG) createDAGState() error { return nil } +func dbPutLocalSubnetworkID(dbTx database.Tx, subnetworkID *subnetworkid.SubnetworkID) error { + return dbTx.Metadata().Put(localSubnetworkKeyName, subnetworkID[:]) +} + // initDAGState attempts to load and initialize the DAG state from the // database. When the db does not yet contain any DAG state, both it and the // DAG state are initialized to the genesis block. func (dag *BlockDAG) initDAGState() error { - // Determine the state of the chain database. We may need to initialize + // Determine the state of the DAG database. We may need to initialize // everything from scratch or upgrade certain buckets. var initialized bool err := dag.db.View(func(dbTx database.Tx) error { initialized = dbTx.Metadata().Get(dagStateKeyName) != nil + if initialized { + localSubnetworkIDBytes := dbTx.Metadata().Get(localSubnetworkKeyName) + localSubnetworkID := &subnetworkid.SubnetworkID{} + localSubnetworkID.SetBytes(localSubnetworkIDBytes) + if !localSubnetworkID.IsEqual(dag.subnetworkID) { + return fmt.Errorf("Cannot start btcd with subnetwork ID %s because"+ + " its database is already built with subnetwork ID %s. If you"+ + " want to switch to a new database, please reset the"+ + " database by starting btcd with --reset-db flag", dag.subnetworkID, localSubnetworkID) + } + } return nil }) if err != nil { diff --git a/btcd.go b/btcd.go index bb9fadabd..26df72531 100644 --- a/btcd.go +++ b/btcd.go @@ -106,6 +106,14 @@ func btcdMain(serverChan chan<- *server.Server) error { return nil } + if cfg.ResetDatabase { + err := removeDatabase() + if err != nil { + btcdLog.Errorf("%s", err) + return err + } + } + // Load the block database. db, err := loadBlockDB() if err != nil { @@ -179,6 +187,11 @@ func btcdMain(serverChan chan<- *server.Server) error { return nil } +func removeDatabase() error { + dbPath := blockDbPath(cfg.DbType) + return os.RemoveAll(dbPath) +} + // removeRegressionDB removes the existing regression test database if running // in regression test mode and it already exists. func removeRegressionDB(dbPath string) error { diff --git a/config/config.go b/config/config.go index af39b45c3..acb747283 100644 --- a/config/config.go +++ b/config/config.go @@ -140,7 +140,7 @@ type configFlags struct { SimNet bool `long:"simnet" description:"Use the simulation test network"` AddCheckpoints []string `long:"addcheckpoint" description:"Add a custom checkpoint. Format: ':'"` DisableCheckpoints bool `long:"nocheckpoints" description:"Disable built-in checkpoints. Don't do this unless you know what you're doing."` - DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"` + DbType string `long:"dbtype" description:"Database backend to use for the Block DAG"` Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"` CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"` DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` @@ -167,6 +167,7 @@ type configFlags struct { RelayNonStd bool `long:"relaynonstd" description:"Relay non-standard transactions regardless of the default settings for the active network."` RejectNonStd bool `long:"rejectnonstd" description:"Reject non-standard transactions regardless of the default settings for the active network."` Subnetwork string `long:"subnetwork" description:"If subnetwork ID != 0, than node will request and process only payloads from specified subnetwork. And if subnetwork ID is 0, than payloads of all subnetworks are processed. Subnetworks with IDs 3 through 255 are reserved for future use and are currently not allowed."` + ResetDatabase bool `long:"reset-db" description:"Reset database before starting node. It's needed when switching between subnetworks."` } // Config defines the configuration options for btcd.