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:
parent
584481280c
commit
25684a2ccb
7 changed files with 78 additions and 66 deletions
25
README.md
25
README.md
|
@ -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
10
db.go
|
@ -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
16
doc.go
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue