2018-03-22 10:47:13 -07:00
|
|
|
package pow
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
crypto "github.com/tendermint/go-crypto"
|
|
|
|
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
)
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// MsgMine - mine some coins with PoW
|
|
|
|
type MsgMine struct {
|
2018-03-22 10:47:13 -07:00
|
|
|
Sender sdk.Address `json:"sender"`
|
|
|
|
Difficulty uint64 `json:"difficulty"`
|
|
|
|
Count uint64 `json:"count"`
|
|
|
|
Nonce uint64 `json:"nonce"`
|
|
|
|
Proof []byte `json:"proof"`
|
|
|
|
}
|
|
|
|
|
2018-04-05 05:13:11 -07:00
|
|
|
// enforce the msg type at compile time
|
2018-04-18 21:49:24 -07:00
|
|
|
var _ sdk.Msg = MsgMine{}
|
2018-04-05 05:13:11 -07:00
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// NewMsgMine - construct mine message
|
|
|
|
func NewMsgMine(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MsgMine {
|
|
|
|
return MsgMine{sender, difficulty, count, nonce, proof}
|
2018-03-22 10:47:13 -07:00
|
|
|
}
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// nolint
|
|
|
|
func (msg MsgMine) Type() string { return "pow" }
|
|
|
|
func (msg MsgMine) Get(key interface{}) (value interface{}) { return nil }
|
|
|
|
func (msg MsgMine) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
|
|
|
func (msg MsgMine) String() string {
|
|
|
|
return fmt.Sprintf("MsgMine{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
2018-03-22 10:47:13 -07:00
|
|
|
}
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// validate the mine message
|
|
|
|
func (msg MsgMine) ValidateBasic() sdk.Error {
|
2018-03-22 10:47:13 -07:00
|
|
|
// 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) {
|
2018-04-17 19:16:21 -07:00
|
|
|
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
|
2018-03-22 10:47:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2018-04-17 19:16:21 -07:00
|
|
|
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("proof: %s", msg.Proof))
|
2018-03-22 10:47:13 -07:00
|
|
|
}
|
|
|
|
if hashUint >= target {
|
2018-04-17 19:16:21 -07:00
|
|
|
return ErrNotBelowTarget(DefaultCodespace, fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
|
2018-03-22 10:47:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// get the mine message sign bytes
|
|
|
|
func (msg MsgMine) GetSignBytes() []byte {
|
2018-03-22 10:47:13 -07:00
|
|
|
b, err := json.Marshal(msg)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|