From 6211eef7eef5b1cc7c48bacf0f439917d914e199 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 4 Apr 2015 15:30:10 -0500 Subject: [PATCH] wire: Add new DoubleSha256SH function. This commit adds a new function which is similar to the DoubleSha256 function except it returns a ShaHash copy instead of a byte slice. It also adds a new benchmark for it. This can be a slight optimization in certain cases where the caller ultimately wants a ShaHash since it can avoid a heap allocation and additional copy to convert the result to a ShaHash (the function simply performs a type cast against the returned array which is not possible against a []byte). existing: DoubleSha256 500000 3081 ns/op 32 B/op 1 allocs/op new: DoubleSha256SH 500000 2939 ns/op 0 B/op 0 allocs/op The hashing functions for blocks and transactions have also been updated to make use of the new function since they directly return the ShaHash. The transaction change in particular is quite useful since transactions are frequently hashed and this change allows all of those hashes to avoid an additional heap allocation. --- blockchain/merkle.go | 7 ++----- wire/bench_test.go | 19 ++++++++++++++++++- wire/blockheader.go | 14 +++++--------- wire/common.go | 7 +++++++ wire/msgtx.go | 8 ++------ 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/blockchain/merkle.go b/blockchain/merkle.go index 5d7e6367..11edfc5f 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -34,11 +34,8 @@ func HashMerkleBranches(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash { copy(sha[:wire.HashSize], left[:]) copy(sha[wire.HashSize:], right[:]) - // Create a new sha hash from the double sha 256. Ignore the error - // here since SetBytes can't fail here due to the fact DoubleSha256 - // always returns a []byte of the right size regardless of input. - newSha, _ := wire.NewShaHash(wire.DoubleSha256(sha[:])) - return newSha + newSha := wire.DoubleSha256SH(sha[:]) + return &newSha } // BuildMerkleTreeStore creates a merkle tree from a slice of transactions, diff --git a/wire/bench_test.go b/wire/bench_test.go index b7d2b7d5..01b29538 100644 --- a/wire/bench_test.go +++ b/wire/bench_test.go @@ -394,7 +394,7 @@ func BenchmarkTxSha(b *testing.B) { } // BenchmarkDoubleSha256 performs a benchmark on how long it takes to perform a -// double sha 256. +// double sha 256 returning a byte slice. func BenchmarkDoubleSha256(b *testing.B) { b.StopTimer() var buf bytes.Buffer @@ -409,3 +409,20 @@ func BenchmarkDoubleSha256(b *testing.B) { _ = DoubleSha256(txBytes) } } + +// BenchmarkDoubleSha256SH performs a benchmark on how long it takes to perform +// a double sha 256 returning a ShaHash. +func BenchmarkDoubleSha256SH(b *testing.B) { + b.StopTimer() + var buf bytes.Buffer + if err := genesisCoinbaseTx.Serialize(&buf); err != nil { + b.Errorf("Serialize: unexpected error: %v", err) + return + } + txBytes := buf.Bytes() + b.StartTimer() + + for i := 0; i < b.N; i++ { + _ = DoubleSha256SH(txBytes) + } +} diff --git a/wire/blockheader.go b/wire/blockheader.go index 3ca8619c..99e1eeb9 100644 --- a/wire/blockheader.go +++ b/wire/blockheader.go @@ -46,21 +46,17 @@ const blockHeaderLen = 80 // BlockSha computes the block identifier hash for the given block header. func (h *BlockHeader) BlockSha() (ShaHash, error) { - // Encode the header and run double sha256 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. Also, SetBytes can't fail here due to the - // fact DoubleSha256 always returns a []byte of the right size - // regardless of input. + // Encode the header and double sha256 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. var buf bytes.Buffer - var sha ShaHash _ = writeBlockHeader(&buf, 0, h) - _ = sha.SetBytes(DoubleSha256(buf.Bytes()[0:blockHeaderLen])) // Even though this function can't currently fail, it still returns // a potential error to help future proof the API should a failure // become possible. - return sha, nil + return DoubleSha256SH(buf.Bytes()), nil } // Deserialize decodes a block header from r into the receiver using a format diff --git a/wire/common.go b/wire/common.go index 27264951..48251e2f 100644 --- a/wire/common.go +++ b/wire/common.go @@ -526,3 +526,10 @@ func DoubleSha256(b []byte) []byte { second := fastsha256.Sum256(first[:]) return second[:] } + +// DoubleSha256SH calculates sha256(sha256(b)) and returns the resulting bytes +// as a ShaHash. +func DoubleSha256SH(b []byte) ShaHash { + first := fastsha256.Sum256(b) + return ShaHash(fastsha256.Sum256(first[:])) +} diff --git a/wire/msgtx.go b/wire/msgtx.go index fa262e33..f24ae098 100644 --- a/wire/msgtx.go +++ b/wire/msgtx.go @@ -170,18 +170,14 @@ func (msg *MsgTx) TxSha() (ShaHash, error) { // Encode the transaction and calculate double sha256 on the result. // Ignore the error returns since the only way the encode could fail // is being out of memory or due to nil pointers, both of which would - // cause a run-time panic. Also, SetBytes can't fail here due to the - // fact DoubleSha256 always returns a []byte of the right size - // regardless of input. + // cause a run-time panic. buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize())) _ = msg.Serialize(buf) - var sha ShaHash - _ = sha.SetBytes(DoubleSha256(buf.Bytes())) // Even though this function can't currently fail, it still returns // a potential error to help future proof the API should a failure // become possible. - return sha, nil + return DoubleSha256SH(buf.Bytes()), nil } // Copy creates a deep copy of a transaction so that the original does not get