From baa688c2b8bfe0da02ce4b732201a7cc6c9da5b2 Mon Sep 17 00:00:00 2001 From: xenowits Date: Sat, 12 Feb 2022 23:16:20 +0530 Subject: [PATCH] add near protocol --- .gitignore | 1 + core/chains/avalanche.go | 1 + core/chains/near.go | 94 ++++++++++++++++++++++++++++++++++++++++ core/main.go | 47 ++++++++++++-------- db/postgres_script.sql | 2 + 5 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 core/chains/near.go diff --git a/.gitignore b/.gitignore index 5e8c8e4..03e2e6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ web/node_modules/ web/.cache/ web/public/ +.idea diff --git a/core/chains/avalanche.go b/core/chains/avalanche.go index 9cf85ef..e81e716 100644 --- a/core/chains/avalanche.go +++ b/core/chains/avalanche.go @@ -31,6 +31,7 @@ type AvalancheErrorResponse struct { Error string `json:"error"` } +// Avalanche calculates nakamoto coefficient for the `Avalanche` C-Chain // In AVAX, stake amounts are already multiplied by 10^9 // So, we need to deal with big numbers here. // Else, if we divide each value with 10^9, we have to deal with fractional numbers which is worse. diff --git a/core/chains/near.go b/core/chains/near.go new file mode 100644 index 0000000..4199e00 --- /dev/null +++ b/core/chains/near.go @@ -0,0 +1,94 @@ +package chains + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/xenowits/nakamoto-coefficient-calculator/core/utils" + "io/ioutil" + "log" + "math/big" + "net/http" + "sort" +) + +type NearResponse struct { + Jsonrpc string `json:"jsonrpc"` + Id int `json:"id"` + Result struct { + Validators []struct { + AccountId string `json:"account_id"` + Stake string `json:"stake"` + } `json:"current_validators"` + } `json:"result"` +} + +func Near() (int, error) { + votingPowers := make([]big.Int, 0, 1024) + + url := fmt.Sprintf("https://rpc.mainnet.near.org") + jsonReqData := []byte(`{"jsonrpc": "2.0","method": "validators","params":[null],"id":1}`) + + // Create a new POST request using http + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonReqData)) + if err != nil { + return -1, err + } + req.Header.Set("Content-Type", "application/json") + + // Send req using http Client + client := &http.Client{} + resp, err := client.Do(req) + + if resp != nil { + // Need to close body when redirection occurs + // In redirection, response is not empty + defer func() { + if err := resp.Body.Close(); err != nil { + log.Printf("failed to close response body") + } + }() + } + + if err != nil { + return -1, err + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return -1, err + } + + var response NearResponse + err = json.Unmarshal(body, &response) + if err != nil { + return -1, err + } + + // loop through the validators voting powers + for _, ele := range response.Result.Validators { + n, ok := new(big.Int).SetString(ele.Stake, 10) + if !ok { + return -1, fmt.Errorf("failed to parse string %s", ele.Stake) + } + votingPowers = append(votingPowers, *n) + } + + // need to sort the powers in descending order since they are in random order + sort.Slice(votingPowers, func(i, j int) bool { + res := (&votingPowers[i]).Cmp(&votingPowers[j]) + if res == 1 { + return true + } + return false + }) + + totalVotingPower := utils.CalculateTotalVotingPowerBigNums(votingPowers) + fmt.Println("Total voting power:", totalVotingPower) + + // now we're ready to calculate the nakamoto coefficient + nakamotoCoefficient := utils.CalcNakamotoCoefficientBigNums(totalVotingPower, votingPowers) + fmt.Println("The Nakamoto coefficient for near protocol is", nakamotoCoefficient) + + return nakamotoCoefficient, nil +} diff --git a/core/main.go b/core/main.go index 45ab930..e2f1e8f 100644 --- a/core/main.go +++ b/core/main.go @@ -16,22 +16,26 @@ func main() { var err error conn, err = pgx.Connect(context.Background(), os.Getenv("DATABASE_URL")) if err != nil { - fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err) + fmt.Fprintln(os.Stderr, "Unable to connect to database", err) os.Exit(1) } - defer conn.Close(context.Background()) - networks := []string{"BNB", "ATOM", "OSMO", "MATIC", "MINA", "SOL", "AVAX", "LUNA", "GRT", "RUNE"} + defer func() { + if err := conn.Close(context.Background()); err != nil { + log.Println("failed to close database connection", err) + } + }() + networks := []string{"BNB", "ATOM", "OSMO", "MATIC", "MINA", "SOL", "AVAX", "LUNA", "GRT", "RUNE", "NEAR"} for _, n := range networks { UpdateChainInfo(n) } } -func UpdateChainInfo(chain_token string) { - prevVal, currVal := GetPrevVal(chain_token), 0 +func UpdateChainInfo(chainToken string) { + prevVal, currVal := getPrevVal(chainToken), 0 var err error - switch chain_token { + switch chainToken { case "BNB": currVal, err = chains.Binance() case "ATOM": @@ -52,33 +56,38 @@ func UpdateChainInfo(chain_token string) { currVal, err = chains.Graph() case "RUNE": currVal, err = chains.Thorchain() + case "NEAR": + currVal, err = chains.Near() } if err != nil { - log.Println("Error occurred for", chain_token, err) - } else { - SaveUpdatedVals(currVal, prevVal, chain_token) + log.Println("failed to update chain info", chainToken, err) + } + + if err := saveUpdatedVals(currVal, prevVal, chainToken); err != nil { + log.Println("failed to save updated values to database", chainToken, err) } } -// Query the database to get the previous (prior to updating it now) value of nakamoto coefficient for the given chain -func GetPrevVal(chain_token string) int { +// GetPrevVal queries the database to get the previous (prior to updating it now) value of nakamoto coefficient for the given chain +// Assumes row for chain already exists in the table +func getPrevVal(chainToken string) int { queryStmt := `SELECT naka_co_curr_val from naka_coefficients WHERE chain_token=$1` - var naka_co_prev_val int - if err := conn.QueryRow(context.Background(), queryStmt, chain_token).Scan(&naka_co_prev_val); err == nil { + var nakaCoeffPrevVal int + if err := conn.QueryRow(context.Background(), queryStmt, chainToken).Scan(&nakaCoeffPrevVal); err == nil { } else { - fmt.Println("Read unsuccessful for "+chain_token, err) + fmt.Println("Read unsuccessful", chainToken, err) return -1 } - return naka_co_prev_val + return nakaCoeffPrevVal } -// Save the recently calculated values back to the database -func SaveUpdatedVals(curr_val int, prev_val int, chain_token string) error { +// SaveUpdatedVals saves the recently calculated values back to the database +func saveUpdatedVals(currVal int, prevVal int, chainToken string) error { queryStmt := `UPDATE naka_coefficients SET naka_co_curr_val=$1, naka_co_prev_val=$2 WHERE chain_token=$3` - _, err := conn.Exec(context.Background(), queryStmt, curr_val, prev_val, chain_token) + _, err := conn.Exec(context.Background(), queryStmt, currVal, prevVal, chainToken) if err != nil { - fmt.Println("Write unsuccessful for "+chain_token, err) + fmt.Println("Write unsuccessful for "+chainToken, err) } return err } diff --git a/db/postgres_script.sql b/db/postgres_script.sql index c3fc062..fa374a3 100644 --- a/db/postgres_script.sql +++ b/db/postgres_script.sql @@ -46,6 +46,8 @@ INSERT INTO naka_coefficients (chain_name, chain_token, naka_co_prev_val, naka_c INSERT INTO naka_coefficients (chain_name, chain_token, naka_co_prev_val, naka_co_curr_val) VALUES ('Thorchain', 'RUNE', -1, 10); +INSERT INTO naka_coefficients (chain_name, chain_token, naka_co_prev_val, naka_co_curr_val) VALUES ('Near', 'NEAR', -1, 6); + select * from naka_coefficients; \q