Add funcs to Block API for wrapped transactions.

This commit adds two new functions to the Block API for working with the
recently added Tx type from the Block.

These new functions are named Tx and Transactions.  Tx returns a
transactions for the specified index as a Tx and also memoizes it so
subsequent calls are more efficient.  Transactions returns a slice of all
transactions in the Block wrapped in a Tx.

This is part of the ongoing transaction hash optimization effort
noted in conformal/btcd#25.
This commit is contained in:
Dave Collins 2013-10-28 10:32:41 -05:00
parent 29f1bf4ae1
commit 8e97f32e68

View file

@ -35,6 +35,8 @@ type Block struct {
blockHeight int64 // Height in the main block chain blockHeight int64 // Height in the main block chain
txShas []*btcwire.ShaHash // Cached transaction hashes txShas []*btcwire.ShaHash // Cached transaction hashes
txShasGenerated bool // ALL transaction hashes generated txShasGenerated bool // ALL transaction hashes generated
transactions []*Tx // Transactions
txnsGenerated bool // ALL wrapped transactions generated
} }
// MsgBlock returns the underlying btcwire.MsgBlock for the Block. // MsgBlock returns the underlying btcwire.MsgBlock for the Block.
@ -83,12 +85,13 @@ func (b *Block) Sha() (*btcwire.ShaHash, error) {
return &sha, nil return &sha, nil
} }
// TxSha returns the hash for the requested transaction number in the Block. // Tx returns a wrapped transaction (btcutil.Tx) for the transaction at the
// The supplied index is 0 based. That is to say, the first transaction is the // specified index in the Block. The supplied index is 0 based. That is to
// block is txNum 0. This is equivalent to calling TxSha on the underlying // say, the first transaction in the block is txNum 0. This is nearly
// btcwire.MsgTx, however it caches the result so subsequent calls are more // equivalent to accessing the raw transaction (btcwire.MsgTx) from the
// efficient. // underlying btcwire.MsgBlock, however the wrapped transaction has some helpful
func (b *Block) TxSha(txNum int) (*btcwire.ShaHash, error) { // properties such as caching the hash so subsequent calls are more efficient.
func (b *Block) Tx(txNum int) (*Tx, error) {
// Ensure the requested transaction is in range. // Ensure the requested transaction is in range.
numTx := b.msgBlock.Header.TxnCount numTx := b.msgBlock.Header.TxnCount
if txNum < 0 || uint64(txNum) > numTx { if txNum < 0 || uint64(txNum) > numTx {
@ -97,28 +100,80 @@ func (b *Block) TxSha(txNum int) (*btcwire.ShaHash, error) {
return nil, OutOfRangeError(str) return nil, OutOfRangeError(str)
} }
// Generate slice to hold all of the transaction hashes if needed. // Generate slice to hold all of the wrapped transactions if needed.
if len(b.txShas) == 0 { if len(b.transactions) == 0 {
b.txShas = make([]*btcwire.ShaHash, numTx) b.transactions = make([]*Tx, numTx)
} }
// Return the cached hash if it has already been generated. // Return the wrapped transaction if it has already been generated.
if b.txShas[txNum] != nil { if b.transactions[txNum] != nil {
return b.txShas[txNum], nil return b.transactions[txNum], nil
} }
// Generate the hash for the transaction. Ignore the error since TxSha // Generate and cache the wrapped transaction and return it.
// can't currently fail. newTx := NewTx(b.msgBlock.Transactions[txNum])
sha, _ := b.msgBlock.Transactions[txNum].TxSha() newTx.SetIndex(txNum)
b.transactions[txNum] = newTx
return newTx, nil
}
// Cache the transaction hash and return it. // Transactions returns a slice of wrapped transactions (btcutil.Tx) for all
b.txShas[txNum] = &sha // transactions in the Block. This is nearly equivalent to accessing the raw
return &sha, nil // transactions (btcwire.MsgTx) in the underlying btcwire.MsgBlock, however it
// instead provides easy access to wrapped versions (btcutil.Tx) of them.
func (b *Block) Transactions() []*Tx {
// Return transactions if they have ALL already been generated. This
// flag is necessary because the wrapped transactions are lazily
// generated in a sparse fashion.
if b.txnsGenerated {
return b.transactions
}
// Generate slice to hold all of the wrapped transactions if needed.
if len(b.transactions) == 0 {
b.transactions = make([]*Tx, b.msgBlock.Header.TxnCount)
}
// Generate and cache the wrapped transactions for all that haven't
// already been done.
for i, tx := range b.transactions {
if tx == nil {
newTx := NewTx(b.msgBlock.Transactions[i])
newTx.SetIndex(i)
b.transactions[i] = newTx
}
}
b.txnsGenerated = true
return b.transactions
}
// TxSha returns the hash for the requested transaction number in the Block.
// The supplied index is 0 based. That is to say, the first transaction in the
// block is txNum 0. This is equivalent to calling TxSha on the underlying
// btcwire.MsgTx, however it caches the result so subsequent calls are more
// efficient.
func (b *Block) TxSha(txNum int) (*btcwire.ShaHash, error) {
// Attempt to get a wrapped transaction for the specified index. It
// will be created lazily if needed or simply return the cached version
// if it has already been generated.
tx, err := b.Tx(txNum)
if err != nil {
return nil, err
}
// Defer to the wrapped transaction which will return the cached hash if
// it has already been generated.
return tx.Sha(), nil
} }
// TxShas returns a slice of hashes for all transactions in the Block. This is // TxShas returns a slice of hashes for all transactions in the Block. This is
// equivalent to calling TxSha on each underlying btcwire.MsgTx, however it // equivalent to calling TxSha on each underlying btcwire.MsgTx, however it
// caches the result so subsequent calls are more efficient. // caches the result so subsequent calls are more efficient.
//
// DEPRECATED - This function will be removed in the next version and
// should not be used. Instead, use Transactions() and .Sha() on each
// transaction.
func (b *Block) TxShas() ([]*btcwire.ShaHash, error) { func (b *Block) TxShas() ([]*btcwire.ShaHash, error) {
// Return cached hashes if they have ALL already been generated. This // Return cached hashes if they have ALL already been generated. This
// flag is necessary because the transaction hashes are lazily generated // flag is necessary because the transaction hashes are lazily generated
@ -136,9 +191,11 @@ func (b *Block) TxShas() ([]*btcwire.ShaHash, error) {
// been done. // been done.
for i, hash := range b.txShas { for i, hash := range b.txShas {
if hash == nil { if hash == nil {
// Ignore the error since TxSha can't currently fail. // Ignore the errors since the only way these can fail
sha, _ := b.msgBlock.Transactions[i].TxSha() // is if the index is out of range which is not possible
b.txShas[i] = &sha // here due to the range.
tx, _ := b.Tx(i)
b.txShas[i] = tx.Sha()
} }
} }