-
Notifications
You must be signed in to change notification settings - Fork 0
/
pow.go
127 lines (101 loc) · 2.87 KB
/
pow.go
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
package gopow
import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
"math/bits"
gonanoid "github.com/matoous/go-nanoid"
)
// NonceGenerator function type. takes a length parameter and returns a string.
// The length parameter is optional; the returned string need not be
// as long as the length parameter
type NonceGenerator func(int) ([]byte, error)
// HashFunction type.
type HashFunction func([]byte) []byte
// Pow ...
type Pow struct {
Secret []byte
NonceLength int
Check bool
Difficulty int
// NonceGenerator method returns a nonce. Takes an integer parameter
// which is `Pow.NonceLength`. Defaults to `gonanoid.Nanoid`
NonceGenerator NonceGenerator
// Hash is a method that hashes a slice of bytes and returns a new slice which is a hash of the slice.
// Defaults to `sha256.Sum256`
Hash HashFunction
}
// GenerateNonce generates a new nonce, also generates signature if verify enabled
func (p *Pow) GenerateNonce() (nonce []byte, checksum []byte, err error) {
nonce, err = p.NonceGenerator(p.NonceLength)
if err != nil {
nonce = []byte{}
return
}
if p.Check {
checksum = p.Hash(append(nonce, p.Secret...))
}
return
}
// VerifyHash verifies the hash given the nonce and data
func (p *Pow) VerifyHash(nonce []byte, data []byte, hash []byte, nonceSig []byte) (bool, error) {
if p.Check {
if len(nonceSig) == 0 {
return false, errors.New("can't verify with empty nonceSig")
}
sign := p.Hash(append(nonce, p.Secret...))
if !bytes.Equal(sign, nonceSig) {
return false, fmt.Errorf("nonce is invalid. Provided nonce hashed to: <%x> Expected: <%x>", sign, nonceSig)
}
}
hashHere := p.Hash(append(data, nonce...))
if !bytes.Equal(hashHere, hash) {
return false, errors.New("failed to verify hash")
}
return true, nil
}
// VerifyDifficulty verifies hash fulfils difficulty requirement
func (p *Pow) VerifyDifficulty(hash []byte) bool {
diff := p.Difficulty
for _, byte := range hash {
lead := bits.LeadingZeros8(uint8(byte))
diff -= lead
if lead < 8 {
if diff <= 0 {
return true
}
return false
}
if diff <= 0 {
return true
}
}
return false
}
// VerifyHashAtDifficulty verifies hash and difficulty
func (p *Pow) VerifyHashAtDifficulty(nonce []byte, data []byte, hash []byte, nonceSig []byte) (bool, error) {
if !p.VerifyDifficulty(hash) {
return false, fmt.Errorf("failed to verify at difficulty: %v", p.Difficulty)
}
return p.VerifyHash(nonce, data, hash, nonceSig)
}
// New helper function to return new pow object with defaults
func New(config *Pow) *Pow {
if config.NonceLength == 0 {
config.NonceLength = 10
}
if config.NonceGenerator == nil {
config.NonceGenerator = func(l int) ([]byte, error) {
nonce, err := gonanoid.Nanoid(l)
return []byte(nonce), err
}
}
if config.Hash == nil {
config.Hash = func(b []byte) []byte {
h := (sha256.Sum256(b))
return h[:]
}
}
return config
}