Browse Source

Let the node generate proof of work

xno
codesoap 1 year ago
parent
commit
bee58fe251
  1. 14
      README.md
  2. 93
      block.go
  3. 2
      change.go
  4. 14
      config.go
  5. 2
      send.go

14
README.md

@ -41,11 +41,11 @@ Creating receive block for 0.100 from nano_39nd8eksw1ia6aokn96z4uthocke47hfsx9gr @@ -41,11 +41,11 @@ Creating receive block for 0.100 from nano_39nd8eksw1ia6aokn96z4uthocke47hfsx9gr
$ # Choosing a representative is important for keeping the network
$ # decentralized.
$ pass nano | atto representative nano_1jr699mk1fi6mxy1y76fmuyf3dgms8s5pzcsge5cyt1az93x4n18uxjenx93
Creating change block (may take many minutes)... done
Creating change block... done
$ # Careful with the send subcommand: No confirmation is required!
$ pass nano | atto send 0.1 nano_11zdqnjpisos53uighoaw95satm4ptdruck7xujbjcs44pbkkbw1h3zomns5
Creating send block (may take many minutes)... done
Creating send block... done
$ atto -h
Usage:
@ -80,11 +80,11 @@ and ensure, that it does nothing you wouldn't want it to do. @@ -80,11 +80,11 @@ and ensure, that it does nothing you wouldn't want it to do.
To change some defaults, take a look at `config.go`.
Signatures and proof of work are created without the help of external
services. This means that the creation of new blocks will take some
time and CPU power. Depending on your CPU, operating system and chance,
calculating the proof of work for "send" and "change" blocks can take
anywhere from a few seconds to 15 minutes or more.
Signatures are created without the help of a node, to avoid your seed or
private keys being stolen by a node operator. Apparently this is not to
be taken for granted, since the node API offers, for example, a [method
for signing](https://docs.nano.org/commands/rpc-protocol/#sign) your
blocks.
atto does not have any persistance and writes nothing to your
file system. This makes atto very portable, but also means, that

93
block.go

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
package main
import (
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
@ -22,9 +22,9 @@ type block struct { @@ -22,9 +22,9 @@ type block struct {
Hash string `json:"-"`
}
type workerResult struct {
hashNumber uint64
nonce uint64
type workGenerateResponse struct {
Error string `json:"error"`
Work string `json:"work"`
}
func (b *block) sign(privateKey *big.Int) error {
@ -121,76 +121,35 @@ func (b *block) hash(publicKey *big.Int) ([]byte, error) { @@ -121,76 +121,35 @@ func (b *block) hash(publicKey *big.Int) ([]byte, error) {
return hash[:], nil
}
func (b *block) addWork(workThreshold uint64, privateKey *big.Int) (err error) {
var suffix []byte
func (b *block) addWork(workThreshold uint64, privateKey *big.Int) error {
var hash string
if b.Previous == "0000000000000000000000000000000000000000000000000000000000000000" {
publicKey := derivePublicKey(privateKey)
suffix = make([]byte, 32, 32)
publicKey.FillBytes(suffix)
publicKeyBytes := make([]byte, 32, 32)
publicKey.FillBytes(publicKeyBytes)
hash = fmt.Sprintf("%064X", publicKeyBytes)
} else {
suffix, err = hex.DecodeString(b.Previous)
if err != nil {
return
}
hash = b.Previous
}
nonce, err := findNonce(workThreshold, suffix)
requestBody := fmt.Sprintf(`{`+
`"action": "work_generate",`+
`"hash": "%s",`+
`"difficulty": "%016x"`+
`}`, string(hash), workThreshold)
responseBytes, err := doRPC(requestBody)
if err != nil {
return
}
b.Work = fmt.Sprintf("%016x", nonce)
return
}
func findNonce(workThreshold uint64, suffix []byte) (uint64, error) {
results := make(chan workerResult)
quit := make(chan bool, workerRoutines)
errs := make(chan error, workerRoutines)
for i := 0; i < workerRoutines; i++ {
go calculateHashes(suffix, uint64(i), results, quit, errs)
}
for {
select {
case result := <-results:
if result.hashNumber >= workThreshold {
for i := 0; i < workerRoutines; i++ {
quit <- true
}
return result.nonce, nil
}
case err := <-errs:
for i := 0; i < workerRoutines; i++ {
quit <- true
}
return 0, err
}
return err
}
}
func calculateHashes(suffix []byte, nonce uint64, results chan workerResult, quit chan bool, errs chan error) {
nonceBytes := make([]byte, 8)
hasher, err := blake2b.New(8, nil)
var response workGenerateResponse
err = json.Unmarshal(responseBytes, &response)
if err != nil {
errs <- err
return
return err
}
for {
select {
case <-quit:
return
default:
binary.LittleEndian.PutUint64(nonceBytes, nonce)
_, err := hasher.Write(append(nonceBytes, suffix...))
if err != nil {
errs <- err
return
}
hashBytes := hasher.Sum(nil)
results <- workerResult{
hashNumber: binary.LittleEndian.Uint64(hashBytes),
nonce: nonce,
}
hasher.Reset()
nonce += uint64(workerRoutines)
}
// Need to check pending.Error because of
// https://github.com/nanocurrency/nano-node/issues/1782.
if response.Error != "" {
return fmt.Errorf("could not get work for block: %s", response.Error)
}
b.Work = response.Work
return nil
}

2
change.go

@ -25,7 +25,7 @@ func changeRepresentative() error { @@ -25,7 +25,7 @@ func changeRepresentative() error {
return fmt.Errorf("account has not yet been opened")
}
representative := flag.Arg(1)
fmt.Fprintf(os.Stderr, "Creating change block (may take many minutes)... ")
fmt.Fprintf(os.Stderr, "Creating change block... ")
err = changeRepresatativeOfAccount(info, representative, privateKey)
if err != nil {
fmt.Fprintln(os.Stderr, "")

14
config.go

@ -1,10 +1,12 @@ @@ -1,10 +1,12 @@
package main
var (
nodeUrl = "https://mynano.ninja/api/node"
defaultRepresentative = "nano_18shbirtzhmkf7166h39nowj9c9zrpufeg75bkbyoobqwf1iu3srfm9eo3pz"
sendWorkThreshold uint64 = 0xfffffff800000000
changeWorkThreshold uint64 = 0xfffffff800000000
receiveWorkThreshold uint64 = 0xfffffe0000000000
workerRoutines = 512
// The node needs to support the work_generate action:
nodeUrl = "https://proxy.powernode.cc/proxy"
defaultRepresentative = "nano_18shbirtzhmkf7166h39nowj9c9zrpufeg75bkbyoobqwf1iu3srfm9eo3pz"
sendWorkThreshold uint64 = 0xfffffff800000000
changeWorkThreshold uint64 = 0xfffffff800000000
receiveWorkThreshold uint64 = 0xfffffe0000000000
)

2
send.go

@ -27,7 +27,7 @@ func sendFunds() error { @@ -27,7 +27,7 @@ func sendFunds() error {
}
amount := flag.Arg(1)
recipient := flag.Arg(2)
fmt.Fprintf(os.Stderr, "Creating send block (may take many minutes)... ")
fmt.Fprintf(os.Stderr, "Creating send block... ")
err = sendFundsToAccount(info, amount, recipient, privateKey)
if err != nil {
fmt.Fprintln(os.Stderr, "")

Loading…
Cancel
Save