Skip to content

Commit 96c9fd8

Browse files
authored
Merge pull request btcsuite#1978 from kcalvinalvin/memory-efficient-txhash
chainhash, wire, btcutil, main: Memory efficient txhash
2 parents 4f72645 + 375f79d commit 96c9fd8

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

chaincfg/chainhash/hashfuncs.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55

66
package chainhash
77

8-
import "crypto/sha256"
8+
import (
9+
"crypto/sha256"
10+
"io"
11+
)
912

1013
// HashB calculates hash(b) and returns the resulting bytes.
1114
func HashB(b []byte) []byte {
@@ -31,3 +34,24 @@ func DoubleHashH(b []byte) Hash {
3134
first := sha256.Sum256(b)
3235
return Hash(sha256.Sum256(first[:]))
3336
}
37+
38+
// DoubleHashRaw calculates hash(hash(w)) where w is the resulting bytes from
39+
// the given serialize function and returns the resulting bytes as a Hash.
40+
func DoubleHashRaw(serialize func(w io.Writer) error) Hash {
41+
// Encode the transaction into the hash. Ignore the error returns
42+
// since the only way the encode could fail is being out of memory
43+
// or due to nil pointers, both of which would cause a run-time panic.
44+
h := sha256.New()
45+
_ = serialize(h)
46+
47+
// This buf is here because Sum() will append the result to the passed
48+
// in byte slice. Pre-allocating here saves an allocation on the second
49+
// hash as we can reuse it. This allocation also does not escape to the
50+
// heap, saving an allocation.
51+
buf := make([]byte, 0, HashSize)
52+
first := h.Sum(buf)
53+
h.Reset()
54+
h.Write(first)
55+
res := h.Sum(buf)
56+
return *(*Hash)(res)
57+
}

chaincfg/chainhash/hashfuncs_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package chainhash
66

77
import (
88
"fmt"
9+
"io"
910
"testing"
1011
)
1112

@@ -133,4 +134,20 @@ func TestDoubleHashFuncs(t *testing.T) {
133134
continue
134135
}
135136
}
137+
138+
// Ensure the hash function which accepts a hash.Hash returns the expected
139+
// result when given a hash.Hash that is of type SHA256.
140+
for _, test := range tests {
141+
serialize := func(w io.Writer) error {
142+
w.Write([]byte(test.in))
143+
return nil
144+
}
145+
hash := DoubleHashRaw(serialize)
146+
h := fmt.Sprintf("%x", hash[:])
147+
if h != test.out {
148+
t.Errorf("DoubleHashRaw(%q) = %s, want %s", test.in, h,
149+
test.out)
150+
continue
151+
}
152+
}
136153
}

0 commit comments

Comments
 (0)