Don't add a genesis block by default.

This commit modifies the way initial database creation is handled.

Previously, the genesis for the main chain was inserted automatically upon
creation of the database.  However, that approach caused an issue since
other networks such as the test network don't use the same genesis block
as the main network.

The new approach introduced by this commit is to leave it up to the caller
to insert the desired genesis block.  In order to support this, the
InsertBlock function has been modified to allow the first (and only the
first) block to be inserted without having an existing parent.  Also, the
NewestSha function has been modified to return a zero hash, -1 for the
height, and no error when the database does not yet have any blocks.  This
allows the caller to determine the difference between no blocks and only
the genesis block (in which case the return values would be the genesis
hash and 0 for the height).
This commit is contained in:
Dave Collins 2013-07-25 16:44:18 -05:00
parent 584481280c
commit 25684a2ccb
7 changed files with 78 additions and 66 deletions

View file

@ -13,9 +13,30 @@ change.
## Sample Use
```Go
db, err := btcdb.CreateDB("sqlite", "dbexample")
// Import packages.
import (
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/sqlite3"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
)
// Create a database and schedule it to be closed on exit.
dbName := "example.db"
db, err := btcdb.CreateDB("sqlite", dbName)
if err != nil {
// Log and handle the error
}
defer db.Close()
// Insert the main network genesis block.
pver := btcwire.ProtocolVersion
genesis := btcutil.NewBlock(&btcwire.GenesisBlock, pver)
newHeight, err := db.InsertBlock(block)
db.Sync()
if err != nil {
// Log and handle the error
}
```
## Documentation

10
db.go
View file

@ -96,7 +96,9 @@ type Db interface {
FetchTxUsedBySha(txsha *btcwire.ShaHash) (spentbuf []byte, err error)
// InsertBlock inserts raw block and transaction data from a block
// into the database.
// into the database. The first block inserted into the database
// will be treated as the genesis block. Every subsequent block insert
// requires the referenced parent block to already exist.
InsertBlock(block *btcutil.Block) (height int64, err error)
// InsertBlockData stores a block hash and its associated data block
@ -119,8 +121,10 @@ type Db interface {
// NewIterateBlocks returns an iterator for all blocks in database.
NewIterateBlocks() (pbi BlockIterator, err error)
// NewestSha provides an interface to quickly look up the hash of
// the most recent (end) block of the block chain.
// NewestSha returns the hash and block height of the most recent (end)
// block of the block chain. It will return the zero hash, -1 for
// the block height, and no error (nil) if there are not any blocks in
// the database yet.
NewestSha() (sha *btcwire.ShaHash, height int64, err error)
// RollbackClose discards the recent database changes to the previously

16
doc.go
View file

@ -21,18 +21,22 @@ although a block can have a variable number of transactions. Along
with these two items, several convenience functions for dealing with
the database are provided as well as functions to query specific items
that may be present in a block or tx (although many of these are in
the db_sqlite subpackage).
the sqlite3 subpackage).
Usage
At the highest level, the use of this packages just requires that you
import it, setup a database, insert some data into it, and optionally,
query the data back. In a more concrete example:
query the data back. The first block inserted into the database will be
treated as the genesis block. Every subsequent block insert requires the
referenced parent block to already exist. In a more concrete example:
// Import packages.
import (
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/sqlite3"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
)
// Create a database and schedule it to be closed on exit.
@ -43,13 +47,13 @@ query the data back. In a more concrete example:
}
defer db.Close()
// Insert a block.
// Insert the main network genesis block.
pver := btcwire.ProtocolVersion
genesis := btcutil.NewBlock(&btcwire.GenesisBlock, pver)
newHeight, err := db.InsertBlock(block)
if err != nil {
// Log and handle the error
}
// Sync the database.
db.Sync()
*/
package btcdb

View file

