Skip to content

Commit

Permalink
btcutil: align new serialization caching logic w/ codebase style
Browse files Browse the repository at this point in the history
  • Loading branch information
Roasbeef committed Dec 29, 2023
1 parent 83605e4 commit 56de9ca
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 24 deletions.
30 changes: 22 additions & 8 deletions btcutil/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ func (b *Block) Transactions() []*Tx {
}

// Offset of each tx. 80 accounts for the block header size.
offset := 80 + wire.VarIntSerializeSize(uint64(len(b.msgBlock.Transactions)))
offset := 80 + wire.VarIntSerializeSize(
uint64(len(b.msgBlock.Transactions)),
)

// Generate and cache the wrapped transactions for all that haven't
// already been done.
Expand All @@ -168,12 +170,16 @@ func (b *Block) Transactions() []*Tx {

// The block may not always have the serializedBlock.
if len(b.serializedBlock) > 0 {
// This allows for the reuse of the already serialized tx.
newTx.setBytes(b.serializedBlock[offset : offset+size])
// This allows for the reuse of the already
// serialized tx.
newTx.setBytes(
b.serializedBlock[offset : offset+size],
)

// Increment offset for this block.
offset += size
}

b.transactions[i] = newTx
}
}
Expand Down Expand Up @@ -248,9 +254,12 @@ func NewBlockFromBytes(serializedBlock []byte) (*Block, error) {
return nil, err
}
b.serializedBlock = serializedBlock
// This initializes []btcutil.Tx to have the serialized raw transactions cached.
// Helps speed up things like generating the txhash.

// This initializes []btcutil.Tx to have the serialized raw
// transactions cached. Helps speed up things like generating the
// txhash.
b.Transactions()

return b, nil
}

Expand All @@ -273,14 +282,19 @@ func NewBlockFromReader(r io.Reader) (*Block, error) {

// NewBlockFromBlockAndBytes returns a new instance of a bitcoin block given
// an underlying wire.MsgBlock and the serialized bytes for it. See Block.
func NewBlockFromBlockAndBytes(msgBlock *wire.MsgBlock, serializedBlock []byte) *Block {
func NewBlockFromBlockAndBytes(msgBlock *wire.MsgBlock,
serializedBlock []byte) *Block {

b := &Block{
msgBlock: msgBlock,
serializedBlock: serializedBlock,
blockHeight: BlockHeightUnknown,
}
// This initializes []btcutil.Tx to have the serialized raw transactions cached.
// Helps speed up things like generating the txhash.

// This initializes []btcutil.Tx to have the serialized raw
// transactions cached. Helps speed up things like generating the
// txhash.
b.Transactions()

return b
}
44 changes: 28 additions & 16 deletions btcutil/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ func (t *Tx) MsgTx() *wire.MsgTx {
return t.msgTx
}

// Hash returns the hash of the transaction. This is equivalent to
// calling TxHash on the underlying wire.MsgTx, however it caches the
// result so subsequent calls are more efficient. If the Tx has the
// raw bytes of the tx cached, it will use that and skip serialization.
// Hash returns the hash of the transaction. This is equivalent to calling
// TxHash on the underlying wire.MsgTx, however it caches the result so
// subsequent calls are more efficient. If the Tx has the raw bytes of the tx
// cached, it will use that and skip serialization.
func (t *Tx) Hash() *chainhash.Hash {
// Return the cached hash if it has already been generated.
if t.txHash != nil {
Expand All @@ -53,45 +53,57 @@ func (t *Tx) Hash() *chainhash.Hash {
return &hash
}

// If we have the raw bytes, then don't call msgTx.TxHash as that has the
// overhead of serialization.
// If we have the raw bytes, then don't call msgTx.TxHash as that has
// the overhead of serialization. Instead, we can take the existing
// serialized bytes and hash them to speed things up.
var hash chainhash.Hash
if t.HasWitness() {
// If the raw bytes contain the witness, we must strip it out before
// calculating the hash.
// If the raw bytes contain the witness, we must strip it out
// before calculating the hash.
baseSize := t.msgTx.SerializeSizeStripped()
nonWitnessBytes := make([]byte, 0, baseSize)

// Append the version bytes.
offset := 4
nonWitnessBytes = append(nonWitnessBytes, t.rawBytes[:offset]...)
nonWitnessBytes = append(
nonWitnessBytes, t.rawBytes[:offset]...,
)

// Append the input and output bytes. -8 to account for the
// version bytes and the locktime bytes.
//
// Skip the 2 bytes for the witness encoding.
offset += 2
nonWitnessBytes = append(nonWitnessBytes, t.rawBytes[offset:offset+baseSize-8]...)
nonWitnessBytes = append(
nonWitnessBytes,
t.rawBytes[offset:offset+baseSize-8]...,
)

// Append the last 4 bytes which are the locktime bytes.
nonWitnessBytes = append(nonWitnessBytes, t.rawBytes[len(t.rawBytes)-4:]...)
nonWitnessBytes = append(
nonWitnessBytes, t.rawBytes[len(t.rawBytes)-4:]...,
)

// We purposely call doublehashh here instead of doublehashraw as we don't have the
// serialization overhead and avoiding the 1 alloc is better in this case.
// We purposely call doublehashh here instead of doublehashraw
// as we don't have the serialization overhead and avoiding the
// 1 alloc is better in this case.
hash = chainhash.DoubleHashRaw(func(w io.Writer) error {
_, err := w.Write(nonWitnessBytes)
return err
})
} else {
// If the raw bytes don't have the witness, we can use it directly.
// If the raw bytes don't have the witness, we can use it
// directly.
//
// We purposely call doublehashh here instead of doublehashraw as we don't have the
// serialization overhead and avoiding the 1 alloc is better in this case.
// We purposely call doublehashh here instead of doublehashraw
// as we don't have the serialization overhead and avoiding the
// 1 alloc is better in this case.
hash = chainhash.DoubleHashRaw(func(w io.Writer) error {
_, err := w.Write(t.rawBytes)
return err
})
}

t.txHash = &hash
return &hash
}
Expand Down

0 comments on commit 56de9ca

Please sign in to comment.