// 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 }