Skip to content

Instantly share code, notes, and snippets.

@ryan-u410
Created April 13, 2023 20:00
Show Gist options
  • Save ryan-u410/3068be2fd6515c59be3c3785b75f4faf to your computer and use it in GitHub Desktop.
Save ryan-u410/3068be2fd6515c59be3c3785b75f4faf to your computer and use it in GitHub Desktop.
package main
import (
"encoding/json"
"fmt"
"io"
"math"
"net/http"
"strconv"
)
func main() {
resp, err := GetNeuronInfo("5F4tQyWrhfGVcNhoqeiNsR6KjD4wMZ2kfhLj4oHYuyHbZAc3")
if err != nil {
panic(err) // TODO: don't panic here
}
PrettyPrint(resp)
}
func getRequest(url string, resp interface{}) error {
res, resErr := http.Get(url)
if resErr != nil {
return resErr
}
if res.StatusCode != http.StatusOK {
return fmt.Errorf("%v", res.StatusCode)
}
defer func() {
closeErr := res.Body.Close()
if closeErr != nil {
panic(closeErr)
}
}()
body, readErr := io.ReadAll(res.Body)
if readErr != nil {
return readErr
}
jsonErr := json.Unmarshal(body, resp)
if jsonErr != nil {
return jsonErr
}
return nil
}
func GetNodeVersion() (*NodeVersionResponse, error) {
url := "http://localhost:8080/node/version"
resp := new(NodeVersionResponse)
err := getRequest(url, &resp)
return resp, err
}
func GetRuntimeSpec() (*RuntimeSpecResponse, error) {
url := "http://localhost:8080/runtime/spec"
resp := new(RuntimeSpecResponse)
err := getRequest(url, &resp)
return resp, err
}
func getNodeNetwork() (*NodeNetworkResponse, error) {
url := "http://localhost:8080/node/network"
resp := new(NodeNetworkResponse)
err := getRequest(url, &resp)
return resp, err
}
func getBalanceInfoForAddress(address string) (*BalanceInfoResponse, error) {
url := fmt.Sprintf("http://localhost:8080/accounts/%s/balance-info", address)
resp := new(BalanceInfoResponse)
err := getRequest(url, &resp)
return resp, err
}
func getHotKeyOwner(address string) (*StorageResponse, error) {
url := fmt.Sprintf("http://localhost:8080/pallets/subtensorModule/storage/Owner?keys[]=%s", address)
resp := new(StorageResponse)
err := getRequest(url, &resp)
return resp, err
}
func getStorageItemSingleKeySingleValue(item string, key interface{}) (*StorageResponseSingleValue, error) {
url := fmt.Sprintf("http://localhost:8080/pallets/subtensorModule/storage/%v?keys[]=%v", item, key)
resp := new(StorageResponseSingleValue)
err := getRequest(url, &resp)
return resp, err
}
func getStorageItemMultiKeySingleValue(item string, key1, key2 interface{}) (*StorageResponseSingleValue, error) {
url := fmt.Sprintf("http://localhost:8080/pallets/subtensorModule/storage/%v?keys[]=%v&keys=%v", item, key1, key2)
resp := new(StorageResponseSingleValue)
err := getRequest(url, &resp)
return resp, err
}
func getStorageItemSingleKeyMultiValue(item string, key interface{}) (*StorageResponseMultiValue, error) {
url := fmt.Sprintf("http://localhost:8080/pallets/subtensorModule/storage/%v?keys[]=%v", item, key)
resp := new(StorageResponseMultiValue)
err := getRequest(url, &resp)
return resp, err
}
func getStorageItemSingleKeyMultiBoolValue(item string, key int) (*StorageResponseMultiBoolValue, error) {
url := fmt.Sprintf("http://localhost:8080/pallets/subtensorModule/storage/%v?keys[]=%v", item, key)
resp := new(StorageResponseMultiBoolValue)
err := getRequest(url, &resp)
return resp, err
}
func PrettyPrint(i interface{}) {
s, ignored := json.MarshalIndent(i, "", "\t")
if ignored != nil {
fmt.Println(string(s))
}
fmt.Println(string(s))
}
type NodeNetworkResponse struct {
NodeRoles []struct {
Full interface{} `json:"full"`
} `json:"nodeRoles"`
NumPeers string `json:"numPeers"`
IsSyncing bool `json:"isSyncing"`
ShouldHavePeers bool `json:"shouldHavePeers"`
LocalPeerID string `json:"localPeerId"`
LocalListenAddresses []string `json:"localListenAddresses"`
PeersInfo string `json:"peersInfo"`
}
type NodeVersionResponse struct {
ClientVersion string `json:"clientVersion"`
ClientImplName string `json:"clientImplName"`
Chain string `json:"chain"`
}
type RuntimeSpecResponse struct {
At struct {
Height string `json:"height"`
Hash string `json:"hash"`
} `json:"at"`
AuthoringVersion string `json:"authoringVersion"`
TransactionVersion string `json:"transactionVersion"`
ImplVersion string `json:"implVersion"`
SpecName string `json:"specName"`
SpecVersion string `json:"specVersion"`
ChainType struct {
Live interface{} `json:"live"`
} `json:"chainType"`
Properties struct {
Ss58Format string `json:"ss58Format"`
TokenDecimals []string `json:"tokenDecimals"`
TokenSymbol []string `json:"tokenSymbol"`
} `json:"properties"`
}
type BalanceInfoResponse struct {
At struct {
Hash string `json:"hash"`
Height string `json:"height"`
} `json:"at"`
Nonce string `json:"nonce"`
TokenSymbol string `json:"tokenSymbol"`
Free string `json:"free"`
Reserved string `json:"reserved"`
MiscFrozen string `json:"miscFrozen"`
FeeFrozen string `json:"feeFrozen"`
Locks []interface{} `json:"locks"`
}
type StorageResponse struct {
At struct {
Hash string `json:"hash"`
Height string `json:"height"`
} `json:"at"`
Pallet string `json:"pallet"`
PalletIndex string `json:"palletIndex"`
StorageItem string `json:"storageItem"`
Keys []string `json:"keys"`
Value string `json:"value"`
}
type PalletStorageItemsResponse struct {
At struct {
Hash string `json:"hash"`
Height string `json:"height"`
} `json:"at"`
Pallet string `json:"pallet"`
PalletIndex string `json:"palletIndex"`
Items []string `json:"items"`
}
type StorageResponseUnknownValue struct {
At struct {
Hash string `json:"hash"`
Height string `json:"height"`
} `json:"at"`
Pallet string `json:"pallet"`
PalletIndex string `json:"palletIndex"`
StorageItem string `json:"storageItem"`
Value interface{} `json:"value"`
}
type StorageResponseSingleValue struct {
At struct {
Hash string `json:"hash"`
Height string `json:"height"`
} `json:"at"`
Pallet string `json:"pallet"`
PalletIndex string `json:"palletIndex"`
StorageItem string `json:"storageItem"`
Value string `json:"value"`
}
type StorageResponseMultiValue struct {
At struct {
Hash string `json:"hash"`
Height string `json:"height"`
} `json:"at"`
Pallet string `json:"pallet"`
PalletIndex string `json:"palletIndex"`
StorageItem string `json:"storageItem"`
Value []string `json:"value"`
}
type StorageResponseMultiBoolValue struct {
At struct {
Hash string `json:"hash"`
Height string `json:"height"`
} `json:"at"`
Pallet string `json:"pallet"`
PalletIndex string `json:"palletIndex"`
StorageItem string `json:"storageItem"`
Value []bool `json:"value"`
}
func (r RuntimeSpecResponse) DecimalsForSymbol(token string) int64 {
decimalString := ""
for n := range r.Properties.TokenSymbol {
sym := r.Properties.TokenSymbol[n]
if sym == token {
decimalString = r.Properties.TokenDecimals[n]
}
}
if decimalString == "" {
panic("decimals not found for given token symbol; consider returning an error instead of a panic here.")
}
num, err := strconv.ParseInt(decimalString, 10, 0)
if err != nil {
panic(err) // TODO: don't panic here.
}
return num
}
func (b BalanceInfoResponse) BalanceFree() float64 {
num, err := strconv.ParseFloat(b.Free, 64)
if err != nil {
panic(err) // TODO: don't panic here.
}
return num
}
type NeuronInfo struct {
UID int
HotKey string
ColdKey string
Stake float64
Rank float64
VTrust float64
Trust float64
Consensus float64
Incentive float64
Dividends float64
Emission float64
Updated float64
VPermit bool
Active bool
Height float64
}
func (n NeuronInfo) VPermitNum() float64 {
if n.VPermit {
return 1.0
}
return 0.0
}
func (n NeuronInfo) ActiveNum() float64 {
if n.Active {
return 1.0
}
return 0.0
}
// https://github.com/opentensor/bittensor/blob/a5f515e0b3582aa9315dc984742682aa67580124/bittensor/_subtensor/chain_data.py#L298
func GetNeuronInfo(address string) (*NeuronInfo, error) { // nolint
netuid := 3
var data = &NeuronInfo{
UID: 0,
HotKey: address,
ColdKey: "",
Stake: 0,
Rank: 0,
VTrust: 0,
Trust: 0,
Consensus: 0,
Incentive: 0,
Dividends: 0,
Emission: 0,
Updated: 0,
VPermit: false,
Active: false,
Height: 0,
}
resp, err := getStorageItemSingleKeySingleValue("Owner", address)
if err != nil {
return nil, err
}
data.ColdKey = resp.Value
resp, err = getStorageItemSingleKeySingleValue("totalHotkeyStake", address)
if err != nil {
return nil, err
}
data.Stake = MustParseFloat64(resp.Value) / RAOPERTAO()
resp, err = getStorageItemMultiKeySingleValue("uids", netuid, address)
if err != nil {
panic(err) // TODO: don't panic here
}
data.UID = int(MustParseInt64(resp.Value))
rank, rankErr := getStorageItemSingleKeyMultiValue("rank", netuid)
if rankErr != nil {
panic(rankErr)
}
data.Rank = MustParseNormalizedFloat(rank.Value[data.UID])
emission, emissionErr := getStorageItemSingleKeyMultiValue("emission", netuid)
if emissionErr != nil {
panic(emissionErr)
}
data.Emission = MustParseFloat64(emission.Value[data.UID]) / RAOPERTAO()
incentive, incentiveErr := getStorageItemSingleKeyMultiValue("incentive", netuid)
if incentiveErr != nil {
panic(incentiveErr)
}
data.Incentive = MustParseNormalizedFloat(incentive.Value[data.UID])
consensus, consensusErr := getStorageItemSingleKeyMultiValue("consensus", netuid)
if consensusErr != nil {
panic(consensusErr)
}
data.Consensus = MustParseNormalizedFloat(consensus.Value[data.UID])
trust, trustErr := getStorageItemSingleKeyMultiValue("trust", netuid)
if trustErr != nil {
panic(trustErr)
}
data.Trust = MustParseNormalizedFloat(trust.Value[data.UID])
validatorTrust, validatorTrustErr := getStorageItemSingleKeyMultiValue("validatorTrust", netuid)
if validatorTrustErr != nil {
panic(validatorTrustErr)
}
data.VTrust = MustParseNormalizedFloat(validatorTrust.Value[data.UID])
dividends, dividendsErr := getStorageItemSingleKeyMultiValue("dividends", netuid)
if dividendsErr != nil {
panic(dividendsErr)
}
data.Dividends = MustParseNormalizedFloat(dividends.Value[data.UID])
active, activeErr := getStorageItemSingleKeyMultiBoolValue("active", netuid)
if activeErr != nil {
panic(activeErr)
}
data.Active = active.Value[data.UID]
validatorPermit, validatorPermitErr := getStorageItemSingleKeyMultiBoolValue("validatorPermit", netuid)
if validatorPermitErr != nil {
panic(validatorPermitErr)
}
data.VPermit = validatorPermit.Value[data.UID]
lastUpdate, lastUpdateErr := getStorageItemSingleKeyMultiValue("lastUpdate", netuid)
if lastUpdateErr != nil {
panic(lastUpdateErr)
}
data.Updated = MustParseFloat64(lastUpdate.At.Height) - MustParseFloat64(lastUpdate.Value[data.UID])
data.Height = MustParseFloat64(lastUpdate.At.Height)
return data, nil
}
// https://github.com/opentensor/bittensor/blob/a5f515e0b3582aa9315dc984742682aa67580124/bittensor/_subtensor/chain_data.py#L153
func RAOPERTAO() float64 {
spec, specErr := GetRuntimeSpec()
if specErr != nil {
panic(specErr)
}
return math.Pow10(int(spec.DecimalsForSymbol("TAO")))
}
func MustParseFloat64(str string) float64 {
num, err := strconv.ParseFloat(str, 64)
if err != nil {
panic(err)
}
return num
}
// https://github.com/opentensor/bittensor/blob/a5f515e0b3582aa9315dc984742682aa67580124/bittensor/utils/__init__.py#L229
func MustParseNormalizedFloat(str string) float64 {
f := MustParseFloat64(str)
return f / math.MaxUint16
}
func MustParseInt64(str string) int64 {
num, err := strconv.ParseInt(str, 10, 64)
if err != nil {
panic(err)
}
return num
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment