Introduce an API change for btcdb

FetchTxBySha changes what it returns, it can now return a TxListReply and
and error if none are found.

FetchTxByShaList is renamed to FetchUnSpentTxByShaList to indicate that
it will (likey/eventually) only return Tx that have some unspent TxOuts.
Tx which are fully spent may not be (reliably) looked up using this API.
This commit is contained in:
Dale Rahn 2013-10-03 15:21:45 -04:00
parent 07d634c76a
commit cda0b10082
10 changed files with 84 additions and 59 deletions

5
db.go
View file

@ -75,11 +75,11 @@ type Db interface {
// FetchTxBySha returns some data for the given transaction hash. The
// implementation may cache the underlying data if desired.
FetchTxBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rpver uint32, blksha *btcwire.ShaHash, err error)
FetchTxBySha(txsha *btcwire.ShaHash) ([]*TxListReply, error)
// FetchTxByShaList returns a TxListReply given an array of transaction
// hashes. The implementation may cache the underlying data if desired.
FetchTxByShaList(txShaList []*btcwire.ShaHash) []*TxListReply
FetchUnSpentTxByShaList(txShaList []*btcwire.ShaHash) ([]*TxListReply)
// InsertBlock inserts raw block and transaction data from a block
// into the database. The first block inserted into the database
@ -145,6 +145,7 @@ type DriverDB struct {
type TxListReply struct {
Sha *btcwire.ShaHash
Tx *btcwire.MsgTx
BlkSha *btcwire.ShaHash
Height int64
TxSpent []bool
Err error

View file

@ -12,15 +12,6 @@ import (
"github.com/conformal/btcwire"
)
// InsertBlockData stores a block hash and its associated data block with a
// previous sha of `prevSha' and a version of `pver'.
func (db *LevelDb) InsertBlockData(sha *btcwire.ShaHash, prevSha *btcwire.ShaHash, pver uint32, buf []byte) (blockid int64, err error) {
db.dbLock.Lock()
defer db.dbLock.Unlock()
return db.insertBlockData(sha, prevSha, buf)
}
func (db *LevelDb) getBlkLoc(sha *btcwire.ShaHash) (int64, error) {
var blkHeight int64
@ -107,7 +98,7 @@ func (db *LevelDb) setBlk(sha *btcwire.ShaHash, blkHeight int64, buf []byte) err
}
// insertSha stores a block hash and its associated data block with a
// previous sha of `prevSha' and a version of `pver'.
// previous sha of `prevSha'.
// insertSha shall be called with db lock held
func (db *LevelDb) insertBlockData(sha *btcwire.ShaHash, prevSha *btcwire.ShaHash, buf []byte) (blockid int64, err error) {
@ -144,7 +135,7 @@ func (db *LevelDb) insertBlockData(sha *btcwire.ShaHash, prevSha *btcwire.ShaHas
return blkHeight, nil
}
// fetchSha returns the datablock and pver for the given ShaHash.
// fetchSha returns the datablock for the given ShaHash.
func (db *LevelDb) fetchSha(sha *btcwire.ShaHash) (rbuf []byte,
rblkHeight int64, err error) {
var blkHeight int64

View file

@ -36,15 +36,15 @@ func (db *LevelDb) fetchBlockBySha(sha *btcwire.ShaHash) (blk *btcutil.Block, er
return
}
// FetchTxByShaList given a array of ShaHash, look up the transactions
// FetchUnSpentTxByShaList given a array of ShaHash, look up the transactions
// and return them in a TxListReply array.
func (db *LevelDb) FetchTxByShaList(txShaList []*btcwire.ShaHash) []*btcdb.TxListReply {
func (db *LevelDb) FetchUnSpentTxByShaList(txShaList []*btcwire.ShaHash) []*btcdb.TxListReply {
db.dbLock.Lock()
defer db.dbLock.Unlock()
replies := make([]*btcdb.TxListReply, len(txShaList))
for i, txsha := range txShaList {
tx, _, _, _, height, txspent, err := db.fetchTxDataBySha(txsha)
tx, blockSha, height, txspent, err := db.fetchTxDataBySha(txsha)
btxspent := []bool{}
if err == nil {
btxspent = make([]bool, len(tx.TxOut), len(tx.TxOut))
@ -54,15 +54,14 @@ func (db *LevelDb) FetchTxByShaList(txShaList []*btcwire.ShaHash) []*btcdb.TxLis
btxspent[idx] = (txspent[byteidx] & (byte(1) << byteoff)) != 0
}
}
txlre := btcdb.TxListReply{Sha: txsha, Tx: tx, Height: height, TxSpent: btxspent, Err: err}
txlre := btcdb.TxListReply{Sha: txsha, Tx: tx, BlkSha: blockSha, Height: height, TxSpent: btxspent, Err: err}
replies[i] = &txlre
}
return replies
}
// fetchTxDataBySha returns several pieces of data regarding the given sha.
func (db *LevelDb) fetchTxDataBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rtxbuf []byte, rpver uint32, rblksha *btcwire.ShaHash, rheight int64, rtxspent []byte, err error) {
var pver uint32
func (db *LevelDb) fetchTxDataBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rblksha *btcwire.ShaHash, rheight int64, rtxspent []byte, err error) {
var blksha *btcwire.ShaHash
var blkHeight int64
var txspent []byte
@ -83,9 +82,7 @@ func (db *LevelDb) fetchTxDataBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx,
//log.Trace("transaction %v is at block %v %v txoff %v, txlen %v\n",
// txsha, blksha, blkHeight, txOff, txLen)
txbuf := make([]byte, txLen)
copy(txbuf[:], blkbuf[txOff:txOff+txLen])
rbuf := bytes.NewBuffer(txbuf)
rbuf := bytes.NewBuffer(blkbuf[txOff:txOff+txLen])
var tx btcwire.MsgTx
err = tx.Deserialize(rbuf)
@ -95,13 +92,28 @@ func (db *LevelDb) fetchTxDataBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx,
return
}
return &tx, txbuf, pver, blksha, blkHeight, txspent, nil
return &tx, blksha, blkHeight, txspent, nil
}
// FetchTxBySha returns some data for the given Tx Sha.
func (db *LevelDb) FetchTxBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rpver uint32, blksha *btcwire.ShaHash, err error) {
rtx, _, rpver, blksha, _, _, err = db.fetchTxDataBySha(txsha)
return
func (db *LevelDb) FetchTxBySha(txsha *btcwire.ShaHash) ([]*btcdb.TxListReply, error) {
tx, blksha, height, txspent, err := db.fetchTxDataBySha(txsha)
if err != nil {
return []*btcdb.TxListReply{}, err
}
replies := make ([]*btcdb.TxListReply, 1)
btxspent := make([]bool, len(tx.TxOut), len(tx.TxOut))
for idx := range tx.TxOut {
byteidx := idx / 8
byteoff := uint(idx % 8)
btxspent[idx] = (txspent[byteidx] & (byte(1) << byteoff)) != 0
}
txlre := btcdb.TxListReply{Sha: txsha, Tx: tx, BlkSha: blksha, Height: height, TxSpent: btxspent, Err: err}
replies[0] = &txlre
return replies, nil
}
// InvalidateTxCache clear/release all cached transactions.

View file

@ -104,7 +104,7 @@ endtest:
}
txneededmap := map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist := db.FetchTxByShaList(txneededList)
txlist := db.FetchUnSpentTxByShaList(txneededList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)
@ -130,7 +130,7 @@ endtest:
}
txlookupmap := map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist = db.FetchTxByShaList(txlookupList)
txlist = db.FetchUnSpentTxByShaList(txlookupList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)
@ -166,7 +166,7 @@ endtest:
}
txlookupmap = map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist = db.FetchTxByShaList(txlookupList)
txlist = db.FetchUnSpentTxByShaList(txlookupList)
for _, txe := range txlist {
if txe.Err != nil {
if _, ok := txneededmap[*txe.Sha]; ok {
@ -188,7 +188,7 @@ endtest:
break endtest
}
txlookupmap = map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist = db.FetchTxByShaList(txlookupList)
txlist = db.FetchUnSpentTxByShaList(txlookupList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)

View file

@ -27,11 +27,6 @@ const (
var log seelog.LoggerInterface = seelog.Disabled
type tBlockInsertData struct {
sha btcwire.ShaHash
pver uint32
buf []byte
}
type tTxInsertData struct {
txsha *btcwire.ShaHash
blockid int64

View file

@ -93,13 +93,13 @@ out:
t.Errorf("referenced tx not found %v ", origintxsha)
}
_, _, _, err = db.FetchTxBySha(origintxsha)
_, err = db.FetchTxBySha(origintxsha)
if err != nil {
t.Errorf("referenced tx not found %v err %v ", origintxsha, err)
}
}
}
txlist := db.FetchTxByShaList(txneededList)
txlist := db.FetchUnSpentTxByShaList(txneededList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)
@ -260,7 +260,7 @@ func testBackout(t *testing.T, mode int) {
t.Errorf("tx %v not located db\n", txsha)
}
_, _, _, err = db.FetchTxBySha(&txsha)
_, err = db.FetchTxBySha(&txsha)
if err != nil {
t.Errorf("tx %v not located db\n", txsha)
return

View file

@ -84,7 +84,7 @@ out:
txin := tx.TxIn[0]
origintxsha := &txin.PreviousOutpoint.Hash
sqlite3.KillTx(db, origintxsha)
_, _, _, err = db.FetchTxBySha(origintxsha)
_, err = db.FetchTxBySha(origintxsha)
if err == nil {
t.Errorf("deleted tx found %v", origintxsha)
}
@ -107,7 +107,7 @@ out:
if height == 248 {
for _, tx := range mblock.Transactions {
txsha, err := tx.TxSha()
_, _, _, err = db.FetchTxBySha(&txsha)
_, err = db.FetchTxBySha(&txsha)
if err == nil {
t.Errorf("referenced tx found, should not have been %v, ", txsha)
}

View file

@ -107,7 +107,7 @@ endtest:
}
txneededmap := map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist := db.FetchTxByShaList(txneededList)
txlist := db.FetchUnSpentTxByShaList(txneededList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)
@ -133,7 +133,7 @@ endtest:
}
txlookupmap := map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist = db.FetchTxByShaList(txlookupList)
txlist = db.FetchUnSpentTxByShaList(txlookupList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)
@ -169,7 +169,7 @@ endtest:
}
txlookupmap = map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist = db.FetchTxByShaList(txlookupList)
txlist = db.FetchUnSpentTxByShaList(txlookupList)
for _, txe := range txlist {
if txe.Err != nil {
if _, ok := txneededmap[*txe.Sha]; ok {
@ -191,7 +191,7 @@ endtest:
break endtest
}
txlookupmap = map[btcwire.ShaHash]*btcdb.TxListReply{}
txlist = db.FetchTxByShaList(txlookupList)
txlist = db.FetchUnSpentTxByShaList(txlookupList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)

View file

@ -99,13 +99,13 @@ out:
t.Errorf("referenced tx not found %v ", origintxsha)
}
_, _, _, err = db.FetchTxBySha(origintxsha)
_, err = db.FetchTxBySha(origintxsha)
if err != nil {
t.Errorf("referenced tx not found %v err %v ", origintxsha, err)
}
}
}
txlist := db.FetchTxByShaList(txneededList)
txlist := db.FetchUnSpentTxByShaList(txneededList)
for _, txe := range txlist {
if txe.Err != nil {
t.Errorf("tx list fetch failed %v err %v ", txe.Sha, txe.Err)
@ -270,8 +270,8 @@ func testBackout(t *testing.T, mode int) {
t.Errorf("tx %v exists in db, failure expected", txsha)
}
_, _, _, err = db.FetchTxBySha(&txsha)
_, _, _, err = db.FetchTxBySha(&txsha)
_, err = db.FetchTxBySha(&txsha)
_, err = db.FetchTxBySha(&txsha)
}
func loadBlocks(t *testing.T, file string) (blocks []*btcutil.Block, err error) {

View file

@ -131,9 +131,9 @@ func (db *SqliteDb) insertBlockCache(sha *btcwire.ShaHash, blk *btcutil.Block) {
bc.blockMap[blkObj.sha] = &blkObj
}
// FetchTxByShaList given a array of ShaHash, look up the transactions
// FetchUnSpentTxByShaList given a array of ShaHash, look up the transactions
// and return them in a TxListReply array.
func (db *SqliteDb) FetchTxByShaList(txShaList []*btcwire.ShaHash) []*btcdb.TxListReply {
func (db *SqliteDb) FetchUnSpentTxByShaList(txShaList []*btcwire.ShaHash) []*btcdb.TxListReply {
db.dbLock.Lock()
defer db.dbLock.Unlock()
@ -229,30 +229,44 @@ func (db *SqliteDb) fetchTxDataBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx
}
// FetchTxBySha returns several pieces of data regarding the given sha.
func (db *SqliteDb) FetchTxBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rpver uint32, rblksha *btcwire.ShaHash, err error) {
func (db *SqliteDb) FetchTxBySha(txsha *btcwire.ShaHash) ([]*btcdb.TxListReply, error) {
var pver uint32
var blksha *btcwire.ShaHash
var height int64
var toff int
var tlen int
var txspent []byte
var blk *btcutil.Block
var blkbuf []byte
var err error
// Check Tx cache
if txc, ok := db.fetchTxCache(txsha); ok {
return txc.tx, txc.pver, &txc.blksha, nil
replies := make ([]*btcdb.TxListReply, 1)
tx := txc.tx
btxspent := make([]bool, len(tx.TxOut), len(tx.TxOut))
for idx := range tx.TxOut {
byteidx := idx / 8
byteoff := uint(idx % 8)
btxspent[idx] = (txc.spent[byteidx] & (byte(1) << byteoff)) != 0
}
txlre := btcdb.TxListReply{Sha: txsha, Tx: tx, BlkSha: &txc.blksha, Height: txc.height, TxSpent: btxspent, Err: nil}
replies[0] = &txlre
return replies, nil
}
// If not cached load it
height, toff, tlen, err = db.FetchLocationBySha(txsha)
height, toff, tlen, txspent, err = db.fetchLocationUsedBySha(txsha)
if err != nil {
return
return []*btcdb.TxListReply{}, err
}
blksha, err = db.FetchBlockShaByHeight(height)
if err != nil {
log.Warnf("block idx lookup %v to %v", height, err)
return
return []*btcdb.TxListReply{}, err
}
log.Tracef("transaction %v is at block %v %v tx %v",
txsha, blksha, height, toff)
@ -261,13 +275,13 @@ func (db *SqliteDb) FetchTxBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rp
if err != nil {
log.Warnf("unable to fetch block %v %v ",
height, &blksha)
return
return []*btcdb.TxListReply{}, err
}
blkbuf, err = blk.Bytes()
if err != nil {
log.Warnf("unable to decode block %v %v", height, &blksha)
return
return []*btcdb.TxListReply{}, err
}
txbuf := make([]byte, tlen)
@ -279,7 +293,7 @@ func (db *SqliteDb) FetchTxBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rp
if err != nil {
log.Warnf("unable to decode tx block %v %v txoff %v txlen %v",
height, &blksha, toff, tlen)
return
return []*btcdb.TxListReply{}, err
}
// Shove data into TxCache
@ -290,10 +304,22 @@ func (db *SqliteDb) FetchTxBySha(txsha *btcwire.ShaHash) (rtx *btcwire.MsgTx, rp
txc.txbuf = txbuf
txc.pver = pver
txc.height = height
txc.spent = txspent
txc.blksha = *blksha
db.insertTxCache(&txc)
return &tx, pver, blksha, nil
btxspent := make([]bool, len(tx.TxOut), len(tx.TxOut))
for idx := range tx.TxOut {
byteidx := idx / 8
byteoff := uint(idx % 8)
btxspent[idx] = (txspent[byteidx] & (byte(1) << byteoff)) != 0
}
replies := make ([]*btcdb.TxListReply, 1)
txlre := btcdb.TxListReply{Sha: txsha, Tx: &tx, BlkSha: blksha, Height: height, TxSpent: btxspent, Err: err}
replies[0] = &txlre
return replies, nil
}
// fetchTxCache look up the given transaction in the Tx cache.