Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions wire/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"time"

"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/crypto/blake256"
)

// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
Expand Down Expand Up @@ -634,6 +635,28 @@ func BenchmarkTxHash(b *testing.B) {
}
}

// BenchmarkTxHashReuseHasher performs a benchmark on how long it takes to hash a
// transaction by reusing the *blake256.Hasher256 object.
func BenchmarkTxHashReuseHasher(b *testing.B) {
h := blake256.NewHasher256()

txHash := func(h *blake256.Hasher256, tx *MsgTx) chainhash.Hash {
txCopy := *tx
txCopy.SerType = TxSerializeNoWitness
err := txCopy.Serialize(h)
if err != nil {
panic(err)
}
return h.Sum256()
}

b.ResetTimer()
for i := 0; i < b.N; i++ {
h.Reset()
_ = txHash(h, &genesisCoinbaseTx)
}
}

// BenchmarkHashB performs a benchmark on how long it takes to perform a hash
// returning a byte slice.
func BenchmarkHashB(b *testing.B) {
Expand Down
28 changes: 15 additions & 13 deletions wire/blockheader.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"time"

"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/crypto/blake256"
"lukechampine.com/blake3"
)

Expand Down Expand Up @@ -88,36 +89,37 @@ type BlockHeader struct {
// header.
const blockHeaderLen = 180

// BlockHash computes the block identifier hash for the given block header.
// BlockHash computes the BLAKE-256 block identifier hash for the given block
// header.
func (h *BlockHeader) BlockHash() chainhash.Hash {
// Encode the header and hash everything prior to the number of
// transactions. Ignore the error returns since there is no way the encode
// could fail except being out of memory which would cause a run-time panic.
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
_ = writeBlockHeader(buf, 0, h)

return chainhash.HashH(buf.Bytes())
hasher := blake256.NewHasher256()
_ = writeBlockHeader(hasher, 0, h)
return hasher.Sum256()
}

// PowHashV1 calculates and returns the version 1 proof of work hash for the
// block header.
// PowHashV1 calculates and returns the version 1 proof of work BLAKE-256 hash
// for the block header.
//
// NOTE: This is the original proof of work hash function used at Decred launch
// and applies to all blocks prior to the activation of DCP0011.
func (h *BlockHeader) PowHashV1() chainhash.Hash {
return h.BlockHash()
}

// PowHashV2 calculates and returns the version 2 proof of work hash as defined
// in DCP0011 for the block header.
// PowHashV2 calculates and returns the version 2 proof of work BLAKE3 hash as
// defined in DCP0011 for the block header.
func (h *BlockHeader) PowHashV2() chainhash.Hash {
// Encode the header and hash everything prior to the number of
// transactions. Ignore the error returns since there is no way the encode
// could fail except being out of memory which would cause a run-time panic.
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
_ = writeBlockHeader(buf, 0, h)

return blake3.Sum256(buf.Bytes())
var digest chainhash.Hash
hasher := blake3.New(len(digest), nil)
_ = writeBlockHeader(hasher, 0, h)
hasher.Sum(digest[:0])
return digest
}

// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
Expand Down
16 changes: 16 additions & 0 deletions wire/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"time"

"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/crypto/blake256"
"lukechampine.com/blake3"
)

