You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
2.8 KiB
132 lines
2.8 KiB
6 years ago
|
// Create and fulfill proof of work requests.
|
||
|
package pow
|
||
|
|
||
|
import (
|
||
|
"encoding/base64"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type Algorithm string
|
||
|
|
||
|
const (
|
||
|
Sha2BDay Algorithm = "sha2bday"
|
||
|
)
|
||
|
|
||
|
// Represents a proof-of-work request.
|
||
|
type Request struct {
|
||
|
|
||
|
// The requested algorithm
|
||
|
Alg Algorithm
|
||
|
|
||
|
// The requested difficulty
|
||
|
Difficulty uint32
|
||
|
|
||
|
// Nonce to diversify the request
|
||
|
Nonce []byte
|
||
|
}
|
||
|
|
||
|
// Represents a completed proof-of-work
|
||
|
type Proof struct {
|
||
|
buf []byte
|
||
|
}
|
||
|
|
||
|
// Convenience function to create a new sha3bday proof-of-work request
|
||
|
// as a string
|
||
|
func NewRequest(difficulty uint32, nonce []byte) string {
|
||
|
req := Request{
|
||
|
Difficulty: difficulty,
|
||
|
Nonce: nonce,
|
||
|
Alg: Sha2BDay,
|
||
|
}
|
||
|
s, _ := req.MarshalText()
|
||
|
return string(s)
|
||
|
}
|
||
|
|
||
|
func (proof Proof) MarshalText() ([]byte, error) {
|
||
|
return []byte(base64.RawStdEncoding.EncodeToString(proof.buf)), nil
|
||
|
}
|
||
|
|
||
|
func (proof *Proof) UnmarshalText(buf []byte) error {
|
||
|
var err error
|
||
|
proof.buf, err = base64.RawStdEncoding.DecodeString(string(buf))
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func (req Request) MarshalText() ([]byte, error) {
|
||
|
return []byte(fmt.Sprintf("%s-%d-%s",
|
||
|
req.Alg,
|
||
|
req.Difficulty,
|
||
|
string(base64.RawStdEncoding.EncodeToString(req.Nonce)))), nil
|
||
|
}
|
||
|
|
||
|
func (req *Request) UnmarshalText(buf []byte) error {
|
||
|
bits := strings.SplitN(string(buf), "-", 3)
|
||
|
if len(bits) != 3 {
|
||
|
return fmt.Errorf("There should be two dashes in a PoW request")
|
||
|
}
|
||
|
alg := Algorithm(bits[0])
|
||
|
if alg != Sha2BDay {
|
||
|
return fmt.Errorf("%s: unsupported algorithm", bits[0])
|
||
|
}
|
||
|
req.Alg = alg
|
||
|
diff, err := strconv.Atoi(bits[1])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
req.Difficulty = uint32(diff)
|
||
|
req.Nonce, err = base64.RawStdEncoding.DecodeString(bits[2])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Convenience function to check whether a proof of work is fulfilled
|
||
|
func Check(request, proof string, data []byte) (bool, error) {
|
||
|
var req Request
|
||
|
var prf Proof
|
||
|
err := req.UnmarshalText([]byte(request))
|
||
|
if err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
err = prf.UnmarshalText([]byte(proof))
|
||
|
if err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
return prf.Check(req, data), nil
|
||
|
}
|
||
|
|
||
|
// Fulfil the proof-of-work request.
|
||
|
func (req *Request) Fulfil(data []byte) Proof {
|
||
|
switch req.Alg {
|
||
|
case Sha2BDay:
|
||
|
return Proof{fulfilSha2BDay(req.Nonce, req.Difficulty, data)}
|
||
|
default:
|
||
|
panic("No such algorithm")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Convenience function to fulfil the proof of work request
|
||
|
func Fulfil(request string, data []byte) (string, error) {
|
||
|
var req Request
|
||
|
err := req.UnmarshalText([]byte(request))
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
proof := req.Fulfil(data)
|
||
|
s, _ := proof.MarshalText()
|
||
|
return string(s), nil
|
||
|
}
|
||
|
|
||
|
// Check whether the proof is ok
|
||
|
func (proof *Proof) Check(req Request, data []byte) bool {
|
||
|
switch req.Alg {
|
||
|
case Sha2BDay:
|
||
|
return checkSha2BDay(proof.buf, req.Nonce, data, req.Difficulty)
|
||
|
default:
|
||
|
panic("No such algorithm")
|
||
|
}
|
||
|
}
|