1 package chainhash
2 3 import (
4 "encoding/hex"
5 "fmt"
6 )
7 8 // HashSize of array used to store hashes. See Hash.
9 const HashSize = 32
10 11 // MaxHashStringSize is the maximum length of a Hash hash string.
12 const MaxHashStringSize = HashSize * 2
13 14 // ErrHashStrSize describes an error that indicates the caller specified a hash string that has too many characters.
15 var ErrHashStrSize = fmt.Errorf("max hash string length is %v bytes", MaxHashStringSize)
16 17 // Hash is used in several of the bitcoin messages and common structures. It typically represents the double sha256 of
18 // data.
19 type Hash [HashSize]byte
20 21 // String returns the Hash as the hexadecimal string of the byte-reversed hash.
22 func (hash Hash) String() string {
23 for i := 0; i < HashSize/2; i++ {
24 hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
25 }
26 return hex.EncodeToString(hash[:])
27 }
28 29 // CloneBytes returns a copy of the bytes which represent the hash as a byte slice. NOTE: It is generally cheaper to
30 // just slice the hash directly thereby reusing the same bytes rather than calling this method.
31 func (hash *Hash) CloneBytes() []byte {
32 newHash := make([]byte, HashSize)
33 copy(newHash, hash[:])
34 return newHash
35 }
36 37 // SetBytes sets the bytes which represent the hash. An error is returned if the number of bytes passed in is not
38 // HashSize.
39 func (hash *Hash) SetBytes(newHash []byte) (e error) {
40 nhlen := len(newHash)
41 if nhlen != HashSize {
42 return fmt.Errorf(
43 "invalid hash length of %v, want %v", nhlen,
44 HashSize,
45 )
46 }
47 copy(hash[:], newHash)
48 return nil
49 }
50 51 // IsEqual returns true if target is the same as hash.
52 func (hash *Hash) IsEqual(target *Hash) bool {
53 if hash == nil && target == nil {
54 return true
55 }
56 if hash == nil || target == nil {
57 return false
58 }
59 return *hash == *target
60 }
61 62 // NewHash returns a new Hash from a byte slice. An error is returned if the number of bytes passed in is not HashSize.
63 func NewHash(newHash []byte) (*Hash, error) {
64 var sh Hash
65 e := sh.SetBytes(newHash)
66 if e != nil {
67 E.Ln(e)
68 return nil, e
69 }
70 return &sh, e
71 }
72 73 // NewHashFromStr creates a Hash from a hash string. The string should be the hexadecimal string of a byte-reversed
74 // hash, but any missing characters result in zero padding at the end of the Hash.
75 func NewHashFromStr(hash string) (ret *Hash, e error) {
76 ret = new(Hash)
77 if e = Decode(ret, hash); E.Chk(e) {
78 return
79 }
80 return
81 }
82 83 // Decode decodes the byte-reversed hexadecimal string encoding of a Hash to a destination.
84 func Decode(dst *Hash, src string) (e error) {
85 // Return error if hash string is too long.
86 if len(src) > MaxHashStringSize {
87 return ErrHashStrSize
88 }
89 // Hex decoder expects the hash to be a multiple of two. When not, pad with a leading zero.
90 var srcBytes []byte
91 if len(src)%2 == 0 {
92 srcBytes = []byte(src)
93 } else {
94 srcBytes = make([]byte, 1+len(src))
95 srcBytes[0] = '0'
96 copy(srcBytes[1:], src)
97 }
98 // Hex decode the source bytes to a temporary destination.
99 var reversedHash Hash
100 if _, e = hex.Decode(reversedHash[HashSize-hex.DecodedLen(len(srcBytes)):], srcBytes); E.Chk(e) {
101 return e
102 }
103 // Reverse copy from the temporary hash to destination. Because the temporary was zeroed, the written result will be
104 // correctly padded.
105 for i, b := range reversedHash[:HashSize/2] {
106 dst[i], dst[HashSize-1-i] = reversedHash[HashSize-1-i], b
107 }
108 return
109 }
110