Skip to content

Commit

Permalink
Remove unused gcs and dc endpoints. (#170)
Browse files Browse the repository at this point in the history
The only known caller of the coin supply and download count endpoints was JavaScript running on decred.org. Since the site overhaul, these endpoints are not used.

Further justifying their removal is the fact that coin supply is readily available from dcrdata API, and the download count was a very rough estimate based on a quite basic heuristic.
  • Loading branch information
jholdstock authored Sep 29, 2022
1 parent 68063af commit 0f2d0fd
Show file tree
Hide file tree
Showing 7 changed files with 5 additions and 278 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Check out source
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b #v3.0.2
- name: Install Linters
run: "curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.48.0"
run: "curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.49.0"
- name: Build
run: go build ./...
- name: Test
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# build
FROM golang:1.18-alpine AS builder
FROM golang:1.19-alpine AS builder

WORKDIR $GOPATH/src/github.com/decred/dcrwebapi
COPY . .
Expand Down
44 changes: 0 additions & 44 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,6 @@ For example, to request the `vsp` call:
https://api.decred.org/?c=vsp
```

## Download Count

Uses GitHub API to retrieve download count from
[decred/decred-binaries](https://github.com/decred/decred-binaries) and
[decred/decred-release](https://github.com/decred/decred-release), sums
them together, and rounds the result down to the nearest 1,000.

Example: <https://api.decred.org/?c=dc>

```json
["DownloadsCount","609k"]
```

## Coin Supply

Uses [dcrdata API](https://dcrdata.decred.org/api/supply) to retrieve total
number of Decred mined so far.

Example: https://api.decred.org/?c=gcs

```json
{
"Airdrop":7,
"CoinSupplyMined":11915785,
"CoinSupplyMinedRaw":1191578535131039,
"CoinSupplyTotal":21000000,
"PercentMined":56.7,
"Pos":25.8,
"Pow":51.5,
"Premine":7,
"Subsidy":8.6
}
```

## Get VSP Info

Collects data from a hard-coded list of Voting Service Providers running
Expand Down Expand Up @@ -94,13 +60,3 @@ Example: <https://api.decred.org/?c=gsd>
},
}
```

## Clear Cache

Clear cached data. Can only be called from localhost (`::1` or `127.0.0.1`).

Example: <https://api.decred.org/?c=cc>

```json
{"response": "cache cleared"}
```
11 changes: 2 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@ module github.com/decred/dcrwebapi

go 1.18

require (
github.com/google/go-github/v39 v39.2.0
github.com/gorilla/handlers v1.5.1
)
require github.com/gorilla/handlers v1.5.1

require (
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/google/go-querystring v1.1.0 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
)
require github.com/felixge/httpsnoop v1.0.1 // indirect
25 changes: 0 additions & 25 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,29 +1,4 @@
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ=
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
9 changes: 1 addition & 8 deletions helper.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2017-2018 The Decred developers
// Copyright (c) 2017-2022 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand All @@ -7,7 +7,6 @@ package main
import (
"bytes"
"encoding/json"
"math"
"net/http"
"strings"
)
Expand Down Expand Up @@ -60,9 +59,3 @@ func writeJSONErrorResponse(writer *http.ResponseWriter, err error) {
(*writer).WriteHeader(http.StatusInternalServerError)
(*writer).Write(errorJSON)
}

// round rounding func
func round(f float64, places uint) float64 {
shift := math.Pow(10, float64(places))
return math.Floor(f*shift+.5) / shift
}
190 changes: 0 additions & 190 deletions service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ package main

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"sync"
"time"

"github.com/google/go-github/v39/github"
)

const (
Expand All @@ -27,19 +23,6 @@ const (
StakepoolAPICurrentVersion = 2
)

// CoinSupply represents the data structure returned by the gcs request.
type CoinSupply struct {
Airdrop float64 `json:"Airdrop"`
CoinSupplyMined float64 `json:"CoinSupplyMined"`
CoinSupplyMinedRaw float64 `json:"CoinSupplyMinedRaw"`
CoinSupplyTotal float64 `json:"CoinSupplyTotal"`
PercentMined float64 `json:"PercentMined"`
PoS float64 `json:"Pos"`
PoW float64 `json:"Pow"`
Premine float64 `json:"Premine"`
Subsidy float64 `json:"Subsidy"`
}

// Vsp contains information about a single Voting Service Provider. Includes
// info hard-coded in dcrwebapi and info retrieved from the VSPs /vspinfo
// endpoint.
Expand Down Expand Up @@ -145,23 +128,12 @@ func (set StakepoolSet) MarshalJSON() ([]byte, error) {
return buffer.Bytes(), nil
}

// CacheEntry represents a cache entry with a specified expiry.
type CacheEntry struct {
// Item defines the cached item.
Item interface{}

// Expire contains the item's expiry.
Expiry time.Time
}

// Service represents a dcrweb service.
type Service struct {
// the http client
HTTPClient *http.Client
// the http router
Router *http.ServeMux
// the service cache
Cache sync.Map
// the stakepools
Stakepools StakepoolSet
Vsps vspSet
Expand All @@ -179,7 +151,6 @@ func NewService() *Service {
Timeout: time.Second * 10,
},
Router: http.NewServeMux(),
Cache: sync.Map{},
Mutex: sync.RWMutex{},

Vsps: vspSet{
Expand Down Expand Up @@ -348,121 +319,6 @@ func (service *Service) getHTTP(url string) ([]byte, error) {
return respBody, nil
}

// downloadCount calculates the cummulative download count for DCR binaries and releases
func downloadCount(service *Service) ([]string, error) {
now := time.Now()
entry, hasDc := service.Cache.Load("dc")
if hasDc {
// return cached response if not invalidated
entry, ok := entry.(CacheEntry)
if !ok {
return nil, fmt.Errorf("bad item in dc cache")
}

if now.Before(entry.Expiry) {
resp, ok := entry.Item.([]string)
if !ok {
return nil, fmt.Errorf("bad item")
}
return resp, nil
}
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

client := github.NewClient(nil)
binaries, _, err := client.Repositories.ListReleases(ctx,
"decred", "decred-binaries", nil)
if err != nil {
return nil, err
}

var countB int
for _, binary := range binaries {
for _, asset := range binary.Assets {
countB += asset.GetDownloadCount()
}
}

releases, _, err := client.Repositories.ListReleases(ctx,
"decred", "decred-release", nil)
if err != nil {
return nil, err
}

var countR int
for _, release := range releases {
for _, asset := range release.Assets {
countR += asset.GetDownloadCount()
}
}

count := countB + countR
countStr := fmt.Sprintf("%dk", count/1000)
resp := []string{"DownloadsCount", countStr}
// cache response
cacheEntry := CacheEntry{
Item: resp,
Expiry: now.Add(4 * time.Hour),
}
service.Cache.Store("dc", cacheEntry)

return resp, nil
}

// coinSupply returns the DCR coin supply on mainnet
func coinSupply(service *Service) (*CoinSupply, error) {
now := time.Now()
entry, hasGSC := service.Cache.Load("gsc")
if hasGSC {
// return cached response if not invalidated
entry := entry.(CacheEntry)
if now.Before(entry.Expiry) {
resp := entry.Item.(*CoinSupply)
return resp, nil
}
}

supplyBody, err := service.getHTTP("https://dcrdata.decred.org/api/supply")
if err != nil {
return nil, err
}

var supply map[string]interface{}
err = json.Unmarshal(supplyBody, &supply)
if err != nil {
return nil, err
}

currentCoinSupply := round(supply["supply_mined"].(float64), 1)
airdrop := 840000.0
premine := 840000.0
coinSupplyAvailable := round(currentCoinSupply/100000000, 0)
coinSupplyAfterGenesisBlock := coinSupplyAvailable - airdrop - premine
totalCoinSupply := 21000000.0
resp := &CoinSupply{
PercentMined: round((coinSupplyAvailable/totalCoinSupply)*100, 1),
CoinSupplyMined: coinSupplyAvailable,
CoinSupplyMinedRaw: currentCoinSupply,
CoinSupplyTotal: 21000000,
Airdrop: round(airdrop/coinSupplyAvailable*100, 1),
Premine: round(premine/coinSupplyAvailable*100, 1),
PoS: round((coinSupplyAfterGenesisBlock*.3)/coinSupplyAvailable*100, 1),
PoW: round((coinSupplyAfterGenesisBlock*.6)/coinSupplyAvailable*100, 1),
Subsidy: round((coinSupplyAfterGenesisBlock*.1)/coinSupplyAvailable*100, 1),
}

// cache response
cacheEntry := CacheEntry{
Item: resp,
Expiry: now.Add(1 * time.Minute),
}
service.Cache.Store("gsc", cacheEntry)

return resp, nil
}

// stakepoolStats fetches statistics for a stakepool
func stakepoolStats(service *Service, key string, apiVersion int) error {
var pool Stakepool
Expand Down Expand Up @@ -648,36 +504,6 @@ func (service *Service) HandleRoutes(writer http.ResponseWriter, request *http.R

route := request.FormValue("c")
switch route {
case "dc":
resp, err := downloadCount(service)
if err != nil {
writeJSONErrorResponse(&writer, err)
return
}

respJSON, err := json.Marshal(resp)
if err != nil {
writeJSONErrorResponse(&writer, err)
return
}

writeJSONResponse(&writer, http.StatusOK, &respJSON)
return
case "gcs":
resp, err := coinSupply(service)
if err != nil {
writeJSONErrorResponse(&writer, err)
return
}

respJSON, err := json.Marshal(resp)
if err != nil {
writeJSONErrorResponse(&writer, err)
return
}

writeJSONResponse(&writer, http.StatusOK, &respJSON)
return
case "vsp":
service.Mutex.RLock()
respJSON, err := json.Marshal(service.Vsps)
Expand All @@ -700,22 +526,6 @@ func (service *Service) HandleRoutes(writer http.ResponseWriter, request *http.R

writeJSONResponse(&writer, http.StatusOK, &respJSON)
return
case "cc":
addr, _, err := net.SplitHostPort(request.RemoteAddr)
if err != nil {
writeJSONErrorResponse(&writer, err)
}

if addr == "::1" || addr == "127.0.0.1" {
service.Cache = sync.Map{}
respJSON := []byte(`{"response": "cache cleared"}`)
writeJSONResponse(&writer, http.StatusOK, &respJSON)
return
}

respJSON := []byte(`{"response": "unauthorized"}`)
writeJSONResponse(&writer, http.StatusBadRequest, &respJSON)
return
default:
writer.WriteHeader(http.StatusNotFound)
return
Expand Down

0 comments on commit 0f2d0fd

Please sign in to comment.