const (
Expand Down Expand Up @@ -382,6 +384,18 @@ func shortWrite(w io.Writer, cb func() (data [8]byte, size int)) error {
w.Write(data[:size])
return nil

// Hashing transactions can be optimized by writing directly to the
// BLAKE-256 hasher.
case *blake256.Hasher256:
w.Write(data[:size])
return nil

// Hashing block headers can be optimized by writing directly to the
// BLAKE-3 hasher.
case *blake3.Hasher:
w.Write(data[:size])
return nil

default:
p := binarySerializer.Borrow()[:size]
copy(p, data[:size])
Expand Down Expand Up @@ -813,6 +827,8 @@ func WriteVarString(w io.Writer, pver uint32, str string) error {
switch w := w.(type) {
case *bytes.Buffer:
_, err = w.WriteString(str)
case *blake256.Hasher256:
w.WriteString(str)
default:
_, err = w.Write([]byte(str))
}
Expand Down
6 changes: 2 additions & 4 deletions wire/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ go 1.17
require (
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/chaincfg/chainhash v1.0.5
github.com/decred/dcrd/crypto/blake256 v1.1.0
lukechampine.com/blake3 v1.3.0
)

require (
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
)
require github.com/klauspost/cpuid/v2 v2.0.9 // indirect
61 changes: 33 additions & 28 deletions wire/msgtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strconv"

"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/crypto/blake256"
)

const (
Expand Down Expand Up @@ -391,24 +392,28 @@ func (msg *MsgTx) serialize(serType TxSerializeType) ([]byte, error) {
return buf.Bytes(), nil
}

// mustSerialize returns the serialization of the transaction for the provided
// serialization type without modifying the original transaction. It will panic
// if any errors occur.
func (msg *MsgTx) mustSerialize(serType TxSerializeType) []byte {
serialized, err := msg.serialize(serType)
// mustHash returns the hash of the transaction for the provided
// serialization type without modifying the original transaction.
// It will panic if serialization fails.
func (msg *MsgTx) mustHash(hasher *blake256.Hasher256, serType TxSerializeType) chainhash.Hash {
// Shallow copy so the serialization type can be changed without
// modifying the original transaction.
mtxCopy := *msg
mtxCopy.SerType = serType
err := mtxCopy.Serialize(hasher)
if err != nil {
panic(fmt.Sprintf("MsgTx failed serializing for type %v",
serType))
}
return serialized
return hasher.Sum256()
}

// TxHash generates the hash for the transaction prefix. Since it does not
// contain any witness data, it is not malleable and therefore is stable for
// use in unconfirmed transaction chains.
// TxHash generates the BLAKE-256 hash for the transaction prefix. Since it
// does not contain any witness data, it is not malleable and therefore is
// stable for use in unconfirmed transaction chains.
func (msg *MsgTx) TxHash() chainhash.Hash {
// TxHash should always calculate a non-witnessed hash.
return chainhash.HashH(msg.mustSerialize(TxSerializeNoWitness))
return msg.mustHash(blake256.NewHasher256(), TxSerializeNoWitness)
}

// CachedTxHash is equivalent to calling TxHash, however it caches the result so
Expand All @@ -433,29 +438,29 @@ func (msg *MsgTx) RecacheTxHash() *chainhash.Hash {
return msg.CachedHash
}

// TxHashWitness generates the hash for the transaction witness.
// TxHashWitness generates the BLAKE-256 hash for the transaction witness.
func (msg *MsgTx) TxHashWitness() chainhash.Hash {
// TxHashWitness should always calculate a witnessed hash.
return chainhash.HashH(msg.mustSerialize(TxSerializeOnlyWitness))
return msg.mustHash(blake256.NewHasher256(), TxSerializeOnlyWitness)
}

// TxHashFull generates the hash for the transaction prefix || witness. It first
// obtains the hashes for both the transaction prefix and witness, then
// concatenates them and hashes the result.
// TxHashFull generates the hash for the transaction prefix || witness. This
// is the BLAKE-256 hash of the concatenation of the individual prefix and
// witness hashes (and not the hash of the full serialization).
func (msg *MsgTx) TxHashFull() chainhash.Hash {
// Note that the inputs to the hashes, the serialized prefix and
// witness, have different serialized versions because the serialized
// encoding of the version includes the real transaction version in the
// lower 16 bits and the transaction serialization type in the upper 16
// bits. The real transaction version (lower 16 bits) will be the same
// in both serializations.
concat := make([]byte, chainhash.HashSize*2)
prefixHash := msg.TxHash()
witnessHash := msg.TxHashWitness()
copy(concat[0:], prefixHash[:])
copy(concat[chainhash.HashSize:], witnessHash[:])

return chainhash.HashH(concat)
// Even for a transaction that has neither prefix nor witness (and
// would otherwise hash to the same result), the prefix and witness
// hashes will still differ due to the serialization type being
// encoded into the upper 16 bits of the transaction version.
hasher := blake256.NewHasher256()
prefixHash := msg.mustHash(hasher, TxSerializeNoWitness)
hasher.Reset()
witnessHash := msg.mustHash(hasher, TxSerializeOnlyWitness)
hasher.Reset()

hasher.WriteBytes(prefixHash[:])
hasher.WriteBytes(witnessHash[:])
return hasher.Sum256()
}

// Copy creates a deep copy of a transaction so that the original does not get
Expand Down