Mining/pkg/node/lethean.go
Claude 10241f5803
Some checks failed
Test / test (push) Waiting to run
Security Scan / security (push) Has been cancelled
ax(node): rename Caps to Capabilities on PoolGateway struct
AX Principle 1 — predictable names over short names.
`Caps` is an abbreviation that requires context to understand;
`Capabilities` is self-documenting.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 11:11:41 +01:00

150 lines
4.5 KiB
Go

// Copyright (c) 2017-2026 Lethean (https://lt.hn)
// SPDX-License-Identifier: EUPL-1.2
package node
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
// LetheanDefaults for chain connectivity.
const (
LetheanTestnetDaemon = "http://127.0.0.1:46941"
LetheanMainnetDaemon = "http://127.0.0.1:36941"
LetheanTestnetPool = "stratum+tcp://pool.lthn.io:5555"
LetheanMainnetPool = "stratum+tcp://pool.lthn.io:3333"
)
// info, err := node.GetChainInfo("http://127.0.0.1:46941")
// if info.Synced { log("height:", info.Height) }
type ChainInfo struct {
Height uint64 `json:"height"`
Aliases int `json:"alias_count"`
Synced bool `json:"synced"`
PoolSize int `json:"tx_pool_size"`
Difficulty uint64 `json:"difficulty"`
}
// pools := node.DiscoverPools("http://127.0.0.1:46941")
// for _, pool := range pools { log(pool.Name, pool.Endpoint) }
type PoolGateway struct {
Name string `json:"name"`
Address string `json:"address"`
Capabilities string `json:"caps"`
Endpoint string `json:"endpoint"`
}
// info, err := node.GetChainInfo("http://127.0.0.1:46941")
// if err == nil && info.Synced { log("chain height:", info.Height) }
func GetChainInfo(daemonURL string) (*ChainInfo, error) {
body := `{"jsonrpc":"2.0","id":"0","method":"getinfo","params":{}}`
response, err := http.Post(daemonURL+"/json_rpc", "application/json", bytes.NewReader([]byte(body)))
if err != nil {
return nil, fmt.Errorf("daemon RPC failed: %w", err)
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
var result struct {
Result ChainInfo `json:"result"`
}
if err := json.Unmarshal(responseBody, &result); err != nil {
return nil, fmt.Errorf("parse getinfo: %w", err)
}
result.Result.Synced = result.Result.Height > 0
return &result.Result, nil
}
// pools := node.DiscoverPools("http://127.0.0.1:46941")
// for _, pool := range pools { log(pool.Name, pool.Endpoint) }
func DiscoverPools(daemonURL string) []PoolGateway {
body := `{"jsonrpc":"2.0","id":"0","method":"get_all_alias_details","params":{}}`
response, err := http.Post(daemonURL+"/json_rpc", "application/json", bytes.NewReader([]byte(body)))
if err != nil {
return nil
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
var result struct {
Result struct {
Aliases []struct {
Alias string `json:"alias"`
Address string `json:"address"`
Comment string `json:"comment"`
} `json:"aliases"`
} `json:"result"`
}
json.Unmarshal(responseBody, &result)
var pools []PoolGateway
for _, alias := range result.Result.Aliases {
if !bytes.Contains([]byte(alias.Comment), []byte("pool")) {
continue
}
parsed := parseComment(alias.Comment)
pools = append(pools, PoolGateway{
Name: alias.Alias,
Address: alias.Address,
Capabilities: parsed["cap"],
Endpoint: fmt.Sprintf("stratum+tcp://%s.lthn:5555", alias.Alias),
})
}
return pools
}
// gateways := node.DiscoverGateways("http://127.0.0.1:46941")
// for _, gw := range gateways { log(gw.Name, gw.Endpoint) }
func DiscoverGateways(daemonURL string) []PoolGateway {
body := `{"jsonrpc":"2.0","id":"0","method":"get_all_alias_details","params":{}}`
response, err := http.Post(daemonURL+"/json_rpc", "application/json", bytes.NewReader([]byte(body)))
if err != nil {
return nil
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
var result struct {
Result struct {
Aliases []struct {
Alias string `json:"alias"`
Address string `json:"address"`
Comment string `json:"comment"`
} `json:"aliases"`
} `json:"result"`
}
json.Unmarshal(responseBody, &result)
var gateways []PoolGateway
for _, alias := range result.Result.Aliases {
if !bytes.Contains([]byte(alias.Comment), []byte("type=gateway")) {
continue
}
parsed := parseComment(alias.Comment)
gateways = append(gateways, PoolGateway{
Name: alias.Alias,
Address: alias.Address,
Capabilities: parsed["cap"],
Endpoint: fmt.Sprintf("%s.lthn", alias.Alias),
})
}
return gateways
}
// parsed := parseComment("cap=pool;endpoint=stratum+tcp://pool.lthn.io:3333")
// parsed["cap"] // "pool"
// parsed["endpoint"] // "stratum+tcp://pool.lthn.io:3333"
func parseComment(comment string) map[string]string {
result := make(map[string]string)
for _, part := range bytes.Split([]byte(comment), []byte(";")) {
separatorIndex := bytes.IndexByte(part, '=')
if separatorIndex > 0 {
result[string(part[:separatorIndex])] = string(part[separatorIndex+1:])
}
}
return result
}