blockchain: publicly export spentTxOut and all attributes

In this commit, we publicly export the spentTxOut struct and all its
attributes. This is the first in a set of commits to optimize the
existing address index by using the spend journal rather than manually
re-creating the utxoViewPoint each time.
This commit is contained in:
Olaoluwa Osuntokun 2018-05-28 21:13:59 -07:00
parent 4bd5b1a43a
commit e4d82bd6e2
5 changed files with 95 additions and 86 deletions

View file

@ -557,7 +557,9 @@ func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List
// it would be inefficient to repeat it. // it would be inefficient to repeat it.
// //
// This function MUST be called with the chain state lock held (for writes). // This function MUST be called with the chain state lock held (for writes).
func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos []spentTxOut) error { func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block,
view *UtxoViewpoint, stxos []SpentTxOut) error {
// Make sure it's extending the end of the best chain. // Make sure it's extending the end of the best chain.
prevHash := &block.MsgBlock().Header.PrevBlock prevHash := &block.MsgBlock().Header.PrevBlock
if !prevHash.IsEqual(&b.bestChain.Tip().hash) { if !prevHash.IsEqual(&b.bestChain.Tip().hash) {
@ -816,7 +818,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
// then they are needed again when doing the actual database updates. // then they are needed again when doing the actual database updates.
// Rather than doing two loads, cache the loaded data into these slices. // Rather than doing two loads, cache the loaded data into these slices.
detachBlocks := make([]*btcutil.Block, 0, detachNodes.Len()) detachBlocks := make([]*btcutil.Block, 0, detachNodes.Len())
detachSpentTxOuts := make([][]spentTxOut, 0, detachNodes.Len()) detachSpentTxOuts := make([][]SpentTxOut, 0, detachNodes.Len())
attachBlocks := make([]*btcutil.Block, 0, attachNodes.Len()) attachBlocks := make([]*btcutil.Block, 0, attachNodes.Len())
// Disconnect all of the blocks back to the point of the fork. This // Disconnect all of the blocks back to the point of the fork. This
@ -846,7 +848,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
// Load all of the spent txos for the block from the spend // Load all of the spent txos for the block from the spend
// journal. // journal.
var stxos []spentTxOut var stxos []SpentTxOut
err = b.db.View(func(dbTx database.Tx) error { err = b.db.View(func(dbTx database.Tx) error {
stxos, err = dbFetchSpendJournalEntry(dbTx, block) stxos, err = dbFetchSpendJournalEntry(dbTx, block)
return err return err
@ -990,7 +992,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
// as spent and add all transactions being created by this block // as spent and add all transactions being created by this block
// to it. Also, provide an stxo slice so the spent txout // to it. Also, provide an stxo slice so the spent txout
// details are generated. // details are generated.
stxos := make([]spentTxOut, 0, countSpentOutputs(block)) stxos := make([]SpentTxOut, 0, countSpentOutputs(block))
err = view.connectTransactions(block, &stxos) err = view.connectTransactions(block, &stxos)
if err != nil { if err != nil {
return err return err
@ -1044,7 +1046,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
// actually connecting the block. // actually connecting the block.
view := NewUtxoViewpoint() view := NewUtxoViewpoint()
view.SetBestHash(parentHash) view.SetBestHash(parentHash)
stxos := make([]spentTxOut, 0, countSpentOutputs(block)) stxos := make([]SpentTxOut, 0, countSpentOutputs(block))
if !fastAdd { if !fastAdd {
err := b.checkConnectBlock(node, block, view, &stxos) err := b.checkConnectBlock(node, block, view, &stxos)
if err == nil { if err == nil {

View file

@ -232,18 +232,25 @@ func dbFetchOrCreateVersion(dbTx database.Tx, key []byte, defaultVersion uint32)
// - 0xb2...ec: pubkey hash // - 0xb2...ec: pubkey hash
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// spentTxOut contains a spent transaction output and potentially additional // SpentTxOut contains a spent transaction output and potentially additional
// contextual information such as whether or not it was contained in a coinbase // contextual information such as whether or not it was contained in a coinbase
// transaction, the version of the transaction it was contained in, and which // transaction, the version of the transaction it was contained in, and which
// block height the containing transaction was included in. As described in // block height the containing transaction was included in. As described in
// the comments above, the additional contextual information will only be valid // the comments above, the additional contextual information will only be valid
// when this spent txout is spending the last unspent output of the containing // when this spent txout is spending the last unspent output of the containing
// transaction. // transaction.
type spentTxOut struct { type SpentTxOut struct {
amount int64 // The amount of the output. // Amount is the amount of the output.
pkScript []byte // The public key script for the output. Amount int64
height int32 // Height of the the block containing the creating tx.
isCoinBase bool // Whether creating tx is a coinbase. // PkScipt is the the public key script for the output.
PkScript []byte
// Height is the height of the the block containing the creating tx.
Height int32
// Denotes if the creating tx is a coinbase.
IsCoinBase bool
} }
// FetchSpendJournal attempts to retrieve the spend journal, or the set of // FetchSpendJournal attempts to retrieve the spend journal, or the set of
@ -272,12 +279,12 @@ func (b *BlockChain) FetchSpendJournal(targetBlock *btcutil.Block) ([]SpentTxOut
// spentTxOutHeaderCode returns the calculated header code to be used when // spentTxOutHeaderCode returns the calculated header code to be used when
// serializing the provided stxo entry. // serializing the provided stxo entry.
func spentTxOutHeaderCode(stxo *spentTxOut) uint64 { func spentTxOutHeaderCode(stxo *SpentTxOut) uint64 {
// As described in the serialization format comments, the header code // As described in the serialization format comments, the header code
// encodes the height shifted over one bit and the coinbase flag in the // encodes the height shifted over one bit and the coinbase flag in the
// lowest bit. // lowest bit.
headerCode := uint64(stxo.height) << 1 headerCode := uint64(stxo.Height) << 1
if stxo.isCoinBase { if stxo.IsCoinBase {
headerCode |= 0x01 headerCode |= 0x01
} }
@ -286,38 +293,38 @@ func spentTxOutHeaderCode(stxo *spentTxOut) uint64 {
// spentTxOutSerializeSize returns the number of bytes it would take to // spentTxOutSerializeSize returns the number of bytes it would take to
// serialize the passed stxo according to the format described above. // serialize the passed stxo according to the format described above.
func spentTxOutSerializeSize(stxo *spentTxOut) int { func spentTxOutSerializeSize(stxo *SpentTxOut) int {
size := serializeSizeVLQ(spentTxOutHeaderCode(stxo)) size := serializeSizeVLQ(spentTxOutHeaderCode(stxo))
if stxo.height > 0 { if stxo.Height > 0 {
// The legacy v1 spend journal format conditionally tracked the // The legacy v1 spend journal format conditionally tracked the
// containing transaction version when the height was non-zero, // containing transaction version when the height was non-zero,
// so this is required for backwards compat. // so this is required for backwards compat.
size += serializeSizeVLQ(0) size += serializeSizeVLQ(0)
} }
return size + compressedTxOutSize(uint64(stxo.amount), stxo.pkScript) return size + compressedTxOutSize(uint64(stxo.Amount), stxo.PkScript)
} }
// putSpentTxOut serializes the passed stxo according to the format described // putSpentTxOut serializes the passed stxo according to the format described
// above directly into the passed target byte slice. The target byte slice must // above directly into the passed target byte slice. The target byte slice must
// be at least large enough to handle the number of bytes returned by the // be at least large enough to handle the number of bytes returned by the
// spentTxOutSerializeSize function or it will panic. // SpentTxOutSerializeSize function or it will panic.
func putSpentTxOut(target []byte, stxo *spentTxOut) int { func putSpentTxOut(target []byte, stxo *SpentTxOut) int {
headerCode := spentTxOutHeaderCode(stxo) headerCode := spentTxOutHeaderCode(stxo)
offset := putVLQ(target, headerCode) offset := putVLQ(target, headerCode)
if stxo.height > 0 { if stxo.Height > 0 {
// The legacy v1 spend journal format conditionally tracked the // The legacy v1 spend journal format conditionally tracked the
// containing transaction version when the height was non-zero, // containing transaction version when the height was non-zero,
// so this is required for backwards compat. // so this is required for backwards compat.
offset += putVLQ(target[offset:], 0) offset += putVLQ(target[offset:], 0)
} }
return offset + putCompressedTxOut(target[offset:], uint64(stxo.amount), return offset + putCompressedTxOut(target[offset:], uint64(stxo.Amount),
stxo.pkScript) stxo.PkScript)
} }
// decodeSpentTxOut decodes the passed serialized stxo entry, possibly followed // decodeSpentTxOut decodes the passed serialized stxo entry, possibly followed
// by other data, into the passed stxo struct. It returns the number of bytes // by other data, into the passed stxo struct. It returns the number of bytes
// read. // read.
func decodeSpentTxOut(serialized []byte, stxo *spentTxOut) (int, error) { func decodeSpentTxOut(serialized []byte, stxo *SpentTxOut) (int, error) {
// Ensure there are bytes to decode. // Ensure there are bytes to decode.
if len(serialized) == 0 { if len(serialized) == 0 {
return 0, errDeserialize("no serialized bytes") return 0, errDeserialize("no serialized bytes")
@ -334,9 +341,9 @@ func decodeSpentTxOut(serialized []byte, stxo *spentTxOut) (int, error) {
// //
// Bit 0 indicates containing transaction is a coinbase. // Bit 0 indicates containing transaction is a coinbase.
// Bits 1-x encode height of containing transaction. // Bits 1-x encode height of containing transaction.
stxo.isCoinBase = code&0x01 != 0 stxo.IsCoinBase = code&0x01 != 0
stxo.height = int32(code >> 1) stxo.Height = int32(code >> 1)
if stxo.height > 0 { if stxo.Height > 0 {
// The legacy v1 spend journal format conditionally tracked the // The legacy v1 spend journal format conditionally tracked the
// containing transaction version when the height was non-zero, // containing transaction version when the height was non-zero,
// so this is required for backwards compat. // so this is required for backwards compat.
@ -356,8 +363,8 @@ func decodeSpentTxOut(serialized []byte, stxo *spentTxOut) (int, error) {
return offset, errDeserialize(fmt.Sprintf("unable to decode "+ return offset, errDeserialize(fmt.Sprintf("unable to decode "+
"txout: %v", err)) "txout: %v", err))
} }
stxo.amount = int64(amount) stxo.Amount = int64(amount)
stxo.pkScript = pkScript stxo.PkScript = pkScript
return offset, nil return offset, nil
} }
@ -367,7 +374,7 @@ func decodeSpentTxOut(serialized []byte, stxo *spentTxOut) (int, error) {
// Since the serialization format is not self describing, as noted in the // Since the serialization format is not self describing, as noted in the
// format comments, this function also requires the transactions that spend the // format comments, this function also requires the transactions that spend the
// txouts. // txouts.
func deserializeSpendJournalEntry(serialized []byte, txns []*wire.MsgTx) ([]spentTxOut, error) { func deserializeSpendJournalEntry(serialized []byte, txns []*wire.MsgTx) ([]SpentTxOut, error) {
// Calculate the total number of stxos. // Calculate the total number of stxos.
var numStxos int var numStxos int
for _, tx := range txns { for _, tx := range txns {
@ -392,7 +399,7 @@ func deserializeSpendJournalEntry(serialized []byte, txns []*wire.MsgTx) ([]spen
// reverse order to match the serialization order. // reverse order to match the serialization order.
stxoIdx := numStxos - 1 stxoIdx := numStxos - 1
offset := 0 offset := 0
stxos := make([]spentTxOut, numStxos) stxos := make([]SpentTxOut, numStxos)
for txIdx := len(txns) - 1; txIdx > -1; txIdx-- { for txIdx := len(txns) - 1; txIdx > -1; txIdx-- {
tx := txns[txIdx] tx := txns[txIdx]
@ -418,7 +425,7 @@ func deserializeSpendJournalEntry(serialized []byte, txns []*wire.MsgTx) ([]spen
// serializeSpendJournalEntry serializes all of the passed spent txouts into a // serializeSpendJournalEntry serializes all of the passed spent txouts into a
// single byte slice according to the format described in detail above. // single byte slice according to the format described in detail above.
func serializeSpendJournalEntry(stxos []spentTxOut) []byte { func serializeSpendJournalEntry(stxos []SpentTxOut) []byte {
if len(stxos) == 0 { if len(stxos) == 0 {
return nil return nil
} }
@ -446,7 +453,7 @@ func serializeSpendJournalEntry(stxos []spentTxOut) []byte {
// NOTE: Legacy entries will not have the coinbase flag or height set unless it // NOTE: Legacy entries will not have the coinbase flag or height set unless it
// was the final output spend in the containing transaction. It is up to the // was the final output spend in the containing transaction. It is up to the
// caller to handle this properly by looking the information up in the utxo set. // caller to handle this properly by looking the information up in the utxo set.
func dbFetchSpendJournalEntry(dbTx database.Tx, block *btcutil.Block) ([]spentTxOut, error) { func dbFetchSpendJournalEntry(dbTx database.Tx, block *btcutil.Block) ([]SpentTxOut, error) {
// Exclude the coinbase transaction since it can't spend anything. // Exclude the coinbase transaction since it can't spend anything.
spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName) spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
serialized := spendBucket.Get(block.Hash()[:]) serialized := spendBucket.Get(block.Hash()[:])
@ -474,7 +481,7 @@ func dbFetchSpendJournalEntry(dbTx database.Tx, block *btcutil.Block) ([]spentTx
// spend journal entry for the given block hash using the provided slice of // spend journal entry for the given block hash using the provided slice of
// spent txouts. The spent txouts slice must contain an entry for every txout // spent txouts. The spent txouts slice must contain an entry for every txout
// the transactions in the block spend in the order they are spent. // the transactions in the block spend in the order they are spent.
func dbPutSpendJournalEntry(dbTx database.Tx, blockHash *chainhash.Hash, stxos []spentTxOut) error { func dbPutSpendJournalEntry(dbTx database.Tx, blockHash *chainhash.Hash, stxos []SpentTxOut) error {
spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName) spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
serialized := serializeSpendJournalEntry(stxos) serialized := serializeSpendJournalEntry(stxos)
return spendBucket.Put(blockHash[:], serialized) return spendBucket.Put(blockHash[:], serialized)

View file

@ -44,37 +44,37 @@ func TestStxoSerialization(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
stxo spentTxOut stxo SpentTxOut
serialized []byte serialized []byte
}{ }{
// From block 170 in main blockchain. // From block 170 in main blockchain.
{ {
name: "Spends last output of coinbase", name: "Spends last output of coinbase",
stxo: spentTxOut{ stxo: SpentTxOut{
amount: 5000000000, Amount: 5000000000,
pkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"), PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
isCoinBase: true, IsCoinBase: true,
height: 9, Height: 9,
}, },
serialized: hexToBytes("1300320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"), serialized: hexToBytes("1300320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
}, },
// Adapted from block 100025 in main blockchain. // Adapted from block 100025 in main blockchain.
{ {
name: "Spends last output of non coinbase", name: "Spends last output of non coinbase",
stxo: spentTxOut{ stxo: SpentTxOut{
amount: 13761000000, Amount: 13761000000,
pkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"), PkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
isCoinBase: false, IsCoinBase: false,
height: 100024, Height: 100024,
}, },
serialized: hexToBytes("8b99700086c64700b2fb57eadf61e106a100a7445a8c3f67898841ec"), serialized: hexToBytes("8b99700086c64700b2fb57eadf61e106a100a7445a8c3f67898841ec"),
}, },
// Adapted from block 100025 in main blockchain. // Adapted from block 100025 in main blockchain.
{ {
name: "Does not spend last output, legacy format", name: "Does not spend last output, legacy format",
stxo: spentTxOut{ stxo: SpentTxOut{
amount: 34405000000, Amount: 34405000000,
pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"), PkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
}, },
serialized: hexToBytes("0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"), serialized: hexToBytes("0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"),
}, },
@ -85,7 +85,7 @@ func TestStxoSerialization(t *testing.T) {
// actually serializing it is calculated properly. // actually serializing it is calculated properly.
gotSize := spentTxOutSerializeSize(&test.stxo) gotSize := spentTxOutSerializeSize(&test.stxo)
if gotSize != len(test.serialized) { if gotSize != len(test.serialized) {
t.Errorf("spentTxOutSerializeSize (%s): did not get "+ t.Errorf("SpentTxOutSerializeSize (%s): did not get "+
"expected size - got %d, want %d", test.name, "expected size - got %d, want %d", test.name,
gotSize, len(test.serialized)) gotSize, len(test.serialized))
continue continue
@ -110,7 +110,7 @@ func TestStxoSerialization(t *testing.T) {
// Ensure the serialized bytes are decoded back to the expected // Ensure the serialized bytes are decoded back to the expected
// stxo. // stxo.
var gotStxo spentTxOut var gotStxo SpentTxOut
gotBytesRead, err := decodeSpentTxOut(test.serialized, &gotStxo) gotBytesRead, err := decodeSpentTxOut(test.serialized, &gotStxo)
if err != nil { if err != nil {
t.Errorf("decodeSpentTxOut (%s): unexpected error: %v", t.Errorf("decodeSpentTxOut (%s): unexpected error: %v",
@ -138,42 +138,42 @@ func TestStxoDecodeErrors(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
stxo spentTxOut stxo SpentTxOut
serialized []byte serialized []byte
bytesRead int // Expected number of bytes read. bytesRead int // Expected number of bytes read.
errType error errType error
}{ }{
{ {
name: "nothing serialized", name: "nothing serialized",
stxo: spentTxOut{}, stxo: SpentTxOut{},
serialized: hexToBytes(""), serialized: hexToBytes(""),
errType: errDeserialize(""), errType: errDeserialize(""),
bytesRead: 0, bytesRead: 0,
}, },
{ {
name: "no data after header code w/o reserved", name: "no data after header code w/o reserved",
stxo: spentTxOut{}, stxo: SpentTxOut{},
serialized: hexToBytes("00"), serialized: hexToBytes("00"),
errType: errDeserialize(""), errType: errDeserialize(""),
bytesRead: 1, bytesRead: 1,
}, },
{ {
name: "no data after header code with reserved", name: "no data after header code with reserved",
stxo: spentTxOut{}, stxo: SpentTxOut{},
serialized: hexToBytes("13"), serialized: hexToBytes("13"),
errType: errDeserialize(""), errType: errDeserialize(""),
bytesRead: 1, bytesRead: 1,
}, },
{ {
name: "no data after reserved", name: "no data after reserved",
stxo: spentTxOut{}, stxo: SpentTxOut{},
serialized: hexToBytes("1300"), serialized: hexToBytes("1300"),
errType: errDeserialize(""), errType: errDeserialize(""),
bytesRead: 2, bytesRead: 2,
}, },
{ {
name: "incomplete compressed txout", name: "incomplete compressed txout",
stxo: spentTxOut{}, stxo: SpentTxOut{},
serialized: hexToBytes("1332"), serialized: hexToBytes("1332"),
errType: errDeserialize(""), errType: errDeserialize(""),
bytesRead: 2, bytesRead: 2,
@ -208,7 +208,7 @@ func TestSpendJournalSerialization(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
entry []spentTxOut entry []SpentTxOut
blockTxns []*wire.MsgTx blockTxns []*wire.MsgTx
serialized []byte serialized []byte
}{ }{
@ -222,11 +222,11 @@ func TestSpendJournalSerialization(t *testing.T) {
// From block 170 in main blockchain. // From block 170 in main blockchain.
{ {
name: "One tx with one input spends last output of coinbase", name: "One tx with one input spends last output of coinbase",
entry: []spentTxOut{{ entry: []SpentTxOut{{
amount: 5000000000, Amount: 5000000000,
pkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"), PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
isCoinBase: true, IsCoinBase: true,
height: 9, Height: 9,
}}, }},
blockTxns: []*wire.MsgTx{{ // Coinbase omitted. blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
Version: 1, Version: 1,
@ -252,16 +252,16 @@ func TestSpendJournalSerialization(t *testing.T) {
// Adapted from block 100025 in main blockchain. // Adapted from block 100025 in main blockchain.
{ {
name: "Two txns when one spends last output, one doesn't", name: "Two txns when one spends last output, one doesn't",
entry: []spentTxOut{{ entry: []SpentTxOut{{
amount: 34405000000, Amount: 34405000000,
pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"), PkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
isCoinBase: false, IsCoinBase: false,
height: 100024, Height: 100024,
}, { }, {
amount: 13761000000, Amount: 13761000000,
pkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"), PkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
isCoinBase: false, IsCoinBase: false,
height: 100024, Height: 100024,
}}, }},
blockTxns: []*wire.MsgTx{{ // Coinbase omitted. blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
Version: 1, Version: 1,

View file

@ -216,7 +216,7 @@ func (view *UtxoViewpoint) AddTxOuts(tx *btcutil.Tx, blockHeight int32) {
// spent. In addition, when the 'stxos' argument is not nil, it will be updated // spent. In addition, when the 'stxos' argument is not nil, it will be updated
// to append an entry for each spent txout. An error will be returned if the // to append an entry for each spent txout. An error will be returned if the
// view does not contain the required utxos. // view does not contain the required utxos.
func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32, stxos *[]spentTxOut) error { func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32, stxos *[]SpentTxOut) error {
// Coinbase transactions don't have any inputs to spend. // Coinbase transactions don't have any inputs to spend.
if IsCoinBase(tx) { if IsCoinBase(tx) {
// Add the transaction's outputs as available utxos. // Add the transaction's outputs as available utxos.
@ -239,11 +239,11 @@ func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32,
// Only create the stxo details if requested. // Only create the stxo details if requested.
if stxos != nil { if stxos != nil {
// Populate the stxo details using the utxo entry. // Populate the stxo details using the utxo entry.
var stxo = spentTxOut{ var stxo = SpentTxOut{
amount: entry.Amount(), Amount: entry.Amount(),
pkScript: entry.PkScript(), PkScript: entry.PkScript(),
height: entry.BlockHeight(), Height: entry.BlockHeight(),
isCoinBase: entry.IsCoinBase(), IsCoinBase: entry.IsCoinBase(),
} }
*stxos = append(*stxos, stxo) *stxos = append(*stxos, stxo)
} }
@ -264,7 +264,7 @@ func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32,
// spend as spent, and setting the best hash for the view to the passed block. // spend as spent, and setting the best hash for the view to the passed block.
// In addition, when the 'stxos' argument is not nil, it will be updated to // In addition, when the 'stxos' argument is not nil, it will be updated to
// append an entry for each spent txout. // append an entry for each spent txout.
func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]spentTxOut) error { func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]SpentTxOut) error {
for _, tx := range block.Transactions() { for _, tx := range block.Transactions() {
err := view.connectTransaction(tx, block.Height(), stxos) err := view.connectTransaction(tx, block.Height(), stxos)
if err != nil { if err != nil {
@ -308,7 +308,7 @@ func (view *UtxoViewpoint) fetchEntryByHash(db database.DB, hash *chainhash.Hash
// created by the passed block, restoring all utxos the transactions spent by // created by the passed block, restoring all utxos the transactions spent by
// using the provided spent txo information, and setting the best hash for the // using the provided spent txo information, and setting the best hash for the
// view to the block before the passed block. // view to the block before the passed block.
func (view *UtxoViewpoint) disconnectTransactions(db database.DB, block *btcutil.Block, stxos []spentTxOut) error { func (view *UtxoViewpoint) disconnectTransactions(db database.DB, block *btcutil.Block, stxos []SpentTxOut) error {
// Sanity check the correct number of stxos are provided. // Sanity check the correct number of stxos are provided.
if len(stxos) != countSpentOutputs(block) { if len(stxos) != countSpentOutputs(block) {
return AssertError("disconnectTransactions called with bad " + return AssertError("disconnectTransactions called with bad " +
@ -405,7 +405,7 @@ func (view *UtxoViewpoint) disconnectTransactions(db database.DB, block *btcutil
// connected. In the case of a fresh database that has // connected. In the case of a fresh database that has
// only ever run with the new v2 format, this code path // only ever run with the new v2 format, this code path
// will never run. // will never run.
if stxo.height == 0 { if stxo.Height == 0 {
utxo, err := view.fetchEntryByHash(db, txHash) utxo, err := view.fetchEntryByHash(db, txHash)
if err != nil { if err != nil {
return err return err
@ -416,17 +416,17 @@ func (view *UtxoViewpoint) disconnectTransactions(db database.DB, block *btcutil
*originOut)) *originOut))
} }
stxo.height = utxo.BlockHeight() stxo.Height = utxo.BlockHeight()
stxo.isCoinBase = utxo.IsCoinBase() stxo.IsCoinBase = utxo.IsCoinBase()
} }
// Restore the utxo using the stxo data from the spend // Restore the utxo using the stxo data from the spend
// journal and mark it as modified. // journal and mark it as modified.
entry.amount = stxo.amount entry.amount = stxo.Amount
entry.pkScript = stxo.pkScript entry.pkScript = stxo.PkScript
entry.blockHeight = stxo.height entry.blockHeight = stxo.Height
entry.packedFlags = tfModified entry.packedFlags = tfModified
if stxo.isCoinBase { if stxo.IsCoinBase {
entry.packedFlags |= tfCoinBase entry.packedFlags |= tfCoinBase
} }
} }

View file

@ -986,7 +986,7 @@ func CheckTransactionInputs(tx *btcutil.Tx, txHeight int32, utxoView *UtxoViewpo
// with that node. // with that node.
// //
// This function MUST be called with the chain state lock held (for writes). // This function MUST be called with the chain state lock held (for writes).
func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos *[]spentTxOut) error { func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos *[]SpentTxOut) error {
// If the side chain blocks end up in the database, a call to // If the side chain blocks end up in the database, a call to
// CheckBlockSanity should be done here in case a previous version // CheckBlockSanity should be done here in case a previous version
// allowed a block that is no longer valid. However, since the // allowed a block that is no longer valid. However, since the