Mining/pkg/node/lethean.go
Claude 22c52c0b70
Some checks failed
Test / test (push) Waiting to run
Security Scan / security (push) Failing after 32s
ax(node): replace prose comment with usage example on chain constants
LetheanDefaults comment restated what the constant block name already says (AX
Principle 2). Replaced with concrete call examples showing GetChainInfo and
DiscoverPools usage against both daemon endpoints.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 14:35:46 +01:00

150 lines
4.6 KiB
Go

// Copyright (c) 2017-2026 Lethean (https://lt.hn)
// SPDX-License-Identifier: EUPL-1.2
package node
import (
"bytes"
"encoding/json"
"io"
"net/http"
)
// info, err := node.GetChainInfo(LetheanMainnetDaemon)
// pools := node.DiscoverPools(LetheanTestnetDaemon)
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, err
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
var rpcResponse struct {
Result ChainInfo `json:"result"`
}
if err := json.Unmarshal(responseBody, &rpcResponse); err != nil {
return nil, err
}
rpcResponse.Result.Synced = rpcResponse.Result.Height > 0
return &rpcResponse.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 rpcResponse struct {
Result struct {
Aliases []struct {
Alias string `json:"alias"`
Address string `json:"address"`
Comment string `json:"comment"`
} `json:"aliases"`
} `json:"result"`
}
json.Unmarshal(responseBody, &rpcResponse)
var pools []PoolGateway
for _, alias := range rpcResponse.Result.Aliases {
if !bytes.Contains([]byte(alias.Comment), []byte("pool")) {
continue
}
commentFields := parseComment(alias.Comment)
pools = append(pools, PoolGateway{
Name: alias.Alias,
Address: alias.Address,
Capabilities: commentFields["cap"],
Endpoint: "stratum+tcp://" + alias.Alias + ".lthn:5555",
})
}
return pools
}
// gateways := node.DiscoverGateways("http://127.0.0.1:46941")
// for _, gateway := range gateways { log(gateway.Name, gateway.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 rpcResponse struct {
Result struct {
Aliases []struct {
Alias string `json:"alias"`
Address string `json:"address"`
Comment string `json:"comment"`
} `json:"aliases"`
} `json:"result"`
}
json.Unmarshal(responseBody, &rpcResponse)
var gateways []PoolGateway
for _, alias := range rpcResponse.Result.Aliases {
if !bytes.Contains([]byte(alias.Comment), []byte("type=gateway")) {
continue
}
commentFields := parseComment(alias.Comment)
gateways = append(gateways, PoolGateway{
Name: alias.Alias,
Address: alias.Address,
Capabilities: commentFields["cap"],
Endpoint: alias.Alias + ".lthn",
})
}
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 {
fields := make(map[string]string)
for _, segment := range bytes.Split([]byte(comment), []byte(";")) {
separatorIndex := bytes.IndexByte(segment, '=')
if separatorIndex > 0 {
fields[string(segment[:separatorIndex])] = string(segment[separatorIndex+1:])
}
}
return fields
}