Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ var testMiner = &cobra.Command{
ctx, cancel := context.WithCancel(context.Background())
exit.GlobalExitHandler.AddCancel(cancel)

client, err := stratum.NewClient("user", "miner", "password", "invitecode", "payoutaddress", config.CompiledInVersion)
client, err := stratum.NewClient("user", "miner", "password", "invitecode", "payoutaddress", config.CompiledInVersion, nil)
if err != nil {
panic(err)
}
Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@ require (
github.com/Factom-Asset-Tokens/factom v0.0.0-20191120022136-7bf60a31a324
github.com/andybalholm/cascadia v1.1.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/aws/aws-sdk-go v1.25.12 // indirect
github.com/cenkalti/backoff v2.1.1+incompatible
github.com/disintegration/imaging v1.6.1 // indirect
github.com/dustin/go-humanize v1.0.0
github.com/ethereum/go-ethereum v1.9.11
github.com/fatih/color v1.7.0 // indirect
github.com/gorilla/rpc v1.2.0
github.com/gorilla/sessions v1.2.0 // indirect
github.com/gosimple/slug v1.8.0 // indirect
github.com/jinzhu/configor v1.1.1 // indirect
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect
github.com/jinzhu/gorm v1.9.11
github.com/kardianos/service v1.0.0
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/microcosm-cc/bluemonday v1.0.2 // indirect
github.com/pegnet/LXRHash v0.0.0-20200205233914-cceb516c4b7f
github.com/pegnet/LXRHash v0.0.0-20200611040256-b33327b51c91
github.com/pegnet/pegnet v0.5.0
github.com/pegnet/pegnetd v0.1.2-0.20191011183044-5eca2d08a5e8
github.com/prometheus/client_golang v1.0.0
Expand Down Expand Up @@ -56,4 +57,6 @@ require (
go.uber.org/atomic v1.4.0
go.uber.org/ratelimit v0.1.0
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20191008105621-543471e840be
)
93 changes: 93 additions & 0 deletions go.sum

Large diffs are not rendered by default.

86 changes: 47 additions & 39 deletions mining/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,17 @@ import (
"fmt"
"math"
"math/rand"
"os"
"strconv"
"sync"
"time"

lxr "github.com/pegnet/LXRHash"
log "github.com/sirupsen/logrus"
"go.uber.org/ratelimit"
)

// LX holds an instance of lxrhash
var LX lxr.LXRHash
var lxInitializer sync.Once

// The init function for LX is expensive. So we should explicitly call the init if we intend
// to use it. Make the init call idempotent
func InitLX() {
lxInitializer.Do(func() {
// This code will only be executed ONCE, no matter how often you call it
LX.Verbose(true)
if size, err := strconv.Atoi(os.Getenv("LXRBITSIZE")); err == nil && size >= 8 && size <= 30 {
LX.Init(0xfafaececfafaecec, uint64(size), 256, 5)
} else {
LX.Init(lxr.Seed, lxr.MapSizeBits, lxr.HashSize, lxr.Passes)
}
})
}

const (
_ = iota
BatchCommand
CurrentHashRate
NewNoncePrefix
NewOPRHash
ResetRecords
Expand Down Expand Up @@ -83,6 +63,9 @@ type PegnetMiner struct {

// Used to compute difficulties
ComputeDifficulty func(oprhash, nonce []byte) (difficulty uint64)

// A handle for the LXR hash
sharedLXR *lxr.LXRHash
}

type oprMiningState struct {
Expand Down Expand Up @@ -150,9 +133,8 @@ func (p *PegnetMiner) ResetNonce() {
p.resetStatic()
}

func NewPegnetMiner(id uint32, commands chan *MinerCommand, successes chan *Winner) *PegnetMiner {
func NewPegnetMiner(id uint32, commands chan *MinerCommand, successes chan *Winner, sharedLXR *lxr.LXRHash) *PegnetMiner {
p := new(PegnetMiner)
InitLX()
p.ID = id
p.PersonalID = id
p.commands = commands
Expand All @@ -163,11 +145,19 @@ func NewPegnetMiner(id uint32, commands chan *MinerCommand, successes chan *Winn
p.ResetNonce()
p.MiningState.stats = NewSingleMinerStats(p.PersonalID)

p.ComputeDifficulty = ComputeDifficulty
p.ComputeDifficulty = wrapComputeDifficulty(sharedLXR)
p.sharedLXR = sharedLXR

return p
}

func (p *PegnetMiner) Close() {
p.paused = true
// Throw away the references to sharedLXR
p.ComputeDifficulty = nil
p.sharedLXR = nil
}

// SetFakeHashRate sets the miner to "fake" a hashrate. All targets are invalid
// The rate is in hashes/s
func (p *PegnetMiner) SetFakeHashRate(rate int) {
Expand Down Expand Up @@ -229,7 +219,7 @@ func (p *PegnetMiner) MineBatch(ctx context.Context, batchsize int) {
}

var results [][]byte
results = LX.HashParallel(p.MiningState.static, batch)
results = p.sharedLXR.HashParallel(p.MiningState.static, batch)
for i := range results {
// do something with the result here
// nonce = batch[i]
Expand Down Expand Up @@ -336,6 +326,14 @@ func (p *PegnetMiner) HandleCommand(c *MinerCommand) {
for _, c := range commands {
p.HandleCommand(c)
}
case CurrentHashRate:
currentStats := *p.MiningState.stats
currentStats.Stop = time.Now()
w := c.Data.(chan *SingleMinerStats)
select {
case w <- &currentStats:
default:
}
case NewNoncePrefix:
p.ID = c.Data.(uint32)
p.ResetNonce()
Expand Down Expand Up @@ -379,6 +377,25 @@ func (p *PegnetMiner) waitForResume(ctx context.Context) {
}
}

func wrapComputeDifficulty(sharedLXR *lxr.LXRHash) func([]byte, []byte) uint64 {
if sharedLXR == nil {
log.Fatal("Cannot calculate difficulty with nil LXRHash")
}
return func(oprhash, nonce []byte) (difficulty uint64) {
no := make([]byte, len(oprhash)+len(nonce))
i := copy(no, oprhash)
copy(no[i:], nonce)
b := sharedLXR.Hash(no)

// The high eight bytes of the hash(hash(entry.Content) + nonce) is the difficulty.
// Because we don't have a difficulty bar, we can define difficulty as the greatest
// value, rather than the minimum value. Our bar is the greatest difficulty found
// within a 10 minute period. We compute difficulty as Big Endian.
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}
}

// CommandBuilder just let's me use building syntax to build commands
type CommandBuilder struct {
command *MinerCommand
Expand All @@ -392,6 +409,11 @@ func BuildCommand() *CommandBuilder {
return c
}

func (b *CommandBuilder) CurrentHashRate(w chan *SingleMinerStats) *CommandBuilder {
b.commands = append(b.commands, &MinerCommand{Command: CurrentHashRate, Data: w})
return b
}

func (b *CommandBuilder) SubmitStats(w chan *SingleMinerStats) *CommandBuilder {
b.commands = append(b.commands, &MinerCommand{Command: SubmitStats, Data: w})
return b
Expand Down Expand Up @@ -430,17 +452,3 @@ func (b *CommandBuilder) Build() *MinerCommand {
b.command.Data = b.commands
return b.command
}

func ComputeDifficulty(oprhash, nonce []byte) (difficulty uint64) {
no := make([]byte, len(oprhash)+len(nonce))
i := copy(no, oprhash)
copy(no[i:], nonce)
b := LX.Hash(no)

// The high eight bytes of the hash(hash(entry.Content) + nonce) is the difficulty.
// Because we don't have a difficulty bar, we can define difficulty as the greatest
// value, rather than the minimum value. Our bar is the greatest difficulty found
// within a 10 minute period. We compute difficulty as Big Endian.
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}
11 changes: 10 additions & 1 deletion prosper-miner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,13 @@ Flags:
-s, --poolhost string URL to connect to the pool (default "localhost:1234")
-u, --user string Username to log into the mining pool

```
```

# Environment variables

The `prosper-miner` program can use the following environment variables:

<dl>
<dt><var>PROSPERPOOL_CONFIG</var></dt>
<dd>When the <var>PROSPERPOOL_CONFIG</var> variable is set, <code>prosper-miner</code> will use the configuration file identified <var>PROSPERPOOL_CONFIG</var> unless the <code>--config</code> argument is passed.</dd>
</dl>
90 changes: 90 additions & 0 deletions prosper-miner/feedcmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package main

import (
"bufio"
"context"
"fmt"
"os"

"github.com/ethereum/go-ethereum/rpc"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var feedCmd = &cobra.Command{
Use: "feed",
Short: "Show notifications from the miner service",
Run: func(cmd *cobra.Command, args []string) {
ctx := context.Background()
clientpipe, err := rpc.DialIPC(ctx, NamedPipeName)
if err != nil {
log.Fatal("Failed to connect to the named pipe")
}
defer clientpipe.Close()
hashRateChannel := make(chan float64)
submissionChannel := make(chan int)
statusChannel := make(chan *MiningStatus)
subscriptions := map[*rpc.ClientSubscription]bool{}

subRate, err := clientpipe.Subscribe(ctx, "mining", hashRateChannel, "hashRateSubscription")
if err != nil {
log.WithError(err).Fatal("Failed to subscribe to mining:hashRateSubscription")
}
subscriptions[subRate] = true
subStatus, err := clientpipe.Subscribe(ctx, "mining", statusChannel, "statusSubscription")
if err != nil {
log.WithError(err).Fatal("Failed to subscribe to mining:statusSubscription")
}
subscriptions[subStatus] = true
subSubmission, err := clientpipe.Subscribe(ctx, "mining", submissionChannel, "submissionSubscription")
if err != nil {
log.WithError(err).Fatal("Failed to subscribe to mining:submissionSubscription")
}
subscriptions[subSubmission] = true

go func() {
for {
var status *MiningStatus
select {
case hashRate := <-hashRateChannel:
fmt.Printf("Current hash rate: %f\n", hashRate)
case status = <-statusChannel:
if status.IsConnected {
fmt.Printf("Connected to %s\n", status.PoolHostAndPort)
} else {
fmt.Print("Disconnected from pool host\n")
}
status = nil
case <-submissionChannel:
fmt.Println("A share was submitted")
case <-subRate.Err():
fmt.Println("Hash rate subscription ended")
delete(subscriptions, subRate)
if len(subscriptions) == 0 {
os.Exit(1)
}
case <-subStatus.Err():
fmt.Println("Status subscription ended")
delete(subscriptions, subStatus)
if len(subscriptions) == 0 {
os.Exit(1)
}
case <-subSubmission.Err():
fmt.Println("Submission subscription ended")
delete(subscriptions, subSubmission)
if len(subscriptions) == 0 {
os.Exit(1)
}
}
}
}()

// Allow the user to exit by pressing Enter
fmt.Println("Press Enter to terminate the feed")
keyboardReader := bufio.NewReader(os.Stdin)
keyboardReader.ReadString('\n')
subRate.Unsubscribe()
subSubmission.Unsubscribe()
return
},
}
Loading