Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EXPERIMENTAL: Support both PPLNS / PROP #8

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* Modern beautiful Ember.js frontend
* Separate stats for workers: can highlight timed-out workers so miners can perform maintenance of rigs
* JSON-API for stats
* PPLNS block reward

#### Proxies

Expand Down
1 change: 1 addition & 0 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"threads": 2,
"coin": "eth",
"name": "main",
"pplns": 9000,

"proxy": {
"enabled": true,
Expand Down
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func startApi() {
}

func startBlockUnlocker() {
cfg.BlockUnlocker.Pplns = cfg.Pplns
u := payouts.NewBlockUnlocker(&cfg.BlockUnlocker, backend)
u.Start()
}
Expand Down Expand Up @@ -82,7 +83,7 @@ func main() {

startNewrelic()

backend = storage.NewRedisClient(&cfg.Redis, cfg.Coin)
backend = storage.NewRedisClient(&cfg.Redis, cfg.Coin, cfg.Pplns)
pong, err := backend.Check()
if err != nil {
log.Printf("Can't establish connection to backend: %v", err)
Expand Down
21 changes: 19 additions & 2 deletions payouts/unlocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type UnlockerConfig struct {
Interval string `json:"interval"`
Daemon string `json:"daemon"`
Timeout string `json:"timeout"`
Pplns int64
}

const minDepth = 16
Expand Down Expand Up @@ -448,12 +449,28 @@ func (u *BlockUnlocker) calculateRewards(block *storage.BlockData) (*big.Rat, *b
revenue := new(big.Rat).SetInt(block.Reward)
minersProfit, poolProfit := chargeFee(revenue, u.config.PoolFee)

shares, err := u.backend.GetRoundShares(block.RoundHeight, block.Nonce)
var shares map[string]int64
var err error
if u.config.Pplns > 0 {
shares, err = u.backend.GetNShares(block.RoundHeight, block.Nonce)
} else {
shares, err = u.backend.GetRoundShares(block.RoundHeight, block.Nonce)
}
if err != nil {
return nil, nil, nil, nil, err
}

rewards := calculateRewardsForShares(shares, block.TotalShares, minersProfit)
var rewards map[string]int64
if u.config.Pplns > 0 {
totalShares := int64(0)
for _, val := range shares {
totalShares += val
}

rewards = calculateRewardsForShares(shares, totalShares, minersProfit)
} else {
rewards = calculateRewardsForShares(shares, block.TotalShares, minersProfit)
}

if block.ExtraReward != nil {
extraReward := new(big.Rat).SetInt(block.ExtraReward)
Expand Down
1 change: 1 addition & 0 deletions proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Config struct {
Threads int `json:"threads"`

Coin string `json:"coin"`
Pplns int64 `json:"pplns"`
Redis storage.Config `json:"redis"`

BlockUnlocker payouts.UnlockerConfig `json:"unlocker"`
Expand Down
70 changes: 67 additions & 3 deletions storage/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Config struct {
type RedisClient struct {
client *redis.Client
prefix string
pplns int64
}

type BlockData struct {
Expand Down Expand Up @@ -78,14 +79,14 @@ type Worker struct {
TotalHR int64 `json:"hr2"`
}

func NewRedisClient(cfg *Config, prefix string) *RedisClient {
func NewRedisClient(cfg *Config, prefix string, pplns int64) *RedisClient {
client := redis.NewClient(&redis.Options{
Addr: cfg.Endpoint,
Password: cfg.Password,
DB: cfg.Database,
PoolSize: cfg.PoolSize,
})
return &RedisClient{client: client, prefix: prefix}
return &RedisClient{client: client, prefix: prefix, pplns: pplns}
}

func (r *RedisClient) Client() *redis.Client {
Expand Down Expand Up @@ -212,12 +213,33 @@ func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundD
tx.HIncrBy(r.formatKey("miners", login), "blocksFound", 1)
tx.Rename(r.formatKey("shares", "roundCurrent"), r.formatRound(int64(height), params[0]))
tx.HGetAllMap(r.formatRound(int64(height), params[0]))
tx.LRange(r.formatKey("lastshares"), 0, r.pplns)
return nil
})
if err != nil {
return false, err
} else {
sharesMap, _ := cmds[10].(*redis.StringStringMapCmd).Result()
lastshares := cmds[len(cmds)-1].(*redis.StringSliceCmd).Val()

totalnshares := make(map[string]int64)
for _, val := range lastshares {
totalnshares[val] += 1
}

ntx := r.client.Multi()
defer ntx.Close()

_, err := ntx.Exec(func() error {
for k, v := range totalnshares {
ntx.HIncrBy(r.formatNShare(int64(height), params[0]), k, v)
}
return nil
})
if err != nil {
return false, err
}

sharesMap, _ := cmds[len(cmds)-2].(*redis.StringStringMapCmd).Result()
totalShares := int64(0)
for _, v := range sharesMap {
n, _ := strconv.ParseInt(v, 10, 64)
Expand All @@ -231,6 +253,9 @@ func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundD
}

func (r *RedisClient) writeShare(tx *redis.Multi, ms, ts int64, login, id string, diff int64, expire time.Duration) {
tx.LPush(r.formatKey("lastshares"), login)
tx.LTrim(r.formatKey("lastshares"), 0, r.pplns)

tx.HIncrBy(r.formatKey("shares", "roundCurrent"), login, diff)
tx.ZAdd(r.formatKey("hashrate"), redis.Z{Score: float64(ts), Member: join(diff, login, id, ms)})
tx.ZAdd(r.formatKey("hashrate", login), redis.Z{Score: float64(ts), Member: join(diff, id, ms)})
Expand All @@ -246,6 +271,10 @@ func (r *RedisClient) formatRound(height int64, nonce string) string {
return r.formatKey("shares", "round"+strconv.FormatInt(height, 10), nonce)
}

func (r *RedisClient) formatNShare(height int64, nonce string) string {
return r.formatKey("nshares", "round"+strconv.FormatInt(height, 10), nonce)
}

func join(args ...interface{}) string {
s := make([]string, len(args))
for i, v := range args {
Expand All @@ -271,6 +300,13 @@ func join(args ...interface{}) string {
} else {
s[i] = "0"
}
case *big.Rat:
x := v.(*big.Rat)
if x != nil {
s[i] = x.FloatString(9)
} else {
s[i] = "0"
}
default:
panic("Invalid type specified for conversion")
}
Expand Down Expand Up @@ -310,6 +346,20 @@ func (r *RedisClient) GetRoundShares(height int64, nonce string) (map[string]int
return result, nil
}

func (r *RedisClient) GetNShares(height int64, nonce string) (map[string]int64, error) {
result := make(map[string]int64)
cmd := r.client.HGetAllMap(r.formatNShare(height, nonce))
if cmd.Err() != nil {
return nil, cmd.Err()
}
sharesMap, _ := cmd.Result()
for login, v := range sharesMap {
n, _ := strconv.ParseInt(v, 10, 64)
result[login] = n
}
return result, nil
}

func (r *RedisClient) GetPayees() ([]string, error) {
payees := make(map[string]struct{})
var result []string
Expand Down Expand Up @@ -552,13 +602,15 @@ func (r *RedisClient) writeImmatureBlock(tx *redis.Multi, block *BlockData) {
// Redis 2.8.x returns "ERR source and destination objects are the same"
if block.Height != block.RoundHeight {
tx.Rename(r.formatRound(block.RoundHeight, block.Nonce), r.formatRound(block.Height, block.Nonce))
tx.Rename(r.formatNShare(block.RoundHeight, block.Nonce), r.formatNShare(block.Height, block.Nonce))
}
tx.ZRem(r.formatKey("blocks", "candidates"), block.candidateKey)
tx.ZAdd(r.formatKey("blocks", "immature"), redis.Z{Score: float64(block.Height), Member: block.key()})
}

func (r *RedisClient) writeMaturedBlock(tx *redis.Multi, block *BlockData) {
tx.Del(r.formatRound(block.RoundHeight, block.Nonce))
tx.Del(r.formatNShare(block.RoundHeight, block.Nonce))
tx.ZRem(r.formatKey("blocks", "immature"), block.immatureKey)
tx.ZAdd(r.formatKey("blocks", "matured"), redis.Z{Score: float64(block.Height), Member: block.key()})
}
Expand All @@ -578,6 +630,7 @@ func (r *RedisClient) GetMinerStats(login string, maxPayments int64) (map[string
tx.ZRevRangeWithScores(r.formatKey("payments", login), 0, maxPayments-1)
tx.ZCard(r.formatKey("payments", login))
tx.HGet(r.formatKey("shares", "roundCurrent"), login)
tx.LRange(r.formatKey("lastshares"), 0, r.pplns)
return nil
})

Expand All @@ -591,6 +644,15 @@ func (r *RedisClient) GetMinerStats(login string, maxPayments int64) (map[string
stats["paymentsTotal"] = cmds[2].(*redis.IntCmd).Val()
roundShares, _ := cmds[3].(*redis.StringCmd).Int64()
stats["roundShares"] = roundShares

lastnshares := cmds[4].(*redis.StringSliceCmd).Val()
nsh := 0
for _, val := range lastnshares {
if val == login {
nsh++
}
}
stats["lastNShares"] = nsh
}

return stats, nil
Expand Down Expand Up @@ -668,6 +730,7 @@ func (r *RedisClient) CollectStats(smallWindow time.Duration, maxBlocks, maxPaym
tx.ZCard(r.formatKey("blocks", "matured"))
tx.ZCard(r.formatKey("payments", "all"))
tx.ZRevRangeWithScores(r.formatKey("payments", "all"), 0, maxPayments-1)
tx.LLen(r.formatKey("lastshares"))
return nil
})

Expand All @@ -676,6 +739,7 @@ func (r *RedisClient) CollectStats(smallWindow time.Duration, maxBlocks, maxPaym
}

result, _ := cmds[2].(*redis.StringStringMapCmd).Result()
result["lastNShares"] = strconv.FormatInt(cmds[11].(*redis.IntCmd).Val(), 10)
stats["stats"] = convertStringMap(result)
candidates := convertCandidateResults(cmds[3].(*redis.ZSliceCmd))
stats["candidates"] = candidates
Expand Down
2 changes: 1 addition & 1 deletion storage/redis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var r *RedisClient
const prefix = "test"

func TestMain(m *testing.M) {
r = NewRedisClient(&Config{Endpoint: "127.0.0.1:6379"}, prefix)
r = NewRedisClient(&Config{Endpoint: "127.0.0.1:6379"}, prefix, 3000)
reset()
c := m.Run()
reset()
Expand Down
2 changes: 1 addition & 1 deletion www/app/controllers/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default Ember.Controller.extend({

roundPercent: Ember.computed('stats', 'model', {
get() {
var percent = this.get('model.roundShares') / this.get('stats.roundShares');
var percent = this.get('model.lastNShares') / this.get('stats.lastNShares');
if (!percent) {
return 0;
}
Expand Down