forked from irisnet/cosmos-sdk
/
types.go
80 lines (69 loc) · 2.42 KB
/
types.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
package pow
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"math"
"strconv"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// MsgMine - mine some coins with PoW
type MsgMine struct {
Sender sdk.AccAddress `json:"sender"`
Difficulty uint64 `json:"difficulty"`
Count uint64 `json:"count"`
Nonce uint64 `json:"nonce"`
Proof []byte `json:"proof"`
}
// enforce the msg type at compile time
var _ sdk.Msg = MsgMine{}
// NewMsgMine - construct mine message
func NewMsgMine(sender sdk.AccAddress, difficulty uint64, count uint64, nonce uint64, proof []byte) MsgMine {
return MsgMine{sender, difficulty, count, nonce, proof}
}
// nolint
func (msg MsgMine) Type() string { return "pow" }
func (msg MsgMine) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} }
func (msg MsgMine) String() string {
return fmt.Sprintf("MsgMine{Sender: %s, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
}
// validate the mine message
func (msg MsgMine) ValidateBasic() sdk.Error {
// check hash
var data []byte
// hash must include sender, so no other users can race the tx
data = append(data, []byte(msg.Sender)...)
countBytes := strconv.FormatUint(msg.Count, 16)
// hash must include count so proof-of-work solutions cannot be replayed
data = append(data, countBytes...)
nonceBytes := strconv.FormatUint(msg.Nonce, 16)
data = append(data, nonceBytes...)
hash := crypto.Sha256(data)
hashHex := make([]byte, hex.EncodedLen(len(hash)))
hex.Encode(hashHex, hash)
hashHex = hashHex[:16]
if !bytes.Equal(hashHex, msg.Proof) {
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
}
// check proof below difficulty
// difficulty is linear - 1 = all hashes, 2 = half of hashes, 3 = third of hashes, etc
target := math.MaxUint64 / msg.Difficulty
hashUint, err := strconv.ParseUint(string(msg.Proof), 16, 64)
if err != nil {
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("proof: %s", msg.Proof))
}
if hashUint >= target {
return ErrNotBelowTarget(DefaultCodespace, fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
}
return nil
}
// get the mine message sign bytes
func (msg MsgMine) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return sdk.MustSortJSON(b)
}