Skip to content

Commit

Permalink
add new RPC APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
swordlet committed May 14, 2024
1 parent 4270c1e commit e1d5665
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 11 deletions.
6 changes: 3 additions & 3 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"listen": "0.0.0.0:8082",
"login": "admin",
"password": "",
"hideIP": false
"hideIP": true
},
"kvrocks": {
"endpoint": "127.0.0.1:6379",
Expand All @@ -56,8 +56,8 @@
},
"payout": {
"poolRation": 5.0,
"rewardRation": 5.0,
"directRation": 5.0,
"rewardRation": 0,
"directRation": 0,
"threshold": 3,
"paymentInterval": "10m",
"mode": "equal",
Expand Down
17 changes: 9 additions & 8 deletions kvstore/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,20 @@ func (r *KvClient) GetPoolRewardsList(start, end int64) ([]PoolRewardsData, erro
func (r *KvClient) GetMinerAccount(address string) (float64, float64, float64, error) {
reward, err := r.client.HGet(ctx, r.formatKey("account", address), "reward").Int64()
if err != nil {
util.Error.Println("get miner total donate error", err, address)
return 0, 0, 0, err
util.Error.Println("get miner total reward error", err, address)
return 0, 0, 0, nil
}

payment, err := r.client.HGet(ctx, r.formatKey("account", address), "payment").Int64()
if err != nil {
util.Error.Println("get miner total payment error", err, address)
return 0, 0, 0, err
}
unpaid, err := r.client.HGet(ctx, r.formatKey("account", address), "unpaid").Int64()
if err != nil {
util.Error.Println("get miner total unpaid error", err, address)
return 0, 0, 0, err
return float64(reward) / 1e9, 0, 0, nil
}

payment, err := r.client.HGet(ctx, r.formatKey("account", address), "payment").Int64()
if err != nil {
util.Error.Println("get miner total payment error", err, address)
return float64(reward) / 1e9, 0, float64(unpaid) / 1e9, nil
}
return float64(reward) / 1e9, float64(payment) / 1e9, float64(unpaid) / 1e9, nil
}
Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ func startFrontend(cfg *pool.Config, s *stratum.StratumServer) {
apiServer.Add("xdag_getPoolWorkers", s.XdagGetPoolWorkers)
apiServer.Add("xdag_poolConfig", s.XdagPoolConfig)
apiServer.Add("xdag_updatePoolConfig", s.XdagUpdatePoolConfig)
apiServer.Add("xdag_minerAccount", s.XdagMinerAccount)
apiServer.Add("xdag_minerHashrate", s.XdagMinerHashrate)
apiServer.Add("xdag_poolHashrate", s.XdagPoolHashrate)

err := apiServer.Run(cfg.Frontend.Listen)
if err != nil {
Expand Down
207 changes: 207 additions & 0 deletions stratum/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,3 +824,210 @@ func (s *StratumServer) XdagGetPoolWorkers(id uint64, params json.RawMessage) jr

return jrpc.EncodeResponse(id, rec, nil)
}

type MinerAccount struct {
Address string `json:"address"`
Timestamp int64 `json:"timestamp"`
TotalReward float64 `json:"total_reward"`
TotalPayment float64 `json:"total_payment"`
TotalUnpaid float64 `json:"total_unpaid"`
}

func (s *StratumServer) XdagMinerAccount(id uint64, params json.RawMessage) jrpc.Response {
now := util.MakeTimestamp()

var args []json.RawMessage
if err := json.Unmarshal(params, &args); err != nil {
return jrpc.EncodeResponse(id, struct{}{}, err)
}
if len(args) != 1 {
return jrpc.EncodeResponse(id, struct{}{}, errors.New("params length error"))
}

var address string
err := json.Unmarshal(args[0], &address)
if err != nil {
return jrpc.EncodeResponse(id, struct{}{}, err)
}

if address == "" || !util.ValidateAddress(address) {
return jrpc.EncodeResponse(id, struct{}{}, errors.New("addres is empty or invalid"))
}
reward, payment, unpaid, err := s.backend.GetMinerAccount(address)
if err != nil {
return jrpc.EncodeResponse(id, struct{}{}, err)
}

data := MinerAccount{
Address: address,
Timestamp: now,
TotalReward: reward,
TotalPayment: payment,
TotalUnpaid: unpaid,
}
return jrpc.EncodeResponse(id, data, nil)
}

type MinerHashrate struct {
Address string `json:"address"`
Timestamp int64 `json:"timestamp"`
TotalHashrate float64 `json:"total_hashrate"`
TotalHashrate24h float64 `json:"total_hashrate24h"`
TotalOnline int `json:"total_online"`
Hashrate []WorkerHashrate `json:"hashrate"`
}

type WorkerHashrate struct {
Name string `json:"name"`
Hashrate float64 `json:"hashrate"`
Hashrate24h float64 `json:"hashrate24h"`
LastBeat int64 `json:"lastBeat"`
ValidShares int64 `json:"validShares"`
StaleShares int64 `json:"staleShares"`
InvalidShares int64 `json:"invalidShares"`
Accepts int64 `json:"accepts"`
Rejects int64 `json:"rejects"`
Ip string `json:"ip"`
Warning bool `json:"warning"`
Timeout bool `json:"timeout"`
}

func (s *StratumServer) XdagMinerHashrate(id uint64, params json.RawMessage) jrpc.Response {
now := util.MakeTimestamp()

var args []json.RawMessage
if err := json.Unmarshal(params, &args); err != nil {
return jrpc.EncodeResponse(id, struct{}{}, err)
}
if len(args) != 1 {
return jrpc.EncodeResponse(id, struct{}{}, errors.New("params length error"))
}

var address string
err := json.Unmarshal(args[0], &address)
if err != nil {
return jrpc.EncodeResponse(id, struct{}{}, err)
}

if address == "" || !util.ValidateAddress(address) {
return jrpc.EncodeResponse(id, struct{}{}, errors.New("addres is empty or invalid"))
}
workers := s.workers.GetWorkers(address)
if len(workers) == 0 {
return jrpc.EncodeResponse(id, struct{}{}, errors.New("no workers"))
}

var result []WorkerHashrate
window24h := 24 * time.Hour

minerHashrate := MinerHashrate{
Address: address,
Timestamp: now,
}

dataChan := make(chan WorkerHashrate, len(workers))

for _, w := range workers {
go func(adress, w string) {
m, ok := s.miners.Get(address + "." + w)
if !ok {
dataChan <- WorkerHashrate{}
return
}

lastBeat := m.getLastBeat()
hashrate := m.hashrate(s.estimationWindow)
hashrate24h := m.hashrate(window24h)
stats := WorkerHashrate{
Name: w,
Hashrate: hashrate,
Hashrate24h: hashrate24h,
LastBeat: lastBeat,
ValidShares: atomic.LoadInt64(&m.validShares),
StaleShares: atomic.LoadInt64(&m.staleShares),
InvalidShares: atomic.LoadInt64(&m.invalidShares),
Accepts: atomic.LoadInt64(&m.accepts),
Rejects: atomic.LoadInt64(&m.rejects),
}
if !s.config.Frontend.HideIP {
stats.Ip = m.ip
} else {
stats.Ip = "-"

}

if now-lastBeat > (int64(s.timeout/2) / 1000000) {
stats.Warning = true
}
if now-lastBeat > (int64(s.timeout) / 1000000) {
stats.Timeout = true
}
dataChan <- stats
}(address, w)
}

i := 0
var hashrate float64
var hashrate24h float64
totalOnline := 0

for {
select {
case <-time.After(10 * time.Second):
return jrpc.EncodeResponse(id, struct{}{}, errors.New("timeout"))
case v := <-dataChan:
if v.Name != "" {
result = append(result, v)
}
i++
hashrate += v.Hashrate
hashrate24h += hashrate24h
if !v.Timeout {
totalOnline++
}

if i == len(workers) {
minerHashrate.TotalHashrate = hashrate
minerHashrate.TotalHashrate24h = hashrate24h
minerHashrate.TotalOnline = totalOnline
minerHashrate.Hashrate = result
return jrpc.EncodeResponse(id, minerHashrate, nil)
}
}
}

}

type PoolHashrate struct {
Hashrate float64 `json:"hashrate"`
Hashrate24h float64 `json:"hashrate24h"`
Total int `json:"total"`
TotalOnline int `json:"total_online"`
}

func (s *StratumServer) XdagPoolHashrate(id uint64, params json.RawMessage) jrpc.Response {
now := util.MakeTimestamp()
totalhashrate := float64(0)
totalhashrate24h := float64(0)
total := 0
totalOnline := 0
window24h := 24 * time.Hour

for m := range s.miners.Iter() {
lastBeat := m.Val.getLastBeat()
hashrate := m.Val.hashrate(s.estimationWindow)
hashrate24h := m.Val.hashrate(window24h)
totalhashrate += hashrate
totalhashrate24h += hashrate24h
total++
if now-lastBeat <= (int64(s.timeout) / 1000000) {
totalOnline++
}
}
return jrpc.EncodeResponse(id, PoolHashrate{
Hashrate: totalhashrate,
Hashrate24h: totalhashrate24h,
Total: total,
TotalOnline: totalOnline,
}, nil)
}

0 comments on commit e1d5665

Please sign in to comment.