-
Notifications
You must be signed in to change notification settings - Fork 264
Expand file tree
/
Copy pathhashing.go
More file actions
142 lines (121 loc) · 3.41 KB
/
hashing.go
File metadata and controls
142 lines (121 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package types
import (
"crypto/sha256"
"errors"
"hash"
"sync"
"unsafe"
"google.golang.org/protobuf/proto"
pb "github.com/evstack/ev-node/types/pb/evnode/v1"
)
var (
leafPrefix = []byte{0}
// sha256Pool reuses sha256 Hash instances to avoid per-block allocation.
// sha256.New() allocates ~213 bytes (216B on 64-bit) per call. Pooling
// eliminates this allocation entirely in the hot path.
sha256Pool = sync.Pool{
New: func() interface{} {
return sha256.New()
},
}
)
// HashSlim returns the SHA256 hash of the header using the slim (current) binary encoding.
func (h *Header) HashSlim() (Hash, error) {
if h == nil {
return nil, errors.New("header is nil")
}
bytes, err := h.MarshalBinary()
if err != nil {
return nil, err
}
hash := sha256.Sum256(bytes)
return hash[:], nil
}
// HashLegacy returns the SHA256 hash of the header using the legacy binary encoding that
// includes the deprecated fields.
func (h *Header) HashLegacy() (Hash, error) {
if h == nil {
return nil, errors.New("header is nil")
}
bytes, err := h.MarshalBinaryLegacy()
if err != nil {
return nil, err
}
hash := sha256.Sum256(bytes)
return hash[:], nil
}
// Hash returns the header hash. It reuses a memoized value if one has already
// been prepared via MemoizeHash, but it does not write to the header itself.
func (h *Header) Hash() Hash {
if h == nil {
return nil
}
if h.cachedHash != nil {
return h.cachedHash
}
return h.computeHash()
}
// MemoizeHash computes the header hash and stores it on the header for future
// Hash() calls. Call this before publishing the header to shared goroutines or
// caches.
//
// If a Header struct is reused (e.g. overwritten via FromProto or field
// assignment), call InvalidateHash() first to clear the cached value before
// calling MemoizeHash again. Failure to do so will return the stale cached hash.
func (h *Header) MemoizeHash() Hash {
if h == nil {
return nil
}
if h.cachedHash != nil {
return h.cachedHash
}
hash := h.computeHash()
if hash != nil {
h.cachedHash = hash
}
return hash
}
func (h *Header) computeHash() Hash {
// Legacy hash takes precedence when legacy fields are present (backwards
// compatibility). Slim hash is the canonical hash for all other headers.
if h.Legacy != nil && !h.Legacy.IsZero() {
if legacyHash, err := h.HashLegacy(); err == nil {
return legacyHash
}
}
slimHash, err := h.HashSlim()
if err != nil {
return nil
}
return slimHash
}
// InvalidateHash clears the memoized hash, forcing recomputation on the next
// Hash() call. Must be called after any mutation of Header fields.
func (h *Header) InvalidateHash() {
if h != nil {
h.cachedHash = nil
}
}
// Hash returns hash of the Data
func (d *Data) Hash() Hash {
// Ignoring the marshal error for now to satisfy the go-header interface
// Later on the usage of Hash should be replaced with DA commitment
dBytes, _ := d.MarshalBinary()
s := sha256Pool.Get().(hash.Hash)
defer sha256Pool.Put(s)
return leafHashOpt(s, dBytes)
}
// DACommitment returns the DA commitment of the Data excluding the Metadata.
func (d *Data) DACommitment() Hash {
pbData := pb.Data{Txs: unsafe.Slice((*[]byte)(unsafe.SliceData(d.Txs)), len(d.Txs))}
dBytes, _ := proto.Marshal(&pbData)
s := sha256Pool.Get().(hash.Hash)
defer sha256Pool.Put(s)
return leafHashOpt(s, dBytes)
}
func leafHashOpt(s hash.Hash, leaf []byte) []byte {
s.Reset()
s.Write(leafPrefix)
s.Write(leaf)
return s.Sum(nil)
}