@ -76,7 +76,7 @@ func testUnspentInsert(t *testing.T, mode int) {
blocks := loadblocks(t)
endtest:
for height := int64(1); height < int64(len(blocks)); height++ {
for height := int64(0); height < int64(len(blocks)); height++ {
block := blocks[height]
// look up inputs to this x

View file

@ -81,7 +81,7 @@ func testOperationalMode(t *testing.T, mode int) {
err = nil
out:
for height := int64(1); height < int64(len(blocks)); height++ {
for height := int64(0); height < int64(len(blocks)); height++ {
block := blocks[height]
if mode != dbTmNoVerify {
// except for NoVerify which does not allow lookups check inputs
@ -203,7 +203,7 @@ func testBackout(t *testing.T, mode int) {
}
err = nil
for height := int64(1); height < int64(len(blocks)); height++ {
for height := int64(0); height < int64(len(blocks)); height++ {
if height == 100 {
t.Logf("Syncing at block height 100")
db.Sync()
@ -305,10 +305,11 @@ func loadBlocks(t *testing.T, file string) (blocks []*btcutil.Block, err error)
}
}()
var block *btcutil.Block
// block 0 isn't really there, put in nil
blocks = append(blocks, block)
// Set the first block as the genesis block.
genesis := btcutil.NewBlock(&btcwire.GenesisBlock, btcwire.ProtocolVersion)
blocks = append(blocks, genesis)
var block *btcutil.Block
err = nil
for height := int64(1); err == nil; height++ {
var rintbuf uint32

View file

@ -178,12 +178,6 @@ func createDB(db *sql.DB) error {
}
}
// Insert the genesis block.
err := insertGenesisBlock(db)
if err != nil {
return err
}
return nil
}
@ -610,8 +604,10 @@ func (db *SqliteDb) DropAfterBlockBySha(sha *btcwire.ShaHash) (err error) {
return
}
// InsertBlock inserts the block data and transaction data from a block
// into the database.
// InsertBlock inserts raw block and transaction data from a block into the
// database. The first block inserted into the database will be treated as the
// genesis block. Every subsequent block insert requires the referenced parent
// block to already exist.
func (db *SqliteDb) InsertBlock(block *btcutil.Block) (height int64, err error) {
db.dbLock.Lock()
defer db.dbLock.Unlock()

View file

@ -5,36 +5,12 @@
package sqlite3
import (
"bytes"
"database/sql"
"github.com/conformal/btcdb"
"github.com/conformal/btcwire"
_ "github.com/mattn/go-sqlite3"
)
// insertGenesisBlock inserts the genesis block of the block chain into the
// database.
func insertGenesisBlock(db *sql.DB) error {
// Encode the genesis block to raw bytes.
pver := uint32(btcwire.ProtocolVersion)
var buf bytes.Buffer
err := btcwire.GenesisBlock.BtcEncode(&buf, pver)
if err != nil {
return err
}
// Insert the genesis block along with its hash and protocol encoding
// version.
sql := blkqueries[blkInsertSha]
sha := btcwire.GenesisHash
_, err = db.Exec(sql, sha.Bytes(), pver, buf.Bytes())
if err != nil {
return err
}
return nil
}
// InsertBlockData stores a block hash and its associated data block with a
// previous sha of `prevSha' and a version of `pver'.
func (db *SqliteDb) InsertBlockData(sha *btcwire.ShaHash, prevSha *btcwire.ShaHash, pver uint32, buf []byte) (blockid int64, err error) {
@ -56,20 +32,26 @@ func (db *SqliteDb) insertBlockData(sha *btcwire.ShaHash, prevSha *btcwire.ShaHa
}
}
var prevOk bool
var blkid int64
prevOk = db.blkExistsSha(prevSha) // exists -> ok
if !prevOk {
// It is an error if the previous block does not already exist in the
// database, unless there are no blocks at all.
if prevOk := db.blkExistsSha(prevSha); !prevOk {
var numBlocks uint64
querystr := "SELECT COUNT(blockid) FROM block;"
err := db.sqldb.QueryRow(querystr).Scan(&numBlocks)
if err != nil {
return 0, err
}
if numBlocks != 0 {
return 0, btcdb.PrevShaMissing
}
}
result, err := db.blkStmts[blkInsertSha].Exec(sha.Bytes(), pver, buf)
if err != nil {
return
}
blkid, err = result.LastInsertId()
blkid, err := result.LastInsertId()
if err != nil {
return 0, err
}
@ -218,8 +200,9 @@ func (db *SqliteDb) FetchHeightRange(startHeight, endHeight int64) (rshalist []b
return
}
// NewestSha provides an interface to quickly look up the sha of
// the most recent (end) of the block chain.
// NewestSha returns the hash and block height of the most recent (end) block of
// the block chain. It will return the zero hash, -1 for the block height, and
// no error (nil) if there are not any blocks in the database yet.
func (db *SqliteDb) NewestSha() (sha *btcwire.ShaHash, blkid int64, err error) {
var row *sql.Row
var blockidx int64
@ -245,6 +228,9 @@ func (db *SqliteDb) NewestSha() (sha *btcwire.ShaHash, blkid int64, err error) {
var shabytes []byte
err = row.Scan(&shabytes, &blockidx)
if err == sql.ErrNoRows {
return &btcwire.ShaHash{}, -1, nil
}
if err == nil {
var retsha btcwire.ShaHash
retsha.SetBytes(shabytes)