Created
April 13, 2023 20:00
-
-
Save ryan-u410/3068be2fd6515c59be3c3785b75f4faf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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