Browse Source

New Update Network setting (classic|mordor)

master
yuriy0803 5 years ago
parent
commit
cd1ff104e5
  1. 2
      .travis.yml
  2. 9
      Makefile
  3. 303
      README.md
  4. 120
      api.json
  5. 17
      api/server.go
  6. 113
      configs/api.json
  7. 18
      configs/nginx.default.example
  8. 113
      configs/payout.json
  9. 109
      configs/stratum2b.json
  10. 114
      configs/stratum4b.json
  11. 114
      configs/stratum8b.json
  12. 114
      configs/stratum9b.json
  13. 113
      configs/unlocker.json
  14. 16
      docs/PAYOUTS.md
  15. 4
      docs/STRATUM.md
  16. 21
      go.mod
  17. 563
      go.sum
  18. 10
      main.go
  19. 19
      misc/nginx-default.conf
  20. 26
      misc/upstart.conf
  21. 14
      payouts/payer.go
  22. 172
      payouts/unlocker.go
  23. 131
      payouts/unlocker_test.go
  24. 4
      policy/policy.go
  25. 4
      proxy/blocks.go
  26. 25
      proxy/config.go
  27. 25
      proxy/handlers.go
  28. 37
      proxy/miner.go
  29. 6
      proxy/proto.go
  30. 23
      proxy/proxy.go
  31. 21
      proxy/stratum.go
  32. 4
      rpc/rpc.go
  33. 2
      scripts/start_2_bil.sh
  34. 108
      service_installer.sh
  35. 238
      storage/redis.go
  36. 2
      util/util.go
  37. 2
      www/.gitignore
  38. 1
      www/README.md
  39. 0
      www/app/.watchmanconfig
  40. 58
      www/app/adapters/chart.js
  41. 94
      www/app/components/chart-diff.js
  42. 69
      www/app/controllers/account.js
  43. 103
      www/app/controllers/account/index.js
  44. 50
      www/app/controllers/account/payouts.js
  45. 23
      www/app/controllers/application.js
  46. 41
      www/app/controllers/index.js
  47. 5
      www/app/helpers/equals.js
  48. 8
      www/app/helpers/format-difficulty.js
  49. 30
      www/app/helpers/worker-colorizer.js
  50. 9
      www/app/helpers/worker-earnperday.js
  51. 33
      www/app/index.html
  52. 18
      www/app/models/block.js
  53. 21
      www/app/models/chart.js
  54. 3
      www/app/router.js
  55. 72
      www/app/routes/application.js
  56. 7
      www/app/routes/chart.js
  57. 8
      www/app/serializers/chart.js
  58. 355
      www/app/styles/app.css
  59. 55
      www/app/templates/account.hbs
  60. 31
      www/app/templates/account/index.hbs
  61. 14
      www/app/templates/account/payouts.hbs
  62. 2
      www/app/templates/account/rewards.hbs
  63. 4
      www/app/templates/application-error.hbs
  64. 91
      www/app/templates/application.hbs
  65. 19
      www/app/templates/blocks.hbs
  66. 17
      www/app/templates/blocks/block.hbs
  67. 40
      www/app/templates/blocks/immature.hbs
  68. 39
      www/app/templates/blocks/index.hbs
  69. 12
      www/app/templates/blocks/pending.hbs
  70. 3
      www/app/templates/components/chart-diff.hbs
  71. 131
      www/app/templates/help.hbs
  72. 198
      www/app/templates/index.hbs
  73. 8
      www/app/templates/luck.hbs
  74. 16
      www/app/templates/miners.hbs
  75. 22
      www/app/templates/payments.hbs
  76. 6
      www/build.sh
  77. 53
      www/config/ember-intl.js
  78. 70
      www/config/environment.js
  79. 80
      www/fix/intl-format-cache/lib/es5.js
  80. 1
      www/fix/intl-format-cache/lib/es5.js.map
  81. 83
      www/fix/intl-format-cache/lib/memoizer.js
  82. 1
      www/fix/intl-format-cache/lib/memoizer.js.map
  83. 76
      www/fix/intl-format-cache/src/es5.js
  84. 81
      www/fix/intl-format-cache/src/memoizer.js
  85. 18792
      www/package-lock.json
  86. 17
      www/package.json
  87. BIN
      www/public/bg_legacy.png
  88. BIN
      www/public/escheresque.png
  89. BIN
      www/public/escheresque_ste.png
  90. 1
      www/public/etc.svg
  91. BIN
      www/public/favicon.ico
  92. BIN
      www/public/favicon.png
  93. BIN
      www/public/sayagata-400px.png
  94. 12
      www/tests/unit/adapters/application-test.js
  95. 12
      www/tests/unit/helpers/format-difficulty-test.js
  96. 12
      www/tests/unit/helpers/worker-colorizer-test.js
  97. 12
      www/tests/unit/helpers/worker-earnperday-test.js
  98. 15
      www/tests/unit/serializers/chart-test.js
  99. 20
      www/tests/units/charts/difficulty-test.js
  100. 159
      www/translations/ar-sa.yaml
  101. Some files were not shown because too many files have changed in this diff Show More

2
.travis.yml

@ -1,7 +1,7 @@
language: go
go:
- "1.10"
- 1.9
- tip
services:

9
Makefile

@ -4,13 +4,16 @@
.PHONY: all test clean
GOBIN = build/bin
GOBIN = ./build/bin
GOGET = env GO111MODULE=on go get
GOTEST = env GO111MODULE=on go test
all:
build/env.sh go get -v ./...
build/env.sh $(GOGET) -v ./...
test: all
build/env.sh go test -v ./...
build/env.sh $(GOTEST) -v ./...
clean:
env GO111MODULE=on go clean -cache
rm -fr build/_workspace/pkg/ $(GOBIN)/*

303
README.md

@ -1,97 +1,122 @@
## Open Source Perkle (ETC 2021) Mining Pool
## Open Source Ethereum Classic Mining Pool
Donations
ETC: 0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6
### WARNING: This code is currently configured for the Ethereum Classic main network
### Features
Telegram https://t.me/poolnode
**This pool is being further developed to provide an easy to use pool for Perkle miners. Testing and bug submissions are welcome!**
**This pool is being further developed to provide an easy to use pool for Ethereum Classic miners. This software is functional however an optimised release of the pool is expected soon. Testing and bug submissions are welcome!**
* Updated to work with Perkle 0.2.1
* Support for HTTP and Stratum mining
* Detailed block stats with luck percentage and full reward
* Failover geth instances: geth high availability built in
* Modern beautiful Ember.js frontend
* Separate stats for workers: can highlight timed-out workers so miners can perform maintenance of rigs
* JSON-API for stats
* PPLNS block reward
* Multi-tx payout at once
* Beautiful front-end highcharts embedded
#### Proxies
* [Ether-Proxy](https://github.com/sammy007/ether-proxy) HTTP proxy with web interface
* [Stratum Proxy](https://github.com/Atrides/eth-proxy) for Ethereum
## Guide to make your very own Perkle mining pool
### Building on Linux
Dependencies:
* go >= 1.10
* go >= 1.13
* core-geth
* redis-server >= 2.8.0
* nodejs >= 4 LTS
* nginx
* geth (core-geth)
**I highly recommend to use Ubuntu 16.04 LTS.**
**I highly recommend to use Ubuntu 20.04 LTS.**
First install [core-geth](https://github.com/etclabscore/core-geth/releases).
Clone & compile:
git config --global http.https://gopkg.in.followRedirects true
git clone https://github.com/yuriy0803/open-etc-pool-friends.git
cd open-etc-pool
make
Install redis-server.
### Running Pool
./build/bin/open-etc-pool-friends config.json
You can use Ubuntu upstart - check for sample config in <code>upstart.conf</code>.
### Install go lang
### Building Frontend
$ sudo apt-get install -y build-essential golang-1.10-go unzip
$ sudo ln -s /usr/lib/go-1.10/bin/go /usr/local/bin/go
Install nodejs. I suggest using LTS version >= 4.x from https://github.com/nodesource/distributions or from your Linux distribution or simply install nodejs on Ubuntu Xenial 16.04.
### Install redis-server
> NOTE: at this point keep your nodejs version <= 10.x.
$ sudo apt-get install redis-server
The frontend is a single-page Ember.js application that polls the pool API to render miner stats.
cd www
It is recommended to bind your DB address on 127.0.0.1 or on internal ip. Also, please set up the password for advanced security!!!
Change <code>ApiUrl: '//example.net/'</code> in <code>www/config/environment.js</code> to match your domain name. Also don't forget to adjust other options.
### Install nginx
Install deps
$ sudo apt-get install nginx
sudo npm install -g ember-cli@2.13
npm install -g bower
sudo chown -R $USER:$GROUP ~/.npm
sudo chown -R $USER:$GROUP ~/.config
npm install
bower install
sample config located at configs/nginx.default.example (HINT, edit and move to /etc/nginx/sites-available/default)
Build.
### Install NODE
./build.sh
This will install the latest nodejs
Configure nginx to serve API on <code>/api</code> subdirectory.
Configure nginx to serve <code>www/dist</code> as static website.
$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
$ sudo apt-get install -y nodejs
#### Serving API using nginx
### Install Perkle Node
See https://github.com/esprezzo/perkle
Create an upstream for API:
### Install Perkle Pool
upstream api {
server 127.0.0.1:8080;
}
$ git clone https://github.com/yuriy0803/open-etc-pool-friends
$ cd open-etc-pool-friends
$ make all
and add this setting after <code>location /</code>:
If you see open-perkle-pool after ls ~/open-etc-pool-friends/build/bin/, the installation has completed.
location /api {
proxy_pass http://api;
}
$ ls ~/open-etc-pool-friends/build/bin/
#### Customization
### Set up Perkle pool
You can customize the layout using built-in web server with live reload:
$ mv config.example.json config.json
$ nano config.json
ember server --port 8082 --environment development
Set up based on commands below.
**Don't use built-in web server in production**.
Check out <code>www/app/templates</code> directory and edit these templates
in order to customise the frontend.
### Configuration
Configuration is actually simple, just read it twice and think twice before changing defaults.
**Don't copy config directly from this manual. Use the example config from the package,
otherwise you will get errors on start because of JSON comments.**
```javascript
{
// The number of cores of CPU.
// Set to the number of CPU cores of your server
"threads": 2,
// Prefix for keys in redis store
"coin": "etc",
// Give unique name to each instance
"name": "main",
// PPLNS rounds
"pplns": 9000,
// mordor OR classic
"network": "classic",
"proxy": {
"enabled": true,
@ -120,7 +145,7 @@ Set up based on commands below.
// Try to get new job from geth in this interval
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
// If there are many rejects because of heavy hash, difficulty should be increased properly.
// Require this share difficulty from miners
"difficulty": 2000000000,
/* Reply error to miner instead of job if redis is unavailable.
@ -182,11 +207,6 @@ Set up based on commands below.
"payments": 50,
// Max numbers of blocks to display in frontend
"blocks": 50,
// Frontend Chart related settings
"poolCharts":"0 */20 * * * *",
"poolChartsNum":74,
"minerCharts":"0 */20 * * * *",
"minerChartsNum":74
/* If you are running API node on a different server where this module
is reading data from redis writeable slave, you must run an api instance with this option enabled in order to purge hashrate stats from main redis node.
@ -206,12 +226,12 @@ Set up based on commands below.
"upstream": [
{
"name": "main",
"url": "http://127.0.0.1:8501",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.2:8501",
"url": "http://127.0.0.2:8545",
"timeout": "10s"
}
],
@ -219,10 +239,9 @@ Set up based on commands below.
// This is standard redis connection options
"redis": {
// Where your redis instance is listening for commands
// NOTE THAT THE POOL IS CONFIGURED FOR Redis database "1"
"endpoint": "127.0.0.1:6379",
"poolSize": 10,
"database": 1,
"database": 0,
"password": ""
},
@ -231,9 +250,9 @@ Set up based on commands below.
"enabled": false,
// Pool fee percentage
"poolFee": 1.0,
// the address is for pool fee. Personal wallet is recommended to prevent from server hacking.
// Pool fees beneficiary address (leave it blank to disable fee withdrawals)
"poolFeeAddress": "",
// Amount of donation to a pool maker. 10 percent of pool fee is donated to a pool maker now. If pool fee is 1 percent, 0.1 percent which is 10 percent of pool fee should be donated to a pool maker.
// Donate 10% from pool fees to developers
"donate": true,
// Unlock only if this number of blocks mined back
"depth": 120,
@ -244,34 +263,33 @@ Set up based on commands below.
// Run unlocker in this interval
"interval": "10m",
// Geth instance node rpc endpoint for unlocking blocks
"daemon": "http://127.0.0.1:8501",
"daemon": "http://127.0.0.1:8545",
// Rise error if can't reach geth in this amount of time
"timeout": "10s"
},
// Pay out miners using this module
"payouts": {
"enabled": true,
"enabled": false,
// Require minimum number of peers on node
"requirePeers": 5,
"requirePeers": 25,
// Run payouts in this interval
"interval": "12h",
// Geth instance node rpc endpoint for payouts processing
"daemon": "http://127.0.0.1:8501",
"daemon": "http://127.0.0.1:8545",
// Rise error if can't reach geth in this amount of time
"timeout": "10s",
// Address with pool coinbase wallet address.
// Address with pool balance
"address": "0x0",
// Let geth to determine gas and gasPrice
"autoGas": true,
// Gas amount and price for payout tx (advanced users only)
"gas": "21000",
"gasPrice": "50000000000",
// The minimum distribution of mining reward. It is 1 CLO now.
"threshold": 1000000000,
// Send payment only if miner's balance is >= 0.5 Ether
"threshold": 500000000,
// Perform BGSAVE on Redis after successful payouts session
"bgsave": false
"concurrentTx": 10
}
}
```
@ -285,142 +303,6 @@ I recommend this deployment strategy:
* Unlocker and payouts instance - 1x each (strict!)
* API instance - 1x
### Run Pool
It is required to run pool by serviced. If it is not, the terminal could be stopped, and pool doesn’t work.
$ sudo nano /etc/systemd/system/etherpool.service
Copy the following example
```
[Unit]
Description=Etherpool
After=perkle.target
[Service]
Type=simple
ExecStart=/home/<your-user-name>/open-etc-pool-friends/build/bin/open-etc-pool-friends /home/<your-user-name>/open-etc-pool-friends/config.json
[Install]
WantedBy=multi-user.target
```
Then run pool by the following commands
$ sudo systemctl enable etherpool
$ sudo systemctl start etherpool
If you want to debug the node command
$ sudo systemctl status etherpool
Backend operation has completed so far.
### Open Firewall
Firewall should be opened to operate this service. Whether Ubuntu firewall is basically opened or not, the firewall should be opened based on your situation.
You can open firewall by opening 80,443,8080,8888,8008.
## Install Frontend
### Modify configuration file
$ nano ~/open-etc-pool-friends/www/config/environment.js
Make some modifications in these settings.
BrowserTitle: 'Perkle Mining Pool',
ApiUrl: '//your-pool-domain/',
HttpHost: 'http://your-pool-domain',
StratumHost: 'your-pool-domain',
PoolFee: '1%',
The frontend is a single-page Ember.js application that polls the pool API to render miner stats.
$ cd ~/open-etc-pool-friends/www
$ sudo npm install -g ember-cli@2.9.1
$ sudo npm install -g bower
$ sudo chown -R $USER:$GROUP ~/.npm
$ sudo chown -R $USER:$GROUP ~/.config
$ npm install
$ bower install
$ ./build.sh
$ cp -R ~/open-etc-pool-friends/www/dist ~/www
As you can see above, the frontend of the pool homepage is created. Then, move to the directory, www, which services the file.
Set up nginx.
$ sudo nano /etc/nginx/sites-available/default
Modify based on configuration file.
# Default server configuration
# nginx example
upstream api {
server 127.0.0.1:8080;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
root /home/<your-user-name>/www;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
location /api {
proxy_pass http://api;
}
}
After setting nginx is completed, run the command below.
$ sudo service nginx restart
Type your homepage address or IP address on the web.
If you face screen without any issues, pool installation has completed.
### Extra) How To Secure the pool frontend with Let's Encrypt (https)
This guide was originally referred from [digitalocean - How To Secure Nginx with Let's Encrypt on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04)
First, install the Certbot's Nginx package with apt-get
```
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx
```
And then open your nginx setting file, make sure the server name is configured!
```
$ sudo nano /etc/nginx/sites-available/default
. . .
server_name <your-pool-domain>;
. . .
```
Change the _ to your pool domain, and now you can obtain your auto-renewaled ssl certificate for free!
```
$ sudo certbot --nginx -d <your-pool-domain>
```
Now you can access your pool's frontend via https! Share your pool link!
### Notes
* Unlocking and payouts are sequential, 1st tx go, 2nd waiting for 1st to confirm and so on. You can disable that in code. Carefully read `docs/PAYOUTS.md`.
@ -428,21 +310,12 @@ Now you can access your pool's frontend via https! Share your pool link!
* You must restart module if you see errors with the word *suspended*.
* Don't run payouts and unlocker modules as part of mining node. Create separate configs for both, launch independently and make sure you have a single instance of each module running.
* If `poolFeeAddress` is not specified all pool profit will remain on coinbase address. If it specified, make sure to periodically send some dust back required for payments.
* DO NOT OPEN YOUR RPC OR REDIS ON 0.0.0.0!!! It will eventually cause coin theft.
### Credits
Made by sammy007. Licensed under GPLv3.
Modified by Akira Takizawa & The Ellaism Project & The Esprezzo Team.
#### Contributors
[Alex Leverington](https://github.com/subtly)
### Donations
### Mordor
ETH/ETC/ETSC/CLO: 0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6
To use this pool on the mordor testnet two settings require changing to "mordor"
![](https://cdn.pbrd.co/images/GP5tI1D.png)
network in your config.json (this sets backend (validation,unlocker) to mordor paramaters)
APP.Network in your www/config/environment.js (this sets the frontend to mordor paramaters)
rerun ./build.sh
Highly appreciated.

120
api.json

@ -0,0 +1,120 @@
{
"threads": 2,
"coin": "etc",
"name": "main",
"pplns": 9000,
"network": "classic",
"proxy": {
"enabled": true,
"listen": "0.0.0.0:8888",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 2000000000,
"hashrateExpiration": "3h",
"healthCheck": true,
"maxFails": 100,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8008",
"timeout": "120s",
"maxConn": 8192
},
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": true,
"purgeOnly": false,
"purgeInterval": "10m",
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 30,
"blocks": 50,
"poolCharts":"*/20 * * * *",
"poolChartsNum":74,
"minerCharts":"*/20 * * * *",
"minerChartsNum":74
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "main",
"url": "http://192.168.178.26:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.2:8545",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 10,
"database": 0,
"password": ""
},
"unlocker": {
"enabled": true,
"poolFee": 1.0,
"poolFeeAddress": "",
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "1m",
"daemon": "http://192.168.178.26:8545",
"timeout": "10s"
},
"payouts": {
"enabled": false,
"requirePeers": 25,
"interval": "20m",
"daemon": "http://192.168.178.26:8545",
"timeout": "10s",
"address": "0xf8d10632dedf8cb9033b1438187a618d08734cc8",
"gas": "21000",
"gasPrice": "50000000000",
"autoGas": true,
"threshold": 500000000,
"bgsave": false,
"concurrentTx": 10
},
"newrelicEnabled": false,
"newrelicName": "MyEtherProxy",
"newrelicKey": "SECRET_KEY",
"newrelicVerbose": false
}

17
api/server.go

@ -2,7 +2,7 @@ package api
import (
"encoding/json"
"fmt"
"fmt"
"log"
"net/http"
"sort"
@ -12,10 +12,10 @@ import (
"time"
"github.com/gorilla/mux"
"github.com/robfig/cron"
"github.com/robfig/cron"
"github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/storage"
"github.com/etclabscore/open-etc-pool/util"
)
type ApiConfig struct {
@ -230,7 +230,7 @@ func (s *ApiServer) StatsIndex(w http.ResponseWriter, r *http.Request) {
if stats != nil {
reply["now"] = util.MakeTimestamp()
reply["stats"] = stats["stats"]
reply["poolCharts"] = stats["poolCharts"]
reply["poolCharts"] = stats["poolCharts"]
reply["hashrate"] = stats["hashrate"]
reply["minersTotal"] = stats["minersTotal"]
reply["maturedTotal"] = stats["maturedTotal"]
@ -245,9 +245,6 @@ func (s *ApiServer) StatsIndex(w http.ResponseWriter, r *http.Request) {
}
func (s *ApiServer) MinersIndex(w http.ResponseWriter, r *http.Request) {
// TODO: Want to get the most used server from workers, so it can be deisplayed in miners page
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "no-cache")
@ -352,8 +349,8 @@ func (s *ApiServer) AccountIndex(w http.ResponseWriter, r *http.Request) {
stats[key] = value
}
stats["pageSize"] = s.config.Payments
stats["minerCharts"], err = s.backend.GetMinerCharts(s.config.MinerChartsNum, login)
stats["paymentCharts"], err = s.backend.GetPaymentCharts(login)
stats["minerCharts"], err = s.backend.GetMinerCharts(s.config.MinerChartsNum, login)
stats["paymentCharts"], err = s.backend.GetPaymentCharts(login)
reply = &Entry{stats: stats, updatedAt: now}
s.miners[login] = reply
}

113
configs/api.json

@ -1,113 +0,0 @@
{
"threads": 2,
"coin": "etc",
"name": "main",
"pplns": 9000,
"proxy": {
"enabled": false,
"listen": "0.0.0.0:8888",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8008",
"timeout": "120s",
"maxConn": 8192
},
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 2000000000,
"healthCheck": true,
"maxFails": 100,
"hashrateExpiration": "3h",
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": true,
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"purgeInterval": "10m",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 50,
"blocks": 50,
"poolCharts":"*/20 * * * *",
"poolChartsNum":74,
"minerCharts":"*/20 * * * *",
"minerChartsNum":74,
"purgeOnly": false
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "main",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.2:8501",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 10,
"database": 1,
"password": ""
},
"unlocker": {
"enabled": false,
"poolFee": 0.5,
"poolFeeAddress": "",
"donate": true,
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "10m",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"classic": true
},
"payouts": {
"enabled": false,
"requirePeers": 5,
"interval": "3h",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"address": "0x",
"autoGas": true,
"gas": "21000",
"gasPrice": "50000000000",
"threshold": 1000000000,
"bgsave": true,
"concurrentTx": 5
}
}

18
configs/nginx.default.example

@ -1,18 +0,0 @@
server {
listen 80;
listen [::]:80;
root /var/www/etcpool;
index index.html index.htm index.nginx-debian.html;
server_name etc.yourdomain.name;
location / {
try_files $uri $uri/ =404;
}
location /api {
proxy_pass http://127.0.0.1:8080/api;
}
}

113
configs/payout.json

@ -1,113 +0,0 @@
{
"threads": 2,
"coin": "etc",
"name": "main",
"pplns": 9000,
"proxy": {
"enabled": false,
"listen": "0.0.0.0:8888",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8008",
"timeout": "120s",
"maxConn": 8192
},
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 2000000000,
"healthCheck": true,
"maxFails": 100,
"hashrateExpiration": "3h",
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": false,
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"purgeInterval": "10m",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 50,
"blocks": 50,
"poolCharts":"0 */20 * * * *",
"poolChartsNum":74,
"minerCharts":"0 */20 * * * *",
"minerChartsNum":74,
"purgeOnly": false
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "main",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.1:8501",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 10,
"database": 1,
"password": ""
},
"unlocker": {
"enabled": false,
"poolFee": 0.5,
"poolFeeAddress": "0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6",
"donate": true,
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "10m",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"classic": true
},
"payouts": {
"enabled": true,
"requirePeers": 4,
"interval": "1h",
"daemon": "http://127.0.0.1:8545",
"timeout": "120s",
"address": "0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6",
"autoGas": true,
"gas": "21000",
"gasPrice": "50000000000",
"threshold": 1000000000,
"bgsave": true,
"concurrentTx": 5
}
}

109
configs/stratum2b.json

@ -1,109 +0,0 @@
{
"threads": 2,
"coin": "etc",
"name": "main",
"pplns": 9000,
"proxy": {
"enabled": true,
"listen": "0.0.0.0:8882",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8002",
"timeout": "120s",
"maxConn": 8192
},
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 2000000000,
"stratumHostname": "Domain name",
"healthCheck": true,
"maxFails": 100,
"hashrateExpiration": "3h",
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": false,
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"purgeInterval": "10m",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 50,
"blocks": 50,
"poolCharts":"0 */20 * * * *",
"poolChartsNum":74,
"minerCharts":"0 */20 * * * *",
"minerChartsNum":74,
"purgeOnly": false
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "backup",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 10,
"database": 1,
"password": ""
},
"unlocker": {
"enabled": false,
"poolFee": 0.5,
"poolFeeAddress": "",
"donate": true,
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "10m",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"classic": true
},
"payouts": {
"enabled": false,
"requirePeers": 5,
"interval": "3h",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"address": "0x",
"autoGas": true,
"gas": "21000",
"gasPrice": "50000000000",
"threshold": 1000000000,
"bgsave": true,
"concurrentTx": 5
}
}

114
configs/stratum4b.json

@ -1,114 +0,0 @@
{
"threads": 4,
"coin": "etc",
"name": "main",
"pplns": 9000,
"proxy": {
"enabled": true,
"listen": "0.0.0.0:8884",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8004",
"timeout": "120s",
"maxConn": 8192
},
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 4000000000,
"stratumHostname": "Domain name",
"healthCheck": true,
"maxFails": 100,
"hashrateExpiration": "3h",
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": false,
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"purgeInterval": "10m",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 50,
"blocks": 50,
"poolCharts":"0 */20 * * * *",
"poolChartsNum":74,
"minerCharts":"0 */20 * * * *",
"minerChartsNum":74,
"purgeOnly": false
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "main",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.2:8501",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 10,
"database": 1,
"password": ""
},
"unlocker": {
"enabled": false,
"poolFee": 0.5,
"poolFeeAddress": "",
"donate": true,
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "10m",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"classic": true
},
"payouts": {
"enabled": false,
"requirePeers": 5,
"interval": "3h",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"address": "0x",
"autoGas": true,
"gas": "21000",
"gasPrice": "50000000000",
"threshold": 1000000000,
"bgsave": true,
"concurrentTx": 5
}
}

114
configs/stratum8b.json

@ -1,114 +0,0 @@
{
"threads": 8,
"coin": "etc",
"name": "main",
"pplns": 9000,
"proxy": {
"enabled": true,
"listen": "0.0.0.0:8888",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8008",
"timeout": "120s",
"maxConn": 8192
},
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 8000000000,
"stratumHostname": "Domain name",
"healthCheck": true,
"maxFails": 100,
"hashrateExpiration": "3h",
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": false,
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"purgeInterval": "10m",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 50,
"blocks": 50,
"poolCharts":"0 */20 * * * *",
"poolChartsNum":74,
"minerCharts":"0 */20 * * * *",
"minerChartsNum":74,
"purgeOnly": false
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "main",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.2:8545",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 15,
"database": 1,
"password": ""
},
"unlocker": {
"enabled": false,
"poolFee": 0.5,
"poolFeeAddress": "0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6",
"donate": true,
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "10m",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"classic": true
},
"payouts": {
"enabled": false,
"requirePeers": 5,
"interval": "3h",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"address": "0x",
"autoGas": true,
"gas": "21000",
"gasPrice": "50000000000",
"threshold": 1000000000,
"bgsave": true,
"concurrentTx": 5
}
}

114
configs/stratum9b.json

@ -1,114 +0,0 @@
{
"threads": 8,
"coin": "etc",
"name": "main",
"pplns": 9000,
"proxy": {
"enabled": true,
"listen": "0.0.0.0:8889",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8009",
"timeout": "120s",
"maxConn": 8192
},
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 9000000000,
"stratumHostname": "Domain name",
"healthCheck": true,
"maxFails": 100,
"hashrateExpiration": "3h",
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": false,
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"purgeInterval": "10m",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 50,
"blocks": 50,
"poolCharts":"0 */20 * * * *",
"poolChartsNum":74,
"minerCharts":"0 */20 * * * *",
"minerChartsNum":74,
"purgeOnly": false
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "main",
"url": "http://127.0.01:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.2:8545",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 15,
"database": 1,
"password": ""
},
"unlocker": {
"enabled": false,
"poolFee": 0.5,
"poolFeeAddress": "0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6",
"donate": true,
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "10m",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"classic": true
},
"payouts": {
"enabled": false,
"requirePeers": 5,
"interval": "3h",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"address": "0x",
"autoGas": true,
"gas": "21000",
"gasPrice": "50000000000",
"threshold": 1000000000,
"bgsave": true,
"concurrentTx": 5
}
}

113
configs/unlocker.json

@ -1,113 +0,0 @@
{
"threads": 2,
"coin": "etc",
"name": "main",
"pplns": 9000,
"proxy": {
"enabled": false,
"listen": "0.0.0.0:8888",
"limitHeadersSize": 1024,
"limitBodySize": 256,
"behindReverseProxy": false,
"stratum": {
"enabled": true,
"listen": "0.0.0.0:8008",
"timeout": "120s",
"maxConn": 8192
},
"blockRefreshInterval": "120ms",
"stateUpdateInterval": "3s",
"difficulty": 2000000000,
"healthCheck": true,
"maxFails": 100,
"hashrateExpiration": "3h",
"policy": {
"workers": 8,
"resetInterval": "60m",
"refreshInterval": "1m",
"banning": {
"enabled": false,
"ipset": "blacklist",
"timeout": 1800,
"invalidPercent": 30,
"checkThreshold": 30,
"malformedLimit": 5
},
"limits": {
"enabled": false,
"limit": 30,
"grace": "5m",
"limitJump": 10
}
}
},
"api": {
"enabled": false,
"listen": "0.0.0.0:8080",
"statsCollectInterval": "5s",
"purgeInterval": "10m",
"hashrateWindow": "30m",
"hashrateLargeWindow": "3h",
"luckWindow": [64, 128, 256],
"payments": 50,
"blocks": 50,
"poolCharts":"0 */20 * * * *",
"poolChartsNum":74,
"minerCharts":"0 */20 * * * *",
"minerChartsNum":74,
"purgeOnly": false
},
"upstreamCheckInterval": "5s",
"upstream": [
{
"name": "main",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
},
{
"name": "backup",
"url": "http://127.0.0.1:8545",
"timeout": "10s"
}
],
"redis": {
"endpoint": "127.0.0.1:6379",
"poolSize": 10,
"database": 1,
"password": ""
},
"unlocker": {
"enabled": true,
"poolFee": 0.5,
"poolFeeAddress": "0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6",
"donate": true,
"depth": 120,
"immatureDepth": 20,
"keepTxFees": false,
"interval": "10m",
"daemon": "http://127.0.0.1:8545",
"timeout": "120s",
"classic": true
},
"payouts": {
"enabled": false,
"requirePeers": 5,
"interval": "3h",
"daemon": "http://127.0.0.1:8545",
"timeout": "10s",
"address": "0x",
"autoGas": true,
"gas": "21000",
"gasPrice": "50000000000",
"threshold": 1000000000,
"bgsave": false,
"concurrentTx": 5
}
}

16
docs/PAYOUTS.md

@ -38,7 +38,7 @@ After payout session, payment module will perform `BGSAVE` (background saving) o
If your payout is not logged and not confirmed by Ethereum network you can resolve it automatically. You need to payouts in maintenance mode by setting up `RESOLVE_PAYOUT=1` or `RESOLVE_PAYOUT=True` environment variable:
`RESOLVE_PAYOUT=1 ./build/bin/open-ethereum-pool payouts.json`.
`RESOLVE_PAYOUT=1 ./build/bin/open-etc-pool payouts.json`.
Payout module will fetch all rows from Redis with key `eth:payments:pending` and credit balance back to miners. Usually you will have only single entry there.
@ -48,13 +48,13 @@ If there was a debit operation performed which is not followed by actual money t
```
Will credit back following balances:
Address: 0x34AE12692BD4567A27e3E86411b58Ea6954BA773, Amount: 166798415 Shannon, 2016-05-11 08:14:34
Address: 0xb85150eb365e7df0941f0cf08235f987ba91506a, Amount: 166798415 Shannon, 2016-05-11 08:14:34
```
followed by
```
Credited 166798415 Shannon back to 0x34AE12692BD4567A27e3E86411b58Ea6954BA773
Credited 166798415 Shannon back to 0xb85150eb365e7df0941f0cf08235f987ba91506a
```
Usually every maintenance run ends with following message and halt:
@ -80,7 +80,7 @@ ZREVRANGE "eth:payments:pending" 0 -1 WITHSCORES
Result will be like this:
> 1) "0x34AE12692BD4567A27e3E86411b58Ea6954BA773:25000000"
> 1) "0xb85150eb365e7df0941f0cf08235f987ba91506a:25000000"
It's a pair of `LOGIN:AMOUNT`.
@ -95,7 +95,7 @@ It's a `UNIXTIME`
```javascript
eth.sendTransaction({
from: eth.coinbase,
to: '0x34AE12692BD4567A27e3E86411b58Ea6954BA773',
to: '0xb85150eb365e7df0941f0cf08235f987ba91506a',
value: web3.toWei(25000000, 'shannon')
})
@ -109,17 +109,17 @@ eth.sendTransaction({
Also usable for fixing missing payment entries.
```
ZADD "eth:payments:all" 1462920526 0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331:0x34AE12692BD4567A27e3E86411b58Ea6954BA773:25000000
ZADD "eth:payments:all" 1462920526 0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331:0xb85150eb365e7df0941f0cf08235f987ba91506a:25000000
```
```
ZADD "eth:payments:0x34AE12692BD4567A27e3E86411b58Ea6954BA773" 1462920526 0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331:25000000
ZADD "eth:payments:0xb85150eb365e7df0941f0cf08235f987ba91506a" 1462920526 0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331:25000000
```
### Delete Erroneous Payment Entry
```
ZREM "eth:payments:pending" "0x34AE12692BD4567A27e3E86411b58Ea6954BA773:25000000"
ZREM "eth:payments:pending" "0xb85150eb365e7df0941f0cf08235f987ba91506a:25000000"
```
### Update Internal Stats

4
docs/STRATUM.md

@ -19,7 +19,7 @@ Request looks like:
"id": 1,
"jsonrpc": "2.0",
"method": "eth_submitLogin",
"params": ["0x34AE12692BD4567A27e3E86411b58Ea6954BA773"]
"params": ["0xb85150eb365e7df0941f0cf08235f987ba91506a"]
}
```
@ -30,7 +30,7 @@ Request can include additional 2nd param (email for example):
"id": 1,
"jsonrpc": "2.0",
"method": "eth_submitLogin",
"params": ["0x34AE12692BD4567A27e3E86411b58Ea6954BA773", "admin@example.net"]
"params": ["0xb85150eb365e7df0941f0cf08235f987ba91506a", "admin@example.net"]
}
```

21
go.mod

@ -0,0 +1,21 @@
module github.com/etclabscore/open-etc-pool
go 1.15
require (
github.com/btcsuite/btcd v0.21.0-beta // indirect
github.com/edsrzf/mmap-go v1.0.0
github.com/etclabscore/go-etchash v0.0.0-20201116172336-ee6bd2e29b29
github.com/ethereum/go-ethereum v1.9.24
github.com/garyburd/redigo v1.6.2 // indirect
github.com/gorilla/mux v1.8.0
github.com/hashicorp/golang-lru v0.5.4
github.com/robfig/cron v1.2.0
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 // indirect
github.com/yvasiyarov/gorelic v0.0.7
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9 // indirect
golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c // indirect
gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a // indirect
gopkg.in/redis.v3 v3.6.4
)

563
go.sum

@ -0,0 +1,563 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20200628203458-851255f7a67b/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 h1:rtI0fD4oG/8eVokGVPYJEW1F88p1ZNgXiEIs9thEE4A=
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c h1:JHHhtb9XWJrGNMcrVP6vyzO4dusgi/HnceHTgxSejUM=
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/etclabscore/go-etchash v0.0.0-20201116172336-ee6bd2e29b29 h1:X/88sZQ3X0BrbYqDGR1U2K0mcz3+A3y8J6IoZMVX5Ds=
github.com/etclabscore/go-etchash v0.0.0-20201116172336-ee6bd2e29b29/go.mod h1:SxEttCWPN7KrSgmuR4mSOBBQjIR39RytnllrhQs2ubw=
github.com/ethereum/go-ethereum v1.9.23 h1:SIKhg/z4Q7AbvqcxuPYvMxf36che/Rq/Pp0IdYEkbtw=
github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
github.com/ethereum/go-ethereum v1.9.24 h1:6AK+ORt3EMDO+FTjzXy/AQwHMbu52J2nYHIjyQX9azQ=
github.com/ethereum/go-ethereum v1.9.24/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM=
github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-latex/latex v0.0.0-20200518072620-0806b477ea35/go.mod h1:PNI+CcWytn/2Z/9f1SGOOYn0eILruVyp0v2/iAs8asQ=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw=
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/karalabe/xgo v0.0.0-20191115072854-c5ccff8648a7/go.mod h1:iYGcTYIPUvEWhFo6aKUuLchs+AV4ssYdyuBbQJZGcBk=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c h1:1RHs3tNxjXGHeul8z2t6H2N2TlAqpKe5yryJztRx4Jk=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I=
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk=
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/tidwall/gjson v1.3.5 h1:2oW9FBNu8qt9jy5URgrzsVx/T/KSn3qn/smJQ0crlDQ=
github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U=
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.7 h1:4DTF1WOM2ZZS/xMOkTFBOcb6XiHu/PKn3rVo6dbewQE=
github.com/yvasiyarov/gorelic v0.0.7/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9 h1:AsFN8kXcCVkUFHyuzp1FtYbzp1nCO/H6+1uPSGEyPzM=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582 h1:0WDrJ1E7UolDk1KhTXxxw3Fc8qtk5x7dHP431KHEJls=
golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582/go.mod h1:tCqSYrHVcf3i63Co2FzBkTCo2gdF6Zak62921dSfraU=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8 h1:AvbQYmiaaaza3cW3QXRyPo5kYgpFIzOAfeAAN7m3qQ4=
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c h1:+B+zPA6081G5cEb2triOIJpcvSW4AYzmIyWAqMn2JAc=
golang.org/x/sys v0.0.0-20201109165425-215b40eba54c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.1/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gonum.org/v1/plot v0.8.0/go.mod h1:3GH8dTfoceRTELDnv+4HNwbvM/eMfdDUGHFG2bo3NeE=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a h1:stTHdEoWg1pQ8riaP5ROrjS6zy6wewH/Q2iwnLCQUXY=
gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/redis.v3 v3.6.4 h1:u7XgPH1rWwsdZnR+azldXC6x9qDU2luydOIeU/l52fE=
gopkg.in/redis.v3 v3.6.4/go.mod h1:6XeGv/CrsUFDU9aVbUdNykN7k1zVmoeg83KC9RbQfiU=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

10
main.go

@ -13,10 +13,10 @@ import (
"github.com/yvasiyarov/gorelic"
"github.com/yuriy0803/open-etc-pool-friends/api"
"github.com/yuriy0803/open-etc-pool-friends/payouts"
"github.com/yuriy0803/open-etc-pool-friends/proxy"
"github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/etclabscore/open-etc-pool/api"
"github.com/etclabscore/open-etc-pool/payouts"
"github.com/etclabscore/open-etc-pool/proxy"
"github.com/etclabscore/open-etc-pool/storage"
)
var cfg proxy.Config
@ -33,7 +33,7 @@ func startApi() {
}
func startBlockUnlocker() {
u := payouts.NewBlockUnlocker(&cfg.BlockUnlocker, backend)
u := payouts.NewBlockUnlocker(&cfg.BlockUnlocker, backend, &cfg.Network)
u.Start()
}

19
misc/nginx-default.conf

@ -0,0 +1,19 @@
upstream api {
server 127.0.0.1:8080;
}
server {
listen 0.0.0.0:80;
root /path/to/pool/www/dist;
index index.html index.htm;
server_name localhost;
location /api {
proxy_pass http://api;
}
location / {
try_files $uri $uri/ /index.html;
}
}

26
misc/upstart.conf

@ -0,0 +1,26 @@
# open-etc-pool
description "open-etc-pool"
env DAEMON=/home/main/src/open-etc-pool/build/bin/open-etc-pool
env CONFIG=/home/main/src/open-etc-pool/config.json
start on filesystem or runlevel [2345]
stop on runlevel [!2345]
setuid main
setgid main
kill signal INT
respawn
respawn limit 10 5
umask 022
pre-start script
test -x $DAEMON || { stop; exit 0; }
end script
# Start
script
exec $DAEMON $CONFIG
end script

14
payouts/payer.go

@ -5,16 +5,16 @@ import (
"log"
"math/big"
"os"
"os/exec"
"os/exec"
"strconv"
"time"
"sync"
"sync"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/rpc"
"github.com/etclabscore/open-etc-pool/storage"
"github.com/etclabscore/open-etc-pool/util"
)
const txCheckInterval = 5 * time.Second
@ -108,7 +108,7 @@ func (u *PayoutsProcessor) Start() {
func (u *PayoutsProcessor) process() {
if u.halt {
log.Println("Payments suspended due to last critical error:", u.lastFail)
os.Exit(1)
os.Exit(1)
return
}
mustPay := 0
@ -233,7 +233,7 @@ func (u *PayoutsProcessor) process() {
break
}
}
wg.Done()
wg.Done()
}(txHash, login, &wg)
if waitingCount > u.config.ConcurrentTx {

172
payouts/unlocker.go

@ -3,43 +3,42 @@ package payouts
import (
"fmt"
"log"
"os"
"math/big"
"os"
"strconv"
"strings"
"time"
"github.com/ethereum/go-ethereum/common/math"
"github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/rpc"
"github.com/etclabscore/open-etc-pool/storage"
"github.com/etclabscore/open-etc-pool/util"
)
type UnlockerConfig struct {
Enabled bool `json:"enabled"`
PoolFee float64 `json:"poolFee"`
PoolFeeAddress string `json:"poolFeeAddress"`
Donate bool `json:"donate"`
Depth int64 `json:"depth"`
ImmatureDepth int64 `json:"immatureDepth"`
KeepTxFees bool `json:"keepTxFees"`
Interval string `json:"interval"`
Daemon string `json:"daemon"`
Timeout string `json:"timeout"`
Classic bool `json:"classic"`
Enabled bool `json:"enabled"`
PoolFee float64 `json:"poolFee"`
PoolFeeAddress string `json:"poolFeeAddress"`
Donate bool `json:"donate"`
Depth int64 `json:"depth"`
ImmatureDepth int64 `json:"immatureDepth"`
KeepTxFees bool `json:"keepTxFees"`
Interval string `json:"interval"`
Daemon string `json:"daemon"`
Timeout string `json:"timeout"`
Ecip1017FBlock int64 `json:"ecip1017FBlock"`
Ecip1017EraRounds *big.Int `json:"ecip1017EraRounds"`
}
const minDepth = 16
const byzantiumHardForkHeight = 4370000
var homesteadReward = math.MustParseBig256("5000000000000000000")
var byzantiumReward = math.MustParseBig256("3200000000000000000")
var classicReward = math.MustParseBig256("3200000000000000000")
var disinflationRateQuotient = big.NewInt(4) // Disinflation rate quotient for ECIP1017
var disinflationRateDivisor = big.NewInt(5) // Disinflation rate divisor for ECIP1017
var big32 = big.NewInt(32)
var big8 = big.NewInt(8)
// Donate 10% from pool fees to developers
const donationFee = 10
const donationAccount = "0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6"
var homesteadReward = math.MustParseBig256("4000000000000000000")
type BlockUnlocker struct {
config *UnlockerConfig
@ -49,7 +48,17 @@ type BlockUnlocker struct {
lastFail error
}
func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient) *BlockUnlocker {
func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient, network *string) *BlockUnlocker {
if *network == "classic" {
cfg.Ecip1017FBlock = 5000000
cfg.Ecip1017EraRounds = big.NewInt(5000000)
} else if *network == "mordor" {
cfg.Ecip1017FBlock = 0
cfg.Ecip1017EraRounds = big.NewInt(2000000)
} else {
log.Fatalln("Invalid network set", network)
}
if len(cfg.PoolFeeAddress) != 0 && !util.IsValidHexAddress(cfg.PoolFeeAddress) {
log.Fatalln("Invalid poolFeeAddress", cfg.PoolFeeAddress)
}
@ -112,6 +121,11 @@ func (u *BlockUnlocker) unlockCandidates(candidates []*storage.BlockData) (*Unlo
/* Search for a normal block with wrong height here by traversing 16 blocks back and forward.
* Also we are searching for a block that can include this one as uncle.
*/
if candidate.Height < minDepth {
orphan = false
// avoid scanning the first 16 blocks
continue
}
for i := int64(minDepth * -1); i < minDepth; i++ {
height := candidate.Height + i
@ -120,6 +134,7 @@ func (u *BlockUnlocker) unlockCandidates(candidates []*storage.BlockData) (*Unlo
}
block, err := u.rpc.GetBlockByHeight(height)
if err != nil {
log.Printf("Error while retrieving block %v from node: %v", height, err)
return nil, err
@ -162,7 +177,7 @@ func (u *BlockUnlocker) unlockCandidates(candidates []*storage.BlockData) (*Unlo
orphan = false
result.uncles++
err := handleUncle(height, uncle, candidate, u.config.Classic)
err := handleUncle(height, uncle, candidate, u.config)
if err != nil {
u.halt = true
u.lastFail = err
@ -212,7 +227,13 @@ func (u *BlockUnlocker) handleBlock(block *rpc.GetBlockReply, candidate *storage
return err
}
candidate.Height = correctHeight
reward := getConstReward(candidate.Height, u.config.Classic)
era := GetBlockEra(big.NewInt(candidate.Height), u.config.Ecip1017EraRounds)
reward := getConstReward(era)
// Add reward for including uncles
uncleReward := getRewardForUncle(reward)
rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles))))
reward.Add(reward, rewardForUncles)
// Add TX fees
extraTxReward, err := u.getExtraRewardForTx(block)
@ -225,23 +246,19 @@ func (u *BlockUnlocker) handleBlock(block *rpc.GetBlockReply, candidate *storage
reward.Add(reward, extraTxReward)
}
// Add reward for including uncles
uncleReward := getRewardForUncle(candidate.Height, u.config.Classic)
rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles))))
reward.Add(reward, rewardForUncles)
candidate.Orphan = false
candidate.Hash = block.Hash
candidate.Reward = reward
return nil
}
func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.BlockData, isClassic bool) error {
func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.BlockData, cfg *UnlockerConfig) error {
uncleHeight, err := strconv.ParseInt(strings.Replace(uncle.Number, "0x", "", -1), 16, 64)
if err != nil {
return err
}
reward := getUncleReward(uncleHeight, height, isClassic)
era := GetBlockEra(big.NewInt(height), cfg.Ecip1017EraRounds)
reward := getUncleReward(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), era, getConstReward(era))
candidate.Height = height
candidate.UncleHeight = uncleHeight
candidate.Orphan = false
@ -253,7 +270,7 @@ func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.Bloc
func (u *BlockUnlocker) unlockPendingBlocks() {
if u.halt {
log.Println("Unlocking suspended due to last critical error:", u.lastFail)
os.Exit(1)
os.Exit(1)
return
}
@ -480,13 +497,6 @@ func (u *BlockUnlocker) calculateRewards(block *storage.BlockData) (*big.Rat, *b
revenue.Add(revenue, extraReward)
}
if u.config.Donate {
var donation = new(big.Rat)
poolProfit, donation = chargeFee(poolProfit, donationFee)
login := strings.ToLower(donationAccount)
rewards[login] += weiToShannonInt64(donation)
}
if len(u.config.PoolFeeAddress) != 0 {
address := strings.ToLower(u.config.PoolFeeAddress)
rewards[address] += weiToShannonInt64(poolProfit)
@ -495,9 +505,9 @@ func (u *BlockUnlocker) calculateRewards(block *storage.BlockData) (*big.Rat, *b
return revenue, minersProfit, poolProfit, rewards, percents, nil
}
func calculateRewardsForShares(shares map[string]int64, total int64, reward *big.Rat) (map[string]int64, map[string]*big.Rat) {
func calculateRewardsForShares(shares map[string]int64, total int64, reward *big.Rat)(map[string]int64, map[string]*big.Rat) {
rewards := make(map[string]int64)
percents := make(map[string]*big.Rat)
percents := make(map[string]*big.Rat)
for login, n := range shares {
percents[login] = big.NewRat(n, total)
@ -521,35 +531,67 @@ func weiToShannonInt64(wei *big.Rat) int64 {
return value
}
func getConstReward(height int64, isClassic bool) *big.Int {
if !isClassic {
if height >= byzantiumHardForkHeight {
return new(big.Int).Set(byzantiumReward)
}
return new(big.Int).Set(homesteadReward)
} else {
return new(big.Int).Set(classicReward)
// GetRewardByEra gets a block reward at disinflation rate.
// Constants MaxBlockReward, DisinflationRateQuotient, and DisinflationRateDivisor assumed.
func GetBlockWinnerRewardByEra(era *big.Int, blockReward *big.Int) *big.Int {
if era.Cmp(big.NewInt(0)) == 0 {
return new(big.Int).Set(blockReward)
}
// MaxBlockReward _r_ * (4/5)**era == MaxBlockReward * (4**era) / (5**era)
// since (q/d)**n == q**n / d**n
// qed
var q, d, r *big.Int = new(big.Int), new(big.Int), new(big.Int)
q.Exp(disinflationRateQuotient, era, nil)
d.Exp(disinflationRateDivisor, era, nil)
r.Mul(blockReward, q)
r.Div(r, d)
return r
}
func getRewardForUncle(height int64, isClassic bool) *big.Int {
reward := getConstReward(height, isClassic)
return new(big.Int).Div(reward, new(big.Int).SetInt64(32))
// GetBlockEra gets which "Era" a given block is within, given an era length (ecip-1017 has era=5,000,000 blocks)
// Returns a zero-index era number, so "Era 1": 0, "Era 2": 1, "Era 3": 2 ...
func GetBlockEra(blockNum, eraLength *big.Int) *big.Int {
// If genesis block or impossible negative-numbered block, return zero-val.
if blockNum.Sign() < 1 {
return new(big.Int)
}
remainder := big.NewInt(0).Mod(big.NewInt(0).Sub(blockNum, big.NewInt(1)), eraLength)
base := big.NewInt(0).Sub(blockNum, remainder)
d := big.NewInt(0).Div(base, eraLength)
dremainder := big.NewInt(0).Mod(d, big.NewInt(1))
return new(big.Int).Sub(d, dremainder)
}
func getUncleReward(uHeight, height int64, isClassic bool) *big.Int {
if !isClassic {
reward := getConstReward(height, isClassic)
k := height - uHeight
reward.Mul(big.NewInt(8-k), reward)
reward.Div(reward, big.NewInt(8))
return reward
} else {
reward := getConstReward(height, isClassic)
reward.Mul(reward, big.NewInt(3125))
reward.Div(reward, big.NewInt(100000))
return reward
func getConstReward(era *big.Int) *big.Int {
var blockReward = homesteadReward
wr := GetBlockWinnerRewardByEra(era, blockReward)
return wr
}
func getRewardForUncle(blockReward *big.Int) *big.Int {
return new(big.Int).Div(blockReward, big32) //return new(big.Int).Div(reward, new(big.Int).SetInt64(32))
}
func getUncleReward(uHeight *big.Int, height *big.Int, era *big.Int, reward *big.Int) *big.Int {
// Era 1 (index 0):
// An extra reward to the winning miner for including uncles as part of the block, in the form of an extra 1/32 (0.15625ETC) per uncle included, up to a maximum of two (2) uncles.
if era.Cmp(big.NewInt(0)) == 0 {
r := new(big.Int)
r.Add(uHeight, big8) // 2,534,998 + 8 = 2,535,006
r.Sub(r, height) // 2,535,006 - 2,534,999 = 7
r.Mul(r, reward) // 7 * 5e+18 = 35e+18
r.Div(r, big8) // 35e+18 / 8 = 7/8 * 5e+18
return r
}
return getRewardForUncle(reward)
}
func (u *BlockUnlocker) getExtraRewardForTx(block *rpc.GetBlockReply) (*big.Int, error) {

131
payouts/unlocker_test.go

@ -1,12 +1,11 @@
package payouts
import (
"github.com/etclabscore/open-etc-pool/rpc"
"github.com/etclabscore/open-etc-pool/storage"
"math/big"
"os"
"testing"
"github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/storage"
)
func TestMain(m *testing.M) {
@ -65,75 +64,77 @@ func TestWeiToShannonInt64(t *testing.T) {
t.Error("Must charge original value")
}
}
func TestGetClassicUncleReward(t *testing.T) {
rewards := make(map[int64]string)
expectedRewards := map[int64]string{
1: "125000000000000000",
}
for i := int64(1); i < 2; i++ {
rewards[i] = getUncleReward(1, i+1, true).String()
}
for i, reward := range rewards {
if expectedRewards[i] != rewards[i] {
t.Errorf("Incorrect uncle reward for %v, expected %v vs %v", i, expectedRewards[i], reward)
}
}
}
func TestGetUncleReward(t *testing.T) {
rewards := make(map[int64]string)
expectedRewards := map[int64]string{
1: "4375000000000000000",
2: "3750000000000000000",
3: "3125000000000000000",
4: "2500000000000000000",
5: "1875000000000000000",
6: "1250000000000000000",
7: "625000000000000000",
}
for i := int64(1); i < 8; i++ {
rewards[i] = getUncleReward(1, i+1, false).String()
}
for i, reward := range rewards {
if expectedRewards[i] != rewards[i] {
t.Errorf("Incorrect uncle reward for %v, expected %v vs %v", i, expectedRewards[i], reward)
}
}
}
func TestGetByzantiumUncleReward(t *testing.T) {
rewards := make(map[int64]string)
expectedRewards := map[int64]string{
1: "2625000000000000000",
2: "2250000000000000000",
3: "1875000000000000000",
4: "1500000000000000000",
5: "1125000000000000000",
6: "750000000000000000",
7: "375000000000000000",
}
for i := int64(1); i < 8; i++ {
rewards[i] = getUncleReward(byzantiumHardForkHeight, byzantiumHardForkHeight+i, false).String()
}
for i, reward := range rewards {
if expectedRewards[i] != rewards[i] {
t.Errorf("Incorrect uncle reward for %v, expected %v vs %v", i, expectedRewards[i], reward)
}
func TestGetBlockEra(t *testing.T) {
blockNum := big.NewInt(11700000)
eraLength := big.NewInt(5000000)
era := GetBlockEra(blockNum, eraLength)
if era.Cmp(big.NewInt(2)) != 0 {
t.Error("Should return Era 2", "era", era)
}
// handle negative blockNum
blockNum = big.NewInt(-50000)
era = GetBlockEra(blockNum, eraLength)
if era.Cmp(big.NewInt(0)) != 0 {
t.Error("Should return Era 0", "era", era)
}
// handle negative blockNum
blockNum = big.NewInt(5000001)
era = GetBlockEra(blockNum, eraLength)
if era.Cmp(big.NewInt(1)) != 0 {
t.Error("Should return Era 1", "era", era)
}
}
func TestGetRewardForUngle(t *testing.T) {
reward := getRewardForUncle(1, false).String()
expectedReward := "156250000000000000"
if expectedReward != reward {
t.Errorf("Incorrect uncle bonus for height %v, expected %v vs %v", 1, expectedReward, reward)
func TestGetBlockWinnerRewardByEra(t *testing.T) {
baseReward := big.NewInt(5000000000000000000)
era := big.NewInt(0)
blockReward := GetBlockWinnerRewardByEra(era, baseReward)
if blockReward.Cmp(big.NewInt(5000000000000000000)) != 0 {
t.Error("Should return blockReward 5000000000000000000", "reward", blockReward)
}
era = big.NewInt(1)
blockReward = GetBlockWinnerRewardByEra(era, baseReward)
if blockReward.Cmp(big.NewInt(4000000000000000000)) != 0 {
t.Error("Should return blockReward 4000000000000000000", "reward", blockReward)
}
era = big.NewInt(2)
blockReward = GetBlockWinnerRewardByEra(era, baseReward)
if blockReward.Cmp(big.NewInt(3200000000000000000)) != 0 {
t.Error("Should return blockReward 3200000000000000000", "reward", blockReward)
}
era = big.NewInt(3)
blockReward = GetBlockWinnerRewardByEra(era, baseReward)
if blockReward.Cmp(big.NewInt(2560000000000000000)) != 0 {
t.Error("Should return blockReward 2560000000000000000", "reward", blockReward)
}
era = big.NewInt(4)
blockReward = GetBlockWinnerRewardByEra(era, baseReward)
if blockReward.Cmp(big.NewInt(2048000000000000000)) != 0 {
t.Error("Should return blockReward 2048000000000000000", "reward", blockReward)
}
}
func TestGetByzantiumRewardForUngle(t *testing.T) {
reward := getRewardForUncle(byzantiumHardForkHeight, false).String()
expectedReward := "93750000000000000"
if expectedReward != reward {
t.Errorf("Incorrect uncle bonus for height %v, expected %v vs %v", byzantiumHardForkHeight, expectedReward, reward)
func TestGetRewardForUncle(t *testing.T) {
baseReward := big.NewInt(4000000000000000000)
uncleReward := getRewardForUncle(baseReward)
if uncleReward.Cmp(big.NewInt(125000000000000000)) != 0 {
t.Error("Should return uncleReward 125000000000000000", "reward", uncleReward)
}
baseReward = big.NewInt(3200000000000000000)
uncleReward = getRewardForUncle(baseReward)
if uncleReward.Cmp(big.NewInt(100000000000000000)) != 0 {
t.Error("Should return uncleReward 100000000000000000", "reward", uncleReward)
}
baseReward = big.NewInt(2560000000000000000)
uncleReward = getRewardForUncle(baseReward)
if uncleReward.Cmp(big.NewInt(80000000000000000)) != 0 {
t.Error("Should return uncleReward 80000000000000000", "reward", uncleReward)
}
baseReward = big.NewInt(2048000000000000000)
uncleReward = getRewardForUncle(baseReward)
if uncleReward.Cmp(big.NewInt(64000000000000000)) != 0 {
t.Error("Should return uncleReward 64000000000000000", "reward", uncleReward)
}
}

4
policy/policy.go

@ -9,8 +9,8 @@ import (
"sync/atomic"
"time"
"github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/storage"
"github.com/etclabscore/open-etc-pool/util"
)
type Config struct {

4
proxy/blocks.go

@ -9,8 +9,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/rpc"
"github.com/etclabscore/open-etc-pool/util"
)
const maxBacklog = 3

25
proxy/config.go

@ -1,10 +1,10 @@
package proxy
import (
"github.com/yuriy0803/open-etc-pool-friends/api"
"github.com/yuriy0803/open-etc-pool-friends/payouts"
"github.com/yuriy0803/open-etc-pool-friends/policy"
"github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/etclabscore/open-etc-pool/api"
"github.com/etclabscore/open-etc-pool/payouts"
"github.com/etclabscore/open-etc-pool/policy"
"github.com/etclabscore/open-etc-pool/storage"
)
type Config struct {
@ -16,9 +16,10 @@ type Config struct {
Threads int `json:"threads"`
Coin string `json:"coin"`
Pplns int64 `json:"pplns"`
Redis storage.Config `json:"redis"`
Network string `json:"network"`
Coin string `json:"coin"`
Pplns int64 `json:"pplns"`
Redis storage.Config `json:"redis"`
BlockUnlocker payouts.UnlockerConfig `json:"unlocker"`
Payouts payouts.PayoutsConfig `json:"payouts"`
@ -39,7 +40,6 @@ type Proxy struct {
Difficulty int64 `json:"difficulty"`
StateUpdateInterval string `json:"stateUpdateInterval"`
HashrateExpiration string `json:"hashrateExpiration"`
StratumHostname string `json:"stratumHostname"`
Policy policy.Config `json:"policy"`
@ -47,6 +47,8 @@ type Proxy struct {
HealthCheck bool `json:"healthCheck"`
Stratum Stratum `json:"stratum"`
StratumNiceHash StratumNiceHash `json:"stratum_nice_hash"`
}
type Stratum struct {
@ -56,6 +58,13 @@ type Stratum struct {
MaxConn int `json:"maxConn"`
}
type StratumNiceHash struct {
Enabled bool `json:"enabled"`
Listen string `json:"listen"`
Timeout string `json:"timeout"`
MaxConn int `json:"maxConn"`
}
type Upstream struct {
Name string `json:"name"`
Url string `json:"url"`

25
proxy/handlers.go

@ -4,10 +4,10 @@ import (
"log"
"regexp"
"strings"
"errors"
"errors"
"github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/rpc"
"github.com/etclabscore/open-etc-pool/util"
)
// Allow only lowercase hexadecimal with 0x prefix
@ -55,7 +55,7 @@ func (s *ProxyServer) handleTCPSubmitRPC(cs *Session, id string, params []string
}
func (s *ProxyServer) handleSubmitRPC(cs *Session, login, id string, params []string) (bool, *ErrorReply) {
if !workerPattern.MatchString(id){
if !workerPattern.MatchString(id) {
id = "0"
}
if len(params) != 3 {
@ -69,44 +69,29 @@ func (s *ProxyServer) handleSubmitRPC(cs *Session, login, id string, params []st
log.Printf("Malformed PoW result from %s@%s %v", login, cs.ip, params)
return false, &ErrorReply{Code: -1, Message: "Malformed PoW result"}
}
go func(s *ProxyServer, cs *Session, login, id string, params []string) {
t := s.currentBlockTemplate()
//MFO: This function (s.processShare) will process a share as per hasher.Verify function of github.com/ethereum/ethash
// output of this function is either:
// true,true (Exists) which means share already exists and it is validShare
// true,false (Exists & invalid)which means share already exists and it is invalidShare or it is a block <-- should not ever happen
// false,false (stale/invalid)which means share is new, and it is not a block, might be a stale share or invalidShare
// false,true (valid)which means share is new, and it is a block or accepted share
// When this function finishes, the results is already recorded in the db for valid shares or blocks.
exist, validShare := s.processShare(login, id, cs.ip, t, params)
ok := s.policy.ApplySharePolicy(cs.ip, !exist && validShare)
// if true,true or true,false
if exist {
log.Printf("Duplicate share from %s@%s %v", login, cs.ip, params)
cs.lastErr = errors.New("Duplicate share")
}
// if false, false
if !validShare {
//MFO: Here we have an invalid share
log.Printf("Invalid share from %s@%s", login, cs.ip)
// Bad shares limit reached, return error and close
if !ok {
cs.lastErr = errors.New("Invalid share")
}
}
//MFO: Here we have a valid share and it is already recorded in DB by miner.go
// if false, true
log.Printf("Valid share from %s@%s", login, cs.ip)
if !ok {
cs.lastErr = errors.New("High rate of invalid shares")
}
}(s, cs, login, id, params)
}(s, cs, login, id, params)
return true, nil
}

37
proxy/miner.go

@ -6,31 +6,36 @@ import (
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/etclabscore/go-etchash"
"github.com/ethereum/go-ethereum/common"
)
var ecip1099FBlockClassic uint64 = 11700000 // classic mainnet
var ecip1099FBlockMordor uint64 = 2520000 // mordor testnet
var ecip1099FBlockMordor uint64 = 2520000 // mordor
var hasher = etchash.New(&ecip1099FBlockClassic)
var hasher *etchash.Etchash = nil
func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, params []string) (bool, bool) {
// Now, the function received some work with login id and worker name and all information, ready to be processed
// and checked if it is a valid work or not, and if it is a block or not and write to db accordingly
if hasher == nil {
if s.config.Network == "classic" {
hasher = etchash.New(&ecip1099FBlockClassic)
} else if s.config.Network == "mordor" {
hasher = etchash.New(&ecip1099FBlockMordor)
} else {
// unknown network
log.Printf("Unknown network configuration %s", s.config.Network)
return false, false
}
}
nonceHex := params[0]
hashNoNonce := params[1]
mixDigest := params[2]
nonce, _ := strconv.ParseUint(strings.Replace(nonceHex, "0x", "", -1), 16, 64)
shareDiff := s.config.Proxy.Difficulty
stratumHostname := s.config.Proxy.StratumHostname
h, ok := t.headers[hashNoNonce]
if !ok {
log.Printf("Stale share from %v@%v", login, ip)
// Here we have a stale share, we need to create a redis function as follows
// CASE1: stale Share
// s.backend.WriteWorkerShareStatus(login, id, valid bool, stale bool, invalid bool)
return false, false
}
@ -51,9 +56,6 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
}
if !hasher.Verify(share) {
// THis is an invalid block, record it
// CASE2: invalid Share
// s.backend.WriteWorkerShareStatus(login, id, valid bool, stale bool, invalid bool)
return false, false
}
@ -66,7 +68,7 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
return false, false
} else {
s.fetchBlockTemplate()
exist, err := s.backend.WriteBlock(login, id, params, shareDiff, h.diff.Int64(), h.height, s.hashrateExpiration, stratumHostname)
exist, err := s.backend.WriteBlock(login, id, params, shareDiff, h.diff.Int64(), h.height, s.hashrateExpiration)
if exist {
return true, false
}
@ -75,23 +77,16 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param
} else {
log.Printf("Inserted block %v to backend", h.height)
}
// Here we have a valid share, which is in-fact a block and it is written to db
log.Printf("Block found by miner %v@%v at height %d", login, ip, h.height)
}
} else {
exist, err := s.backend.WriteShare(login, id, params, shareDiff, h.height, s.hashrateExpiration, stratumHostname)
exist, err := s.backend.WriteShare(login, id, params, shareDiff, h.height, s.hashrateExpiration)
if exist {
return true, false
}
if err != nil {
log.Println("Failed to insert share data into backend:", err)
}
// Here we have a valid share, which is only a share and it is written to db
}
// This means success, either a valid share or a valid block, in this case, record a valid share for the worker
// CASE3: Valid Share
// s.backend.WriteWorkerShareStatus(login, id, valid bool, stale bool, invalid bool)
return false, true
}

6
proxy/proto.go

@ -8,6 +8,12 @@ type JSONRpcReq struct {
Params json.RawMessage `json:"params"`
}
type JSONRpcReqNH struct {
Id interface{} `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type StratumReq struct {
JSONRpcReq
Worker string `json:"worker"`

23
proxy/proxy.go

@ -13,10 +13,10 @@ import (
"github.com/gorilla/mux"
"github.com/yuriy0803/open-etc-pool-friends/policy"
"github.com/yuriy0803/open-etc-pool-friends/rpc"
"github.com/yuriy0803/open-etc-pool-friends/storage"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/policy"
"github.com/etclabscore/open-etc-pool/rpc"
"github.com/etclabscore/open-etc-pool/storage"
"github.com/etclabscore/open-etc-pool/util"
)
type ProxyServer struct {
@ -34,6 +34,13 @@ type ProxyServer struct {
sessionsMu sync.RWMutex
sessions map[*Session]struct{}
timeout time.Duration
Extranonce string
}
type jobDetails struct {
JobID string
SeedHash string
HeaderHash string
}
type Session struct {
@ -42,9 +49,11 @@ type Session struct {
// Stratum
sync.Mutex
conn *net.TCPConn
login string
lastErr error
conn *net.TCPConn
login string
lastErr error
subscriptionID string
JobDeatils jobDetails
}
func NewProxy(cfg *Config, backend *storage.RedisClient) *ProxyServer {

21
proxy/stratum.go

@ -9,7 +9,7 @@ import (
"net"
"time"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/util"
)
const (
@ -20,11 +20,11 @@ func (s *ProxyServer) ListenTCP() {
timeout := util.MustParseDuration(s.config.Proxy.Stratum.Timeout)
s.timeout = timeout
addr, err := net.ResolveTCPAddr("tcp", s.config.Proxy.Stratum.Listen)
addr, err := net.ResolveTCPAddr("tcp4", s.config.Proxy.Stratum.Listen)
if err != nil {
log.Fatalf("Error: %v", err)
}
server, err := net.ListenTCP("tcp", addr)
server, err := net.ListenTCP("tcp4", addr)
if err != nil {
log.Fatalf("Error: %v", err)
}
@ -66,7 +66,6 @@ func (s *ProxyServer) handleTCPClient(cs *Session) error {
cs.enc = json.NewEncoder(cs.conn)
connbuff := bufio.NewReaderSize(cs.conn, MaxReqSize)
s.setDeadline(cs.conn)
for {
data, isPrefix, err := connbuff.ReadLine()
if isPrefix {
@ -103,20 +102,6 @@ func (s *ProxyServer) handleTCPClient(cs *Session) error {
func (cs *Session) handleTCPMessage(s *ProxyServer, req *StratumReq) error {
// Handle RPC methods
switch req.Method {
// claymore -esm 1
case "eth_login":
var params []string
err := json.Unmarshal(req.Params, &params)
if err != nil {
log.Println("Malformed stratum request params from", cs.ip)
return err
}
reply, errReply := s.handleLoginRPC(cs, params, req.Worker)
if errReply != nil {
return cs.sendTCPError(req.Id, errReply)
}
return cs.sendTCPResult(req.Id, reply)
// claymore -esm 0
case "eth_submitLogin":
var params []string
err := json.Unmarshal(req.Params, &params)

4
rpc/rpc.go

@ -14,7 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/util"
)
type RPCClient struct {
@ -99,7 +99,7 @@ func (r *RPCClient) GetWork() ([]string, error) {
}
func (r *RPCClient) GetPendingBlock() (*GetBlockReplyPart, error) {
rpcResp, err := r.doPost(r.Url, "eth_getBlockByNumber", []interface{}{"latest", true})
rpcResp, err := r.doPost(r.Url, "eth_getBlockByNumber", []interface{}{"pending", false})
if err != nil {
return nil, err
}

2
scripts/start_2_bil.sh

@ -1,2 +0,0 @@
#!/bin/bash
./build/bin/open-etc-pool-friends ./configs/stratum2b.json

108
service_installer.sh

@ -1,108 +0,0 @@
#!/bin/bash
#will make the services for the pool, based on the pool exe location of /usr/local/bin/poolbin
user="perklepool"
coin="prkl"
config_dir="/home/$user/open-etc-pool-friends/configs"
poolbinary="/home/$user/open-etc-pool-friends/build/bin/open-etc-pool-friends"
if [ ! -e $config_dir ] || [ ! -e $poolbinary ]
then
echo missing config dir or pool binary, exiting
exit 1
fi
echo "
[Unit]
Description=$coin-api
[Service]
Type=simple
ExecStart=$poolbinary $config_dir/api.json
[Install]
WantedBy=multi-user.target
">/etc/systemd/system/$coin-api.service
echo "
[Unit]
Description=$coin-stratum2b
[Service]
Type=simple
ExecStart=$poolbinary $config_dir/stratum2b.json
[Install]
WantedBy=multi-user.target
">/etc/systemd/system/$coin-stratum2b.service
echo "
[Unit]
Description=$coin-stratum4b
[Service]
Type=simple
ExecStart=$poolbinary $config_dir/stratum4b.json
[Install]
WantedBy=multi-user.target
">/etc/systemd/system/$coin-stratum4b.service
echo "
[Unit]
Description=$coin-stratum9b
[Service]
Type=simple
ExecStart=$poolbinary $config_dir/stratum9b.json
[Install]
WantedBy=multi-user.target
">/etc/systemd/system/$coin-stratum9b.service
echo "
[Unit]
Description=$coin-unlocker
[Service]
Type=simple
ExecStart=$poolbinary $config_dir/unlocker.json
[Install]
WantedBy=multi-user.target
">/etc/systemd/system/$coin-unlocker.service
echo "
[Unit]
Description=$coin-payout
[Service]
Type=simple
ExecStart=$poolbinary $config_dir/payout.json
[Install]
WantedBy=multi-user.target
">/etc/systemd/system/$coin-payout.service
systemctl daemon-reload
systemctl enable $coin-api
systemctl enable $coin-stratum2b
systemctl enable $coin-stratum4b
systemctl enable $coin-stratum9b
#systemctl enable $coin-unlocker
#systemctl enable $coin-payout
systemctl start $coin-api
systemctl start $coin-stratum2b
systemctl start $coin-stratum4b
systemctl start $coin-stratum9b
#systemctl start $coin-unlocker
#systemctl start $coin-payout

238
storage/redis.go

@ -10,7 +10,7 @@ import (
"gopkg.in/redis.v3"
"github.com/yuriy0803/open-etc-pool-friends/util"
"github.com/etclabscore/open-etc-pool/util"
)
type Config struct {
@ -23,7 +23,7 @@ type Config struct {
type RedisClient struct {
client *redis.Client
prefix string
pplns int64
pplns int64
}
type PoolCharts struct {
@ -47,19 +47,21 @@ type PaymentCharts struct {
}
type SumRewardData struct {
Interval int64 `json:"inverval"`
Reward int64 `json:"reward"`
Name string `json:"name"`
Offset int64 `json:"offset"`
Interval int64 `json:"inverval"`
Reward int64 `json:"reward"`
Name string `json:"name"`
Offset int64 `json:"offset"`
}
type RewardData struct {
Height int64 `json:"blockheight"`
Timestamp int64 `json:"timestamp"`
BlockHash string `json:"blockhash"`
Reward int64 `json:"reward"`
Percent float64 `json:"percent"`
Immature bool `json:"immature"`
Height int64 `json:"blockheight"`
Timestamp int64 `json:"timestamp"`
BlockHash string `json:"blockhash"`
Reward int64 `json:"reward"`
Percent float64 `json:"percent"`
Immature bool `json:"immature"`
}
type BlockData struct {
@ -107,31 +109,22 @@ func (b *BlockData) key() string {
type Miner struct {
LastBeat int64 `json:"lastBeat"`
HR int64 `json:"hr"`
Shares int `json:"shares"`
Offline bool `json:"offline"`
startedAt int64
}
// Addition from Mohannad Otaibi to report Difficulty
type Worker struct {
Miner
TotalHR int64 `json:"hr2"`
WorkerDiff int64 `json:"difficulty"`
WorkerHostname string `json:"hostname"`
TotalShares int `json:"valid"`
TotalHR int64 `json:"hr2"`
}
func NewRedisClient(cfg *Config, prefix string, pplns int64) *RedisClient {
options := redis.Options{
client := redis.NewClient(&redis.Options{
Addr: cfg.Endpoint,
Password: cfg.Password,
DB: cfg.Database,
PoolSize: cfg.PoolSize,
}
if cfg.Endpoint[0:1] == "/" {
options.Network = "unix"
}
client := redis.NewClient(&options)
})
return &RedisClient{client: client, prefix: prefix, pplns: pplns}
}
@ -209,11 +202,7 @@ func convertPoolChartsResults(raw *redis.ZSliceCmd) []*PoolCharts {
pc.PoolHash, _ = strconv.ParseInt(str[strings.LastIndex(str, ":")+1:], 10, 64)
result = append(result, &pc)
}
var reverse []*PoolCharts
for i := len(result) - 1; i >= 0; i-- {
reverse = append(reverse, result[i])
}
return reverse
return result
}
func convertMinerChartsResults(raw *redis.ZSliceCmd) []*MinerCharts {
@ -229,11 +218,7 @@ func convertMinerChartsResults(raw *redis.ZSliceCmd) []*MinerCharts {
mc.WorkerOnline = strings.Split(str, ":")[4]
result = append(result, &mc)
}
var reverse []*MinerCharts
for i := len(result) - 1; i >= 0; i-- {
reverse = append(reverse, result[i])
}
return reverse
return result
}
func (r *RedisClient) GetAllMinerAccount() (account []string, err error) {
@ -340,7 +325,7 @@ func (r *RedisClient) checkPoWExist(height uint64, params []string) (bool, error
return val == 0, err
}
func (r *RedisClient) WriteShare(login, id string, params []string, diff int64, height uint64, window time.Duration, hostname string) (bool, error) {
func (r *RedisClient) WriteShare(login, id string, params []string, diff int64, height uint64, window time.Duration) (bool, error) {
exist, err := r.checkPoWExist(height, params)
if err != nil {
return false, err
@ -356,14 +341,14 @@ func (r *RedisClient) WriteShare(login, id string, params []string, diff int64,
ts := ms / 1000
_, err = tx.Exec(func() error {
r.writeShare(tx, ms, ts, login, id, diff, window, hostname)
r.writeShare(tx, ms, ts, login, id, diff, window)
tx.HIncrBy(r.formatKey("stats"), "roundShares", diff)
return nil
})
return false, err
}
func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundDiff int64, height uint64, window time.Duration, hostname string) (bool, error) {
func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundDiff int64, height uint64, window time.Duration) (bool, error) {
exist, err := r.checkPoWExist(height, params)
if err != nil {
return false, err
@ -379,7 +364,7 @@ func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundD
ts := ms / 1000
cmds, err := tx.Exec(func() error {
r.writeShare(tx, ms, ts, login, id, diff, window, hostname)
r.writeShare(tx, ms, ts, login, id, diff, window)
tx.HSet(r.formatKey("stats"), "lastBlockFound", strconv.FormatInt(ts, 10))
tx.HDel(r.formatKey("stats"), "roundShares")
tx.ZIncrBy(r.formatKey("finders"), 1, login)
@ -393,9 +378,9 @@ func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundD
return false, err
} else {
shares := cmds[len(cmds)-1].(*redis.StringSliceCmd).Val()
shares := cmds[len(cmds) - 1].(*redis.StringSliceCmd).Val()
tx2 := r.client.Multi()
tx2 := r.client.Multi()
defer tx2.Close()
totalshares := make(map[string]int64)
@ -413,7 +398,7 @@ func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundD
return false, err
}
sharesMap, _ := cmds[len(cmds)-3].(*redis.StringStringMapCmd).Result()
sharesMap, _ := cmds[len(cmds) - 3].(*redis.StringStringMapCmd).Result()
totalShares := int64(0)
for _, v := range sharesMap {
n, _ := strconv.ParseInt(v, 10, 64)
@ -426,29 +411,16 @@ func (r *RedisClient) WriteBlock(login, id string, params []string, diff, roundD
}
}
// ID is the worker name
func (r *RedisClient) writeShare(tx *redis.Multi, ms, ts int64, login, id string, diff int64, expire time.Duration, hostname string) {
/* # Note To Me:
Will have to write to get from redis the current value for round
shares and increase by 1, then include the new number to be added to redis
*/
func (r *RedisClient) writeShare(tx *redis.Multi, ms, ts int64, login, id string, diff int64, expire time.Duration) {
times := int(diff / 1000000000)
// Moved get hostname to stratums
for i := 0; i < times; i++ {
tx.LPush(r.formatKey("lastshares"), login)
}
tx.LTrim(r.formatKey("lastshares"), 0, r.pplns)
tx.HIncrBy(r.formatKey("shares", "roundCurrent"), login, diff)
// For aggregation of hashrate, to store value in hashrate key
tx.ZAdd(r.formatKey("hashrate"), redis.Z{Score: float64(ts), Member: join(diff, login, id, ms, diff, hostname)})
// For separate miner's workers hashrate, to store under hashrate table under login key
tx.ZAdd(r.formatKey("hashrate", login), redis.Z{Score: float64(ts), Member: join(diff, id, ms, diff, hostname)})
// Will delete hashrates for miners that gone
tx.Expire(r.formatKey("hashrate", login), expire)
tx.ZAdd(r.formatKey("hashrate"), redis.Z{Score: float64(ts), Member: join(diff, login, id, ms)})
tx.ZAdd(r.formatKey("hashrate", login), redis.Z{Score: float64(ts), Member: join(diff, id, ms)})
tx.Expire(r.formatKey("hashrate", login), expire) // Will delete hashrates for miners that gone
tx.HSet(r.formatKey("miners", login), "lastShare", strconv.FormatInt(ts, 10))
}
@ -478,13 +450,6 @@ func join(args ...interface{}) string {
} else {
s[i] = "0"
}
case *big.Int:
n := v.(*big.Int)
if n != nil {
s[i] = n.String()
} else {
s[i] = "0"
}
case *big.Rat:
x := v.(*big.Rat)
if x != nil {
@ -492,7 +457,13 @@ func join(args ...interface{}) string {
} else {
s[i] = "0"
}
case *big.Int:
n := v.(*big.Int)
if n != nil {
s[i] = n.String()
} else {
s[i] = "0"
}
default:
panic("Invalid type specified for conversion")
}
@ -519,12 +490,12 @@ func (r *RedisClient) GetImmatureBlocks(maxHeight int64) ([]*BlockData, error) {
}
func (r *RedisClient) GetRewards(login string) ([]*RewardData, error) {
option := redis.ZRangeByScore{Min: "0", Max: strconv.FormatInt(10, 10)}
cmd := r.client.ZRangeByScoreWithScores(r.formatKey("rewards", login), option)
if cmd.Err() != nil {
return nil, cmd.Err()
}
return convertRewardResults(cmd), nil
option := redis.ZRangeByScore{Min: "0", Max: strconv.FormatInt(10, 10)}
cmd := r.client.ZRangeByScoreWithScores(r.formatKey("rewards", login), option)
if cmd.Err() != nil {
return nil, cmd.Err()
}
return convertRewardResults(cmd), nil
}
func (r *RedisClient) GetRoundShares(height int64, nonce string) (map[string]int64, error) {
@ -679,9 +650,9 @@ func (r *RedisClient) WritePayment(login, txHash string, amount int64) error {
tx.HIncrBy(r.formatKey("finances"), "pending", (amount * -1))
tx.HIncrBy(r.formatKey("finances"), "paid", amount)
tx.ZAdd(r.formatKey("payments", "all"), redis.Z{Score: float64(ts), Member: join(txHash, login, amount)})
tx.ZRemRangeByRank(r.formatKey("payments", "all"), 0, -10000)
tx.ZRemRangeByRank(r.formatKey("payments", "all"), 0, -10000)
tx.ZAdd(r.formatKey("payments", login), redis.Z{Score: float64(ts), Member: join(txHash, amount)})
tx.ZRemRangeByRank(r.formatKey("payments", login), 0, -100)
tx.ZRemRangeByRank(r.formatKey("payments", login), 0, -100)
tx.ZRem(r.formatKey("payments", "pending"), join(login, amount))
tx.Del(r.formatKey("payments", "lock"))
tx.HIncrBy(r.formatKey("paymentsTotal"), "all", 1)
@ -692,24 +663,24 @@ func (r *RedisClient) WritePayment(login, txHash string, amount int64) error {
}
func (r *RedisClient) WriteReward(login string, amount int64, percent *big.Rat, immature bool, block *BlockData) error {
if amount <= 0 {
if (amount <= 0) {
return nil
}
tx := r.client.Multi()
defer tx.Close()
tx := r.client.Multi()
defer tx.Close()
addStr := join(amount, percent, immature, block.Hash, block.Height, block.Timestamp)
remStr := join(amount, percent, !immature, block.Hash, block.Height, block.Timestamp)
remscore := block.Timestamp - 3600*24*40 // Store the last 40 Days
addStr := join(amount, percent, immature, block.Hash, block.Height, block.Timestamp)
remStr := join(amount, percent, !immature, block.Hash, block.Height, block.Timestamp)
remscore := block.Timestamp - 3600 * 24 * 40 // Store the last 40 Days
_, err := tx.Exec(func() error {
tx.ZAdd(r.formatKey("rewards", login), redis.Z{Score: float64(block.Timestamp), Member: addStr})
tx.ZRem(r.formatKey("rewards", login), remStr)
tx.ZRemRangeByScore(r.formatKey("rewards", login), "-inf", "("+strconv.FormatInt(remscore, 10))
_, err := tx.Exec(func() error {
tx.ZAdd(r.formatKey("rewards", login), redis.Z{Score: float64(block.Timestamp), Member: addStr})
tx.ZRem(r.formatKey("rewards", login), remStr)
tx.ZRemRangeByScore(r.formatKey("rewards", login), "-inf", "(" + strconv.FormatInt(remscore, 10))
return nil
})
return err
return nil
})
return err
}
func (r *RedisClient) WriteImmatureBlock(block *BlockData, roundRewards map[string]int64) error {
@ -769,7 +740,7 @@ func (r *RedisClient) WriteMaturedBlock(block *BlockData, roundRewards map[strin
tx.HSet(r.formatKey("finances"), "lastCreditHeight", strconv.FormatInt(block.Height, 10))
tx.HSet(r.formatKey("finances"), "lastCreditHash", block.Hash)
tx.HIncrBy(r.formatKey("finances"), "totalMined", block.RewardInShannon())
tx.Expire(r.formatKey("credits", block.Height, block.Hash), 604800*time.Second)
tx.Expire(r.formatKey("credits", block.Height, block.Hash), 604800 * time.Second)
return nil
})
return err
@ -848,7 +819,6 @@ func (r *RedisClient) GetMinerStats(login string, maxPayments int64) (map[string
tx.LRange(r.formatKey("lastshares"), 0, r.pplns)
tx.ZRevRangeWithScores(r.formatKey("rewards", login), 0, 39)
tx.ZRevRangeWithScores(r.formatKey("rewards", login), 0, -1)
return nil
})
@ -945,7 +915,7 @@ func (r *RedisClient) CollectStats(smallWindow time.Duration, maxBlocks, maxPaym
tx.ZCard(r.formatKey("blocks", "matured"))
tx.HGet(r.formatKey("paymentsTotal"), "all")
tx.ZRevRangeWithScores(r.formatKey("payments", "all"), 0, maxPayments-1)
tx.LLen(r.formatKey("lastshares"))
tx.LLen(r.formatKey("lastshares"))
return nil
})
@ -954,7 +924,7 @@ func (r *RedisClient) CollectStats(smallWindow time.Duration, maxBlocks, maxPaym
}
result, _ := cmds[2].(*redis.StringStringMapCmd).Result()
result["nShares"] = strconv.FormatInt(cmds[11].(*redis.IntCmd).Val(), 10)
result["nShares"] = strconv.FormatInt(cmds[11].(*redis.IntCmd).Val(), 10)
stats["stats"] = convertStringMap(result)
candidates := convertCandidateResults(cmds[3].(*redis.ZSliceCmd))
stats["candidates"] = candidates
@ -994,7 +964,6 @@ func (r *RedisClient) CollectWorkersStats(sWindow, lWindow time.Duration, login
tx.ZRangeWithScores(r.formatKey("hashrate", login), 0, -1)
tx.ZRevRangeWithScores(r.formatKey("rewards", login), 0, 39)
tx.ZRevRangeWithScores(r.formatKey("rewards", login), 0, -1)
return nil
})
@ -1037,7 +1006,6 @@ func (r *RedisClient) CollectWorkersStats(sWindow, lWindow time.Duration, login
totalHashrate += worker.TotalHR
workers[id] = worker
}
stats["workers"] = workers
stats["workersTotal"] = len(workers)
stats["workersOnline"] = online
@ -1046,20 +1014,22 @@ func (r *RedisClient) CollectWorkersStats(sWindow, lWindow time.Duration, login
stats["currentHashrate"] = currentHashrate
stats["rewards"] = convertRewardResults(cmds[2].(*redis.ZSliceCmd)) // last 40
rewards := convertRewardResults(cmds[3].(*redis.ZSliceCmd)) // all
rewards := convertRewardResults(cmds[3].(*redis.ZSliceCmd)) // all
var dorew []*SumRewardData
dorew = append(dorew, &SumRewardData{Name: "Last 60 minutes", Interval: 3600, Offset: 0})
dorew = append(dorew, &SumRewardData{Name: "Last 12 hours", Interval: 3600 * 12, Offset: 0})
dorew = append(dorew, &SumRewardData{Name: "Last 24 hours", Interval: 3600 * 24, Offset: 0})
dorew = append(dorew, &SumRewardData{Name: "Last 7 days", Interval: 3600 * 24 * 7, Offset: 0})
dorew = append(dorew, &SumRewardData{Name: "Last 30 days", Interval: 3600 * 24 * 30, Offset: 0})
dorew = append(dorew, &SumRewardData{ Name: "Last 60 minutes", Interval: 3600, Offset: 0 })
dorew = append(dorew, &SumRewardData{ Name: "Last 12 hours", Interval: 3600 * 12, Offset: 0 })
dorew = append(dorew, &SumRewardData{ Name: "Last 24 hours", Interval: 3600 * 24, Offset: 0 })
dorew = append(dorew, &SumRewardData{ Name: "Last 7 days", Interval: 3600 * 24 * 7, Offset: 0 })
dorew = append(dorew, &SumRewardData{ Name: "Last 30 days", Interval: 3600 * 24 * 30, Offset: 0 })
for _, reward := range rewards {
for _, dore := range dorew {
for _,dore := range dorew {
dore.Reward += 0
if reward.Timestamp > now-dore.Interval {
if reward.Timestamp > now - dore.Interval {
dore.Reward += reward.Reward
}
}
@ -1144,23 +1114,23 @@ func convertCandidateResults(raw *redis.ZSliceCmd) []*BlockData {
}
func convertRewardResults(rows ...*redis.ZSliceCmd) []*RewardData {
var result []*RewardData
for _, row := range rows {
for _, v := range row.Val() {
// "amount:percent:immature:block.Hash:block.height"
reward := RewardData{}
reward.Timestamp = int64(v.Score)
fields := strings.Split(v.Member.(string), ":")
//block.UncleHeight, _ = strconv.ParseInt(fields[0], 10, 64)
reward.BlockHash = fields[3]
var result []*RewardData
for _, row := range rows {
for _, v := range row.Val() {
// "amount:percent:immature:block.Hash:block.height"
reward := RewardData{}
reward.Timestamp = int64(v.Score)
fields := strings.Split(v.Member.(string), ":")
//block.UncleHeight, _ = strconv.ParseInt(fields[0], 10, 64)
reward.BlockHash = fields[3]
reward.Reward, _ = strconv.ParseInt(fields[0], 10, 64)
reward.Percent, _ = strconv.ParseFloat(fields[1], 64)
reward.Percent, _ = strconv.ParseFloat(fields[1], 64)
reward.Immature, _ = strconv.ParseBool(fields[2])
reward.Height, _ = strconv.ParseInt(fields[4], 10, 64)
result = append(result, &reward)
}
}
return result
result = append(result, &reward)
}
}
return result
}
func convertBlockResults(rows ...*redis.ZSliceCmd) []*BlockData {
@ -1198,32 +1168,16 @@ func convertWorkersStats(window int64, raw *redis.ZSliceCmd) map[string]Worker {
for _, v := range raw.Val() {
parts := strings.Split(v.Member.(string), ":")
share, _ := strconv.ParseInt(parts[0], 10, 64)
//By Mohannad
var hostname string
if len(parts) > 3 {
hostname = parts[4]
} else {
hostname = "unknown"
}
id := parts[1]
score := int64(v.Score)
worker := workers[id]
// Add for large window
worker.TotalHR += share
worker.TotalShares += 1
// Addition from Mohannad Otaibi to report Difficulty
worker.WorkerDiff = share
worker.WorkerHostname = hostname
// End Mohannad Adjustments
// Add for small window if matches
if score >= now-window {
worker.HR += share
worker.Shares += 1
}
if worker.LastBeat < score {
@ -1296,11 +1250,7 @@ func convertPaymentsResults(raw *redis.ZSliceCmd) []map[string]interface{} {
}
result = append(result, tx)
}
var reverse []map[string]interface{}
for i := len(result) - 1; i >= 0; i-- {
reverse = append(reverse, result[i])
}
return reverse
return result
}
/*
@ -1331,19 +1281,5 @@ func convertPaymentChartsResults(raw *redis.ZSliceCmd) []*PaymentCharts {
result = append(result, &pc)
}
}
var reverse []*PaymentCharts
for i := len(result) - 1; i >= 0; i-- {
reverse = append(reverse, result[i])
}
return reverse
}
func (r *RedisClient) GetCurrentHashrate(login string) (int64, error) {
hashrate := r.client.HGet(r.formatKey("currenthashrate", login), "hashrate")
if hashrate.Err() == redis.Nil {
return 0, nil
} else if hashrate.Err() != nil {
return 0, hashrate.Err()
}
return hashrate.Int64()
return result
}

2
util/util.go

@ -7,8 +7,8 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
)
var Ether = math.BigPow(10, 18)

2
www/.gitignore vendored

@ -7,6 +7,7 @@
# dependencies
/node_modules
/bower_components
/chart/node_modules
# misc
/.sass-cache
@ -15,3 +16,4 @@
/libpeerconnection.log
npm-debug.log
testem.log
/config/environment.js

1
www/README.md

@ -50,4 +50,3 @@ Specify what it takes to deploy your app.
* Development Browser Extensions
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
* [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)

0
www/app/.watchmanconfig

58
www/app/adapters/chart.js

@ -0,0 +1,58 @@
import JSONAPIAdapter from 'ember-data/adapters/json-api';
export default JSONAPIAdapter.extend({
namespace: 'api',
host: 'http://45.63.65.79:4500'
});
// {
// title: "And",
// content: 'a',
// author: 'one'
// },
// {
// title: "And",
// content: 'a',
// author: 'two'
// },
// {
// title: "And",
// content: 'a',
// author: 'three'
// }
//
// [{
// title: 'Test Note 1',
// content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaeca cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
// author: 'Ryan Christiani'
// }, {
// title: 'Test Note 2',
// content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaeca cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaeca cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
// author: 'Ryan Christiani'
// }, {
// title: 'Test Note 3',
// content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaeca cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaeca cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
// author: 'Ryan Christiani'
// }]
// [{
// name: 'gear',
// colorByPoint: true,
// chartdata: [
// {y: 10, name: 'Hi1'},
// {y: 12, name: 'hi2'},
// {y: 40, name: 'Hi3'}
// ]
// }]
// [{
// name: 'gear',
// colorByPoint: true,
// chartdata: 1,
// }, {
// name: 'gear',
// colorByPoint: true,
// chartdata: 2,
// }, {
// name: 'gear',
// colorByPoint: true,
// chartdata: 3,
// }]

94
www/app/components/chart-diff.js

@ -0,0 +1,94 @@
import Ember from 'ember';
export default Ember.Component.extend({
// summaryOptions: {
// chart: {
// plotBackgroundColor: null,
// plotBorderWidth: null,
// plotShadow: false,
// type: 'pie'
// },
// title: {
// text: 'Total weight of gear in each category'
// },
// tooltip: {
// pointFormat: '<b>{point.percentage:.1f}%</b> of {series.name}'
// },
// plotOptions: {
// pie: {
// allowPointSelect: true,
// cursor: 'pointer',
// dataLabels: {
// enabled: false
// },
// showInLegend: true
// }
// }
// },
// summaryData: [{
// name: 'gear',
// colorByPoint: true,
// data: [
// {y: 10, name: 'Hi1'},
// {y: 12, name: 'hi2'},
// {y: 40, name: 'Hi3'}
// ]
// }]
summaryOptions : {
title: {
text: 'Monthly Average Temperature',
x: -20 //center
},
subtitle: {
text: 'Source: WorldClimate.com',
x: -20
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
yAxis: {
title: {
text: 'Temperature (°C)'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
valueSuffix: '°C'
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0
}
},
summaryData : [{
name: 'Tokyo',
data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
}, {
name: 'New York',
data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]
}, {
name: 'Berlin',
data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]
}, {
name: 'London',
data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]
}]
});
// [{
// name: 'gear',
// colorByPoint: true,
// chartdata: [
// {y: 10, name: 'Hi1'},
// {y: 12, name: 'hi2'},
// {y: 40, name: 'Hi3'}
// ]
// }]

69
www/app/controllers/account.js

@ -4,14 +4,13 @@ export default Ember.Controller.extend({
applicationController: Ember.inject.controller('application'),
config: Ember.computed.reads('applicationController.config'),
stats: Ember.computed.reads('applicationController.model.stats'),
hashrate: Ember.computed.reads('applicationController.hashrate'),
chartOptions: Ember.computed("model.hashrate", {
get() {
var e = this,
t = e.getWithDefault("model.minerCharts"),
a = {
chart: {
backgroundColor: "rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.1)",
type: "spline",
marginRight: 10,
height: 400,
@ -19,10 +18,10 @@ export default Ember.Controller.extend({
load: function() {
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getTime(),
var x = (new Date).getTime(),
y = e.getWithDefault("model.currentHashrate") / 1000000;
series.addPoint([x, y], true, true);
}, 109000000);
series.addPoint([x, y], true, true)
}, 109000000)
}
}
},
@ -55,15 +54,11 @@ export default Ember.Controller.extend({
color: "#808080"
}],
legend: {
enabled: true,
itemStyle:
{
color:"#ffffff"
},
enabled: true
},
tooltip: {
formatter: function() {
return this.y > 1000000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000000).toFixed(2) + "&nbsp;TH/s</b>" : this.y > 1000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000).toFixed(2) + "&nbsp;GH/s</b>" : this.y > 1000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000).toFixed(2) + "&nbsp;MH/s</b>" : "<b>" + this.point.d + "<b><br>Hashrate&nbsp;<b>" + this.y.toFixed(2) + "&nbsp;H/s</b>";
return this.y > 1000000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000000).toFixed(2) + "&nbsp;TH/s</b>" : this.y > 1000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000).toFixed(2) + "&nbsp;GH/s</b>" : this.y > 1000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000).toFixed(2) + "&nbsp;MH/s</b>" : "<b>" + this.point.d + "<b><br>Hashrate&nbsp;<b>" + this.y.toFixed(2) + "&nbsp;H/s</b>"
},
@ -77,79 +72,61 @@ export default Ember.Controller.extend({
name: "Average hashrate",
data: function() {
var e, a = [];
if (null != t) {
if (null != t)
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].minerLargeHash;
a.push({
r = new Date(1e3 * t[e].x), l = r.toLocaleString(), n = t[e].minerLargeHash, a.push({
x: r,
d: l,
y: n
});
}
} else {
a.push({
})
} else a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
return a
}()
}, {
name: "Current hashrate",
data: function() {
var e, a = [];
if (null != t) {
if (null != t)
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].minerHash;
a.push({
r = new Date(1e3 * t[e].x), l = r.toLocaleString(), n = t[e].minerHash, a.push({
x: r,
d: l,
y: n
});
}
} else {
a.push({
})
} else a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
});
return a
}()
}]
};
return a;
return a
}
}),
roundPercent: Ember.computed('stats', 'model', {
get() {
var percent = this.get('model.roundShares') / this.get('stats.nShares');
if (!percent) {
return 0;
}
if(percent>100){
return 100;
}
return percent;
}
}),
netHashrate: Ember.computed({
get() {
return this.get('hashrate');
}
}),
earnPerDay: Ember.computed('model', {
get() {
return 24 * 60 * 60 / this.get('config').BlockTime * this.get('config').BlockReward *
this.getWithDefault('model.hashrate') / this.get('hashrate');
}
})
});

103
www/app/controllers/account/index.js

@ -2,29 +2,26 @@ import Ember from 'ember';
export default Ember.Controller.extend({
applicationController: Ember.inject.controller('application'),
netstats: Ember.computed.reads('applicationController'),
stats: Ember.computed.reads('applicationController.model.stats'),
config: Ember.computed.reads('applicationController.config'),
stats: Ember.computed.reads('applicationController.model.stats'),
chartOptions: Ember.computed("model.hashrate", {
get() {
var e = this,
t = e.getWithDefault("model.minerCharts"),
a = {
chart: {
backgroundColor: "rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.1)",
type: "spline",
marginRight: 10,
height: 200,
height: 400,
events: {
load: function() {
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getTime(),
var x = (new Date).getTime(),
y = e.getWithDefault("model.currentHashrate") / 1000000;
series.addPoint([x, y], true, true);
}, 1090000000);
series.addPoint([x, y], true, true)
}, 109000000)
}
}
},
@ -33,11 +30,6 @@ export default Ember.Controller.extend({
},
xAxis: {
ordinal: false,
labels: {
style: {
color: "#ccc"
}
},
type: "datetime",
dateTimeLabelFormats: {
millisecond: "%H:%M:%S",
@ -52,34 +44,21 @@ export default Ember.Controller.extend({
},
yAxis: {
title: {
text: "Hashrate by Account",
style: {
color: "#ccc"
},
},
labels: {
style: {
color: "#ccc"
}
text: "HASHRATE"
},
//softMin: e.getWithDefault("model.currentHashrate") / 1000000,
//softMax: e.getWithDefault("model.currentHashrate") / 1000000,
min: 0
},
plotLines: [{
value: 0,
width: 1,
color: "#aaaaaa"
color: "#808080"
}],
legend: {
enabled: true,
itemStyle:
{
color:"#ccc"
},
enabled: true
},
tooltip: {
formatter: function() {
return this.y > 1000000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000000).toFixed(2) + "&nbsp;TH/s</b>" : this.y > 1000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000).toFixed(2) + "&nbsp;GH/s</b>" : this.y > 1000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000).toFixed(2) + "&nbsp;MH/s</b>" : "<b>" + this.point.d + "<b><br>Hashrate&nbsp;<b>" + this.y.toFixed(2) + "&nbsp;H/s</b>";
return this.y > 1000000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000000).toFixed(2) + "&nbsp;TH/s</b>" : this.y > 1000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000).toFixed(2) + "&nbsp;GH/s</b>" : this.y > 1000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000).toFixed(2) + "&nbsp;MH/s</b>" : "<b>" + this.point.d + "<b><br>Hashrate&nbsp;<b>" + this.y.toFixed(2) + "&nbsp;H/s</b>"
},
@ -90,62 +69,60 @@ export default Ember.Controller.extend({
},
series: [{
color: "#E99002",
name: "3 hours average hashrate",
name: "Average hashrate",
data: function() {
var e, a = [];
if (null != t) {
if (null != t)
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].minerLargeHash;
a.push({
r = new Date(1e3 * t[e].x), l = r.toLocaleString(), n = t[e].minerLargeHash, a.push({
x: r,
d: l,
y: n
});
}
} else {
a.push({
})
} else a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
});
return a
}()
}, {
name: "30 minutes average hashrate",
name: "Current hashrate",
data: function() {
var e, a = [];
if (null != t) {
if (null != t)
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].minerHash;
a.push({
r = new Date(1e3 * t[e].x), l = r.toLocaleString(), n = t[e].minerHash, a.push({
x: r,
d: l,
y: n
});
}
} else {
a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
})
} else a.push({
x: 0,
d: 0,
y: 0
});
return a
}()
}]
};
return a;
return a
}
})
}),
roundPercent: Ember.computed('stats', 'model', {
get() {
var percent = this.get('model.roundShares') / this.get('stats.nShares');
if (!percent) {
return 0;
}
return percent;
}
})
});

50
www/app/controllers/account/payouts.js

@ -21,7 +21,7 @@ export default Ember.Controller.extend({
t = e.getWithDefault("model.paymentCharts"),
a = {
chart: {
backgroundColor: "rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.1)",
type: "column",
marginRight: 10,
height: 200,
@ -29,10 +29,10 @@ export default Ember.Controller.extend({
load: function() {
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getDate(),
var x = (new Date).getDate(),
y = e.getWithDefault("model.paymentCharts");
series.addPoint([x, y], true, true);
}, 1090000000);
series.addPoint([x, y], true, true)
}, 1090000000)
}
}
},
@ -60,15 +60,11 @@ export default Ember.Controller.extend({
color: "#808080"
}],
legend: {
enabled: true,
itemStyle:
{
color:"#ccc"
},
enabled: true
},
tooltip: {
formatter: function() {
return "<b>" + Highcharts.dateFormat('%Y-%m-%d', new Date(this.x)) + "<b><br>Payment&nbsp;<b>" + this.y.toFixed(8) + "&nbsp;ETC</b>";
return "<b>" + Highcharts.dateFormat('%Y-%m-%d', new Date(this.x)) + "<b><br>Payment&nbsp;<b>" + this.y.toFixed(8) + "&nbsp;ESN</b>"
},
useHTML: true
},
@ -80,32 +76,28 @@ export default Ember.Controller.extend({
name: "Payment Series",
data: function() {
var e, a = [];
if (null != t) {
if (null != t)
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].amount / 1000000000;
a.push({
x: r,
d: l,
y: n
});
}
} else {
a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
r = new Date(1e3 * t[e].x),
l = r.toLocaleString(),
n = t[e].amount / 1000000000, a.push({
x: r,
d: l,
y: n
})
} else a.push({
x: 0,
d: 0,
y: 0
});
return a
}()
}]
};
return a;
return a
}
})
});

23
www/app/controllers/application.js

@ -2,7 +2,6 @@ import Ember from 'ember';
import config from '../config/environment';
export default Ember.Controller.extend({
intl: Ember.inject.service(),
get config() {
return config.APP;
},
@ -66,26 +65,6 @@ export default Ember.Controller.extend({
}
}),
languages: Ember.computed('model', {
get() {
return this.get('model.languages');
}
}),
selectedLanguage: Ember.computed({
get() {
var langs = this.get('languages');
var lang = Ember.$.cookie('lang');
for (var i = 0; i < langs.length; i++) {
if (langs[i].value == lang) {
return langs[i].name;
}
}
return lang;
}
}),
roundVariance: Ember.computed('model', {
get() {
var percent = this.get('model.stats.roundShares') / this.get('difficulty');
@ -98,7 +77,7 @@ export default Ember.Controller.extend({
nextEpoch: Ember.computed('height', {
get() {
var epochOffset = (30000 - (this.getWithDefault('height', 1) % 30000)) * 1000 * this.get('config').BlockTime;
var epochOffset = (60000 - (this.getWithDefault('height', 1) % 60000)) * 1000 * this.get('config').BlockTime;
return Date.now() + epochOffset;
}
})

41
www/app/controllers/index.js

@ -21,30 +21,24 @@ export default Ember.Controller.extend({
t = e.getWithDefault("stats.model.poolCharts"),
a = {
chart: {
backgroundColor: "rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.1)",
type: "spline",
height: 300,
marginRight: 10,
events: {
load: function() {
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getTime(), y = e.getWithDefault("model.Hashrate") / 1000000;
series.addPoint([x, y], true, true);
}, 1090000000);
setInterval(function() {var x = (new Date).getTime(), y = e.getWithDefault("model.Hashrate") / 1000000; series.addPoint([x, y], true, true)}, 1090000000)
}
}
},
title: {
text: "Our pool's hashrate",
style: {
color: "#ccc"
}
text: "Our pool's hashrate"
},
xAxis: {
labels: {
style: {
color: "#ccc"
color: "#000"
}
},
ordinal: false,
@ -54,27 +48,27 @@ export default Ember.Controller.extend({
title: {
text: "HASHRATE",
style: {
color: "#ccc"
color: "#000"
}
},
min: 0,
labels: {
style: {
color: "#ccc"
color: "#000"
}
}
},
plotLines: [{
value: 0,
width: 1,
color: "#ccc"
color: "#000"
}],
legend: {
enabled: false
},
tooltip: {
formatter: function() {
return this.y > 1000000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000000).toFixed(2) + "&nbsp;TH/s</b>" : this.y > 1000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000).toFixed(2) + "&nbsp;GH/s</b>" : this.y > 1000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000).toFixed(2) + "&nbsp;MH/s</b>" : "<b>" + this.point.d + "<b><br>Hashrate<b>&nbsp;" + this.y.toFixed(2) + "&nbsp;H/s</b>";
return this.y > 1000000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000000).toFixed(2) + "&nbsp;TH/s</b>" : this.y > 1000000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000000).toFixed(2) + "&nbsp;GH/s</b>" : this.y > 1000000 ? "<b>" + this.point.d + "<b><br>Hashrate&nbsp;" + (this.y / 1000000).toFixed(2) + "&nbsp;MH/s</b>" : "<b>" + this.point.d + "<b><br>Hashrate<b>&nbsp;" + this.y.toFixed(2) + "&nbsp;H/s</b>"
},
useHTML: true
},
@ -86,31 +80,26 @@ export default Ember.Controller.extend({
name: "Hashrate",
data: function() {
var e, a = [];
if (null != t) {
if (null != t)
for (e = 0; e <= t.length - 1; e += 1) {
var n = 0,
r = 0,
l = 0;
r = new Date(1e3 * t[e].x);
l = r.toLocaleString();
n = t[e].y; a.push({
r = new Date(1e3 * t[e].x), l = r.toLocaleString(), n = t[e].y, a.push({
x: r,
d: l,
y: n
});
}
} else {
a.push({
})
} else a.push({
x: 0,
d: 0,
y: 0
});
}
return a;
});
return a
}()
}]
};
return a;
return a
}
})
});

5
www/app/helpers/equals.js

@ -0,0 +1,5 @@
import Ember from 'ember';
export default Ember.Helper.helper(function equals(params) {
return params[0] === params[1];
});

8
www/app/helpers/format-difficulty.js

@ -1,8 +0,0 @@
import Ember from 'ember';
export function formatDifficulty(value) {
value = value / 1000000000
return Ember.String.htmlSafe('<span class="label label-success">' + value + 'b</span>');
}
export default Ember.Helper.helper(formatDifficulty);

30
www/app/helpers/worker-colorizer.js

@ -1,30 +0,0 @@
import Ember from 'ember';
export function workerColorizer(value) {
let class_name;
let difference_seconds = (Date.now() / 1000) - value;
if (difference_seconds >= (60 * 15)) {
class_name = "offline-1";
}
if (difference_seconds >= (60 * 17)) {
class_name = "offline-2";
}
if (difference_seconds >= (60 * 20)) {
class_name = "offline-3";
}
if (difference_seconds >= (60 * 25)) {
class_name = "offline-4";
}
if (difference_seconds >= (60 * 28)) {
class_name = "offline-5";
}
return class_name;
}
export default Ember.Helper.helper(workerColorizer);

9
www/app/helpers/worker-earnperday.js

@ -1,9 +0,0 @@
import Ember from 'ember';
import config from '../config/environment';
export function workerEarnperday(hashrates) {
return 24 * 60 * 60 / config.APP.BlockTime * (hashrates[0] / hashrates[1]) * config.APP.BlockReward;
}
export default Ember.Helper.helper(workerEarnperday);

33
www/app/index.html

@ -4,22 +4,37 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Official etc Pool | https://t.me/poolnode</title>
<meta name="description" content="Highly reliable mining pool"/>
<meta name="keywords" content="Mining pool, mining, cryptocurrency"/>
<title>ETC Mining Pool</title>
<meta name="description" content="High profitability ETC mining pool"/>
<meta name="keywords" content="Ethereum, ethereum, Classic, classic, ETC, etc, pool, mining, cryptocurrency"/>
<script src="https://cdn.polyfill.io/v1/polyfill.min.js?features=Intl.~locale.en"></script>
{{content-for "head"}}
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<script src="https://kit.fontawesome.com/20c576d59b.js" crossorigin="anonymous"></script>
<link href="https://fonts.googleapis.com/css?family=Noto+Sans|Roboto" rel="stylesheet">
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/open-social-pool.css">
<link rel="stylesheet" href="{{rootURL}}assets/open-etc-pool.css">
<link rel="shortcut icon" type="image/x-icon" href="{{rootURL}}favicon.png" />
{{content-for "head-footer"}}
</head>
<body class="bg-dark">
<body>
{{content-for "body"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/open-social-pool.js"></script>
<script src="{{rootURL}}assets/open-etc-pool.js"></script>
{{content-for "body-footer"}}
<footer class="footer">
<div class="container">
<div class="text-center">
<div class="row">
<div class="col-md-12" style="text-align: center;">
<p class="text-muted" style="margin:20px 0">&copy; |
Powered by <a href="https://github.com/yuriy0803/open-etc-pool-friends" target="_blank">open-etc-pool-friends</a>
</p>
</div>
</div>
</div>
</div>
</footer>
</body>
</html>

18
www/app/models/block.js

@ -1,5 +1,23 @@
import Ember from 'ember';
// {
// "candidatesTotal": 0,
// "hashrate": 0,
// "immatureTotal": 0,
// "maturedTotal": 11,
// "minersTotal": 0,
// "nodes": [{
// "difficulty": "2735271",
// "height": "63151",
// "lastBeat": "1471098611",
// "name": "jee-test-pool"
// }],
// "now": 1471098614036,
// "stats": {
// "lastBlockFound": 1471052210
// }
// }
var Block = Ember.Object.extend({
variance: Ember.computed('difficulty', 'shares', function() {
var percent = this.get('shares') / this.get('difficulty');

21
www/app/models/chart.js

@ -0,0 +1,21 @@
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
chartdata: [DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number'), DS.attr('number')]
});
// {
// name: DS.attr('string'),
// colorByPoint: DS.attr('boolean'),
// chartdata: [
// {y: DS.attr('number'), name: DS.attr('string')},
// {y: DS.attr('number'), name: DS.attr('string')},
// {y: DS.attr('number'), name: DS.attr('string')}
// ]
// }
// {
// title: DS.attr('string'),
// content: DS.attr('string'),
// author: DS.attr('string')
// }

3
www/app/router.js

@ -18,11 +18,10 @@ Router.map(function() {
});
this.route('help');
//this.route('help-ar');
this.route('payments');
this.route('miners');
this.route('about');
//this.route('about-ar');
this.route('chart');
});
export default Router;

72
www/app/routes/application.js

@ -1,77 +1,12 @@
import Ember from 'ember';
import config from '../config/environment';
function selectLocale(selected) {
// FIXME
let supported = ['en', 'ar-sa', 'en-us'];
const language = navigator.languages[0] || navigator.language || navigator.userLanguage;
let locale = selected;
if (locale == null) {
// default locale
locale = language;
if (supported.indexOf(locale) < 0) {
locale = locale.replace(/\-[a-zA-Z]*$/, '');
}
}
if (supported.indexOf(locale) >= 0) {
if (locale === 'en') {
locale = 'en-us';
}
} else {
locale = 'en-us';
}
return locale;
}
export default Ember.Route.extend({
intl: Ember.inject.service(),
selectedLanguage: null,
languages: null,
beforeModel() {
let locale = this.get('selectedLanguage');
if (!locale) {
// read cookie
locale = Ember.$.cookie('lang');
// pick a locale
locale = selectLocale(locale);
this.get('intl').setLocale(locale);
Ember.$.cookie('lang', locale);
console.log('INFO: locale selected - ' + locale);
this.set('selectedLanguage', locale);
}
let intl = this.get('intl');
this.set('languages', [
{ name: intl.t('lang.arabic'), value: 'ar-sa'},
{ name: intl.t('lang.english'), value: 'en-us'}
]);
},
actions: {
selectLanguage: function(lang) {
let selected = lang;
if (typeof selected === 'undefined') {
return true;
}
let locale = selectLocale(selected);
this.get('intl').setLocale(locale);
this.set('selectedLanguage', locale);
Ember.$.cookie('lang', locale);
let languages = this.get('languages');
for (var i = 0; i < languages.length; i++) {
if (languages[i].value == locale) {
Ember.$('#selectedLanguage').html(languages[i].name + '<b class="caret"></b>');
break;
}
}
return true;
}
},
beforeModel() {
this.get('intl').setLocale('en-us');
},
model: function() {
var url = config.APP.ApiUrl + 'api/stats';
@ -83,6 +18,5 @@ export default Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller, model);
Ember.run.later(this, this.refresh, 5000);
model.languages = this.get('languages');
}
});

7
www/app/routes/chart.js

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('chart');
}
});

8
www/app/serializers/chart.js

@ -0,0 +1,8 @@
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
primaryKey: '_id',
serializeId: function(id) {
return id.toString();
}
});

355
www/app/styles/app.css

@ -5,13 +5,22 @@ html {
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 60px;
background: url('/bg.png');
}
caption, th {
text-align: inherit;
.footer {
position: absolute;
bottom: 0;
width: 100%;
/* Set the fixed height of the footer here */
height: 60px;
background-color: #f5f5f5;
}
.center {
align-items: center;
display: flex;
}
/* Custom page CSS
-------------------------------------------------- */
@ -19,16 +28,12 @@ caption, th {
body {
padding-top: 20px;
padding-bottom: 0px;
color: #ddd;
padding-bottom: 5px;
}
body > .container {
padding: 0px 15px 0;
}
.container .text-muted {
margin: 20px 0;
}
/* doesn't work -------- */
#coin_calculator iframe form{
@ -70,9 +75,14 @@ h1, h2, h3, h4, h5, h6 {
color:#333;
}
.space-medium {
padding-top: 40px;
padding-bottom: 40px;
.container .text-muted {
margin: 20px 0;
}
.footer > .container {
padding-right: 15px;
padding-left: 15px;
}
.jumbotron {
@ -81,53 +91,64 @@ h1, h2, h3, h4, h5, h6 {
margin-bottom: 15px;
}
.jumbotron-brand {
margin: 0;
padding: 40px 0 15px 0;
margin-bottom: 15px;
background-color: #e9ffee;
}
code {
font-size: 80%;
}
.hash {
font-family: Courier, monospace;
}
.navbar-default {
background-color: #060820;
border-color: transparent;
background-color: #ffffff;
border-color: #61BD81;
border-bottom-width: 2px;
}
.navbar-default .navbar-brand {
color: #dadada;
color: #4A4A4A;
}
.navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus {
color: #ffffff;
color: #4A4A4A;
}
.navbar-default .navbar-text {
color: #dadada;
color: #4A4A4A;
}
.navbar-default .navbar-nav > li > a {
color: #dadada;
color: #4A4A4A;
}
.navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus {
color: #ffffff;
color: #61BD81;
}
.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus {
color: #ffffff;
background-color: #0a6c9d;
background-color: #61BD81;
}
.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
color: #ffffff;
background-color: #69102b;
}
.navbar-default .navbar-toggle {
border-color: #0a6c9d;
border-color: #2885da;
}
.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus {
background-color: #0a6c9d;
background-color: #2885da;
}
.navbar-default .navbar-toggle .icon-bar {
background-color: #dadada;
background-color: #4A4A4A;
}
.navbar-default .navbar-collapse,
.navbar-default .navbar-form {
border-color: #00ff58;
border-color: #4A4A4A;
}
.navbar-default .navbar-link {
color: #dadada;
color: #4A4A4A;
}
.navbar-default .navbar-link:hover {
color: #ffffff;
@ -135,54 +156,84 @@ code {
@media (max-width: 767px) {
.navbar-default .navbar-nav .open .dropdown-menu > li > a {
color: #dadada;
color: #4A4A4A;
}
.navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
color: #ffffff;
}
.navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #ffffff;
background-color: #0a6c9d;
background-color: #3f9ff7;
}
}
span.logo-1 {
font-weight: 700;
color: #53ad2d;
font-style: italic;
color: #61BD81;
padding: 0 7px 0 23px;
}
span.logo-2 {
font-weight: 500;
font-size:smaller;
color: #FFF;
font-weight: 700;
color: #4A4A4A;
padding: 0 23px 0 5px;
}
span.logo-3 {
color: #FFF;
font-weight: 100;
padding: 0 3px 0 3px;
}
span.logo-gold {
color: #f2cf18;
font-weight: 700;
padding: 0 3px 0 3px;
}
.etc {
height: 30px;
width: 30px;
pointer-events: none;
}
.etc-green > object{
float: left;
}
.etc-green > strong{
float: left;
}
/* That time i figured out how to do that thing to scale svg
.etc-green {
transform: scale(0.4);
transform: translate(-200px, -50px);
}
*/
.navbar-collapse {
font-size: 14px;
font-weight: 200;
background-color: rgb(0, 12, 34);
}
.note {
margin: 0 0 20px 0;
padding: 15px 30px 15px 15px;
border-left: 5px solid #eee;
border-radius: 15px;
border-radius: 5px;
}
.note-info {
background-color: #E8F6FC;
border-color: #57b5e3;
color: #333333;
}
.note-danger {
background-color: #ff9999;
background-color: #fce8e8;
border-color: #ff0000;
}
@ -190,175 +241,171 @@ h4.note {
margin-top: 0;
font-weight: 300 !important;
}
/*---------------------------------------------------------------------------------------------------*/
/*----------------------------Bootstrap side notes for calling out things----------------------------*/
/*---------------------------------------------------------------------------------------------------*/
.hash {
font-family: 'Inconsolata', monospace;
/* Base styles (regardless of theme) */
.bs-callout {
padding: 20px;
margin: 20px 0;
border: 1px solid #eee;
border-left-width: 5px;
border-radius: 3px;
}
/* Stats */
.stats-box {
padding: 24px 30px;
background: #0e102f;
border-radius: 15px;
margin-bottom: 6px;
.bs-callout h4 {
margin-top: 0;
margin-bottom: 5px;
}
.stats-box > h3 > i {
width: 21px;
.bs-callout p:last-child {
margin-bottom: 0;
}
.stats-box > div > .fa {
width: 25px;
.bs-callout code {
border-radius: 3px;
}
.stats-box > div > span:first-of-type{
font-weight: bold;
.bs-callout+.bs-callout {
margin-top: -5px;
}
/* Themes for different contexts */
.bg-dark {
background-image: linear-gradient(to bottom, #0b0c22 0%, #0e122f 70%);
/* Default */
.bs-callout-default {
background-color: #eeeeee;
border-left-color: #777;
}
.worker-class.warning.offline-1 > td{
background-color: #ffd1d1;
.bs-callout-default h4 {
color: #777;
}
.worker-class.warning.offline-2 > td{
background-color: #f99393;
/* Primary */
.bs-callout-primary {
background-color: #e8effc;
border-left-color: #428bca;
}
.worker-class.warning.offline-3 > td{
background-color: #ff5959;
color:#ffffff;
.bs-callout-primary h4 {
color: #428bca;
}
.worker-class.warning.offline-4 > td{
background-color: #ff3e3e;
color:#ffffff;
/* Success */
.bs-callout-success {
background-color: #eafce8;
border-left-color: #5cb85c;
}
.worker-class.warning.offline-5 > td{
background-color: #ff1e1e;
color:#ffffff;
.bs-callout-success h4 {
color: #5cb85c;
}
.jumbotron{
background: transparent;
color:#bbbbbb;
/* Danger */
.bs-callout-danger {
background-color: #fce8e8;
border-left-color: #d9534f;
}
.jumbotron small{
color:#919191;
margin-left:30px;
.bs-callout-danger h4 {
color: #d9534f;
}
.alert{
border-radius:20px;
/* Warning */
.bs-callout-warning {
background-color: #fcfbe8;
border-left-color: #f0ad4e;
}
.nav-tabs{
margin-bottom:20px;
}
.nav-tabs>li>a {
border-radius: 15px 15px 0 0;
.bs-callout-warning h4 {
color: #f0ad4e;
}
a {
color: #5ea2ff;
/* Info */
.bs-callout-info {
background-color: #E8F6FC;
border-left-color: #5bc0de;
}
.bs-callout-info h4 {
color: #5bc0de;
}
.table-striped>tbody>tr:nth-of-type(odd) {
background-color: rgba(0,0,0,0.5);
/*---------------------------------------------------------------------------------------------------*/
/*----------------------------x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x----------------------------*/
/*---------------------------------------------------------------------------------------------------*/
/* Stats */
.stats {
margin-bottom: 10px;
margin-top: 5px;
}
.table {
margin-bottom: 0px;
.stats:last-child{
width: auto;
}
.table-responsive {
color: #444;
background-color: rgba(255,255,255,0.9);
padding: 5px 5px 0px;
border-radius: 20px 20px 0px 0px;
margin-bottom:20px;
.stats > h3 > i {
width: 21px;
}
.table-responsive a {
color: #004bb1;
.stats > div{
padding: 5px 0;
}
.table-striped>tbody>tr:nth-of-type(odd) {
background-color: rgba(0,0,0,.2);
.stats > div > .fa {
width: 25px;
}
.highcharts-wrapper{
margin-bottom:20px;
.stats > div > span:first-of-type{
font-weight: bold;
}
/*--------------------------------------| Bootstrap overrides |--------------------------------------*/
.command_lines{
margin: 15px 0;
.ul, .ul-link {
font-weight: 200;
text-align: center;
}
.command_lines ul{
margin-bottom:0px;
.ul-link:hover, .ul-link:focus {
color: inherit;
text-decoration: none;
opacity: .70;
}
.command_lines h5{
color: #333;
}
.command_lines .tab-content {
background-color: #E8F6FC;
border-left: 5px solid #eee;
border-color: #57b5e3;
color: #ddd;
border-radius: 0 0 15px 15px;
.ul-default {
color: #777;
border-bottom: 2px solid #777;
}
.command_lines .nav-tabs {
border-bottom: 0;
.ul-primary {
color: #428bca;
border-bottom: 2px solid #428bca;
}
.command_lines .nav-tabs>li.active>a,
.command_lines .nav-tabs>li.active>a:focus,
.command_lines .nav-tabs>li.active>a:hover {
border-left: 5px solid #eee;
border-color: #57b5e3;
border-bottom: 0px;
.ul-success {
color: #5cb85c;
border-bottom: 2px solid #5cb85c;
}
.command_lines .panel-group .panel+.panel {
margin-top: 0px;
.ul-danger {
color: #d9534f;
border-bottom: 2px solid #d9534f;
}
.command_lines .panel-group .panel {
margin-bottom: 0;
border-radius: 0;
.ul-warning {
color: #f0ad4e;
border-bottom: 2px solid #f0ad4e;
}
.command_lines .panel-default>.panel-heading {
color: #dddddd;
background-color: #0e102f;
border: 0px;
.ul-info {
color: #5bc0de;
border-bottom: 2px solid #5bc0de;
}
.command_lines .panel-default {
border: 0px;
.ul-etc {
color: #61BD81;
border-bottom: 2px solid #61BD81;
}
.join_telegram{
border-radius: 5px;
font-size: x-large;
}
/*-----------------------------------------| Custom Classes |-----------------------------------------*/
.btn-etc {
background-color: #61BD81;
border-color: #408c5a;
}
.footer { background: #020207;}
.footer-section { text-align: center; }
.footer-title { margin-bottom: 40px; }
.footer > .container {
padding-right: 15px;
padding-left: 15px;
.btn-etc:hover {
color: #fff;
background-color: #428359;
border-color: #2c623e;
}
/*--------------------------------------| x-x-x-x-x-x-x-x-x-x |--------------------------------------*/

55
www/app/templates/account.hbs

@ -1,75 +1,60 @@
{{outlet 'error' }}
<div class="jumbotron">
<div class="jumbotron-brand">
<div class="container">
<div class="row">
<div class="col-md-4 stats">
<div class="stats-box">
<div style="display: block;">
<i class="fa fa-cloud"></i> {{t "account.immature.balance"}}: <span>{{format-balance model.stats.immature}} {{config.Unit}}</span><br>
<small>{{t "account.immature.description"}}</small>
<i class="fa fa-cloud"></i> Immature Balance: <span>{{format-balance model.stats.immature}}</span><br>
<small>Preliminary balance awaiting blocks to mature.</small>
</div>
<div style="display: block;">
<i class="fa fa-bank"></i> {{t "account.pending.balance"}}: <span>{{format-balance model.stats.balance}} {{config.Unit}}</span><br>
<small>{{t "account.pending.description"}}</small>
<i class="fa fa-bank"></i> Pending Balance: <span>{{format-balance model.stats.balance}}</span><br>
<small>Credited coins awaiting payout.</small>
</div>
{{#if model.stats.pending}}
<div style="display: block;">
<i class="fa fa-clock-o"></i> {{t "account.current"}}: <span>{{format-balance model.stats.pending}} {{config.Unit}}</span><br>
<i class="fa fa-clock-o"></i> Current Payment: <span>{{format-balance model.stats.pending}}</span><br>
</div>
{{/if}}
<div style="display: block;"><i class="fa fa-btc"></i> {{t "account.total.paid"}}: <span>{{format-balance model.stats.paid}} {{config.Unit}}</span></div>
<div style="display: block;"><i class="fa fa-money"></i> Total Paid: <span>{{format-balance model.stats.paid}}</span></div>
<div style="display: block;"><i class="fa fa-money"></i> Last 24h Reward: <span>{{format-balance model.24hreward}}</span></div>
</div>
</div>
<div class="col-md-4 stats">
<div class="stats-box">
{{#if model.stats.lastShare}}
<div style="display: block;"><i class="fa fa-hourglass-half"></i>
{{t "account.last_share_submitted"}}: <span>{{format-relative (seconds-to-ms (string-to-int model.stats.lastShare))}}</span>
<div style="display: block;"><i class="fa fa-clock-o"></i>
Last Share Submitted: <span>{{format-relative (seconds-to-ms (string-to-int model.stats.lastShare))}}</span>
</div>
{{/if}}
<div style="display: block;"><i class="fa fa-cogs"></i> {{t "account.online"}}: <span>{{format-number model.workersOnline}}</span></div>
<div style="display: block;"><i class="fa fa-tachometer"></i> {{t "account.hashrate"}} ({{t "account.short_average_abbrv"}}): <span>{{format-hashrate model.currentHashrate}}</span></div>
<div style="display: block;"><i class="fa fa-tachometer"></i> {{t "account.hashrate"}} ({{t "account.long_average_abbrv"}}): <span>{{format-hashrate model.hashrate}}</span></div>
<div style="display: block;"><i class="fa fa-calculator"></i> {{t "account.earnings.miner"}}: <span>{{format-number earnPerDay}} {{config.Unit}}</span></div>
<div style="display: block;"><i class="fa fa-gears"></i> Workers Online: <span>{{format-number model.workersOnline}}</span></div>
<div style="display: block;"><i class="fa fa-tachometer"></i> Hashrate (30m): <span>{{format-hashrate model.currentHashrate}}</span></div>
<div style="display: block;"><i class="fa fa-tachometer"></i> Hashrate (3h): <span>{{format-hashrate model.hashrate}}</span></div>
</div>
</div>
<div class="col-md-4 stats">
<div class="stats-box">
<div style="display: block;"><i class="fa fa-tachometer"></i> {{t "account.blocks.found"}}: <span>{{format-number model.stats.blocksFound fallback='0'}}</span></div>
<div style="display: block;"><i class="fa fa-paper-plane-o"></i> {{t "account.total.payments"}}: <span>{{format-number model.paymentsTotal}}</span></div>
<div style="display: block;"><i class="fa fa-tachometer"></i> Blocks Found: <span>{{format-number model.stats.blocksFound fallback='0'}}</span></div>
<div style="display: block;"><i class="fa fa-paper-plane-o"></i> Total Payments: <span>{{format-number model.paymentsTotal}}</span></div>
<div style="display: block;">
<i class="fa fa-percent"></i> {{t "account.round_share"}}: <span>{{format-number roundPercent style='percent' maximumFractionDigits='6'}}</span><br/>
<small>{{t "account.round_share_description"}}</small>
<i class="fa fa-gears"></i> Your Round Share: <span>{{format-number roundPercent style='percent' maximumFractionDigits='6'}}</span><br/>
<small>Percent of your contribution to current round.</small>
</div>
<div style="display: block;">
<i class="fa fa-hourglass"></i>
{{t "account.epoch_switch"}}: <span>{{format-relative applicationController.nextEpoch units="hour"}}</span>
<i class="fa fa-clock-o"></i>
Epoch Switch: <span>{{format-relative applicationController.nextEpoch units="hour"}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container">
<ul class="nav nav-tabs">
{{#active-li currentWhen='account.index' role='presentation'}}
{{#link-to 'account.index'}}{{t "account.workers"}} <span class="badge alert-danger">{{model.workersOffline}}</span>{{/link-to}}
{{#link-to 'account.index'}}Workers <span class="badge alert-danger">{{model.workersOffline}}</span>{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='account.rewards' role='presentation'}}
{{#link-to 'account.rewards'}}Rewards{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='account.payouts' role='presentation'}}
{{#link-to 'account.payouts'}}{{t "account.payouts"}}{{/link-to}}
{{#link-to 'account.payouts'}}Payouts{{/link-to}}
{{/active-li}}
</ul>
</div>

31
www/app/templates/account/index.hbs

@ -1,29 +1,23 @@
<div class="container">
{{high-charts mode=chartMode chartOptions=chartOptions content=chartData}}
{{#if model.workers}}
<h4>{{t "account.your_workers"}}</h4>
<h4>Your Workers</h4>
<div class="table-responsive">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>ID</th>
<th>{{t "account.hashrate"}} ({{t "account.short_average_abbrv"}} / {{t "account.short_average_abbrv"}})</th>
<th>{{t "account.earnings.worker"}}</th>
<th>{{t "account.difficulty"}}</th>
<th>{{t "account.server"}}</th>
<th>{{t "account.valid"}}</th>
<th>{{t "account.last_share"}}</th>
<th>Hashrate (rough, short average)</th>
<th>Hashrate (accurate, long average)</th>
<th>Last Share</th>
</tr>
</thead>
<tbody>
{{#each-in model.workers as |k v|}}
<tr class="worker-class {{if v.offline "warning" "success"}} {{worker-colorizer v.lastBeat}}">
<tr class="{{if v.offline "warning" "success"}}">
<td>{{k}}</td>
<td>{{format-hashrate v.hr}} / {{format-hashrate v.hr2}}</td>
<td>{{format-number (worker-earnperday v.hr netstats.hashrate)}} {{config.Unit}}</td>
<td>{{format-difficulty v.difficulty}}</td>
<td>{{v.hostname}}</td>
<td>{{v.valid}}</td>
<td>{{format-hashrate v.hr}}</td>
<td>{{format-hashrate v.hr2}}</td>
<td>{{format-relative (seconds-to-ms v.lastBeat)}}</td>
</tr>
{{/each-in}}
@ -31,13 +25,16 @@
</table>
</div>
{{else}}
<h3>{{t "account.no_workers_online"}}</h3>
<h3>No workers online</h3>
{{/if}}
<div class="alert alert-info" role="alert">
<span class="sr-only">{{t "account.notice"}}:</span>
{{format-html-message "account.notice_html"}}
<span class="sr-only">Notice:</span>
Your average hashrate will be smoothly adjusted until you have shares to fullfill estimation window.<br/>
There are two windows, long and short, first is equal to about 30 minutes and long window is usually equal to 3 hours.<br/>
Dead (sick) workers will be highlighted in a table of workers if they didn't submit a share for 1/2 of short window,
so you can perform maintenance of your rigs.
</div>
<div class="alert alert-info" role="alert">
<strong>{{t "account.json_api_url"}}:</strong> <a href="{{config.ApiUrl}}api/accounts/{{model.login}}">/api/accounts/{{model.login}}</a>
<strong>Your bulk stats JSON API URL:</strong> <a href="/api/accounts/{{model.login}}">/api/accounts/{{model.login}}</a>
</div>
</div>

14
www/app/templates/account/payouts.hbs

@ -1,14 +1,14 @@
<div class="container">
{{high-charts mode=chartMode chartOptions=chartPayment content=chartData}}
{{high-charts mode=chartMode chartOptions=chartPayment content=chartData}}
{{#if model.payments}}
<h3>{{t "payout.latest_payouts"}}</h3>
<h4>Your Latest Payouts</h4>
<div class="table-responsive">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{{t "payout.time"}}</th>
<th>{{t "payout.txid"}}</th>
<th>{{t "payout.amount"}} {{config.Unit}}</th>
<th>Time</th>
<th>Tx ID</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
@ -16,7 +16,7 @@
<tr>
<td>{{format-date-locale tx.timestamp}}</td>
<td>
<a href="{{t "links.blockExplorerLink_tx"}}{{tx.tx}}" class="hash" rel="nofollow" target="_blank">{{tx.tx}}</a>
<a href="https://expedition.dev/tx/{{tx.tx}}" class="hash" rel="nofollow" target="_blank">{{tx.tx}}</a>
</td>
<td>{{format-balance tx.amount}}</td>
</tr>
@ -25,6 +25,6 @@
</table>
</div>
{{else}}
<h3>{{t "payout.no_payouts_yet"}}</h3>
<h3>No payouts yet</h3>
{{/if}}
</div>

2
www/app/templates/account/rewards.hbs

@ -14,6 +14,7 @@
<thead>
<tr>
<th>Block Height</th>
<th>Time Found</th>
<th>Reward</th>
<th>Round Share</th>
</tr>
@ -22,6 +23,7 @@
{{#each model.rewards as |tx|}}
<tr>
<td>{{format-number tx.blockheight}}</td>
<td>{{format-date-locale tx.timestamp}}</td>
<td>
{{#if tx.immature}}
<span class="label label-default">{{format-balance tx.reward}}</span>

4
www/app/templates/application-error.hbs

@ -1,6 +1,6 @@
<div class="container">
<div class="page-header">
<h1>{{t "error.heading"}}</h1>
<p>{{t "error.message"}}</p>
<h1>Stats API Temporarily Down</h1>
<p>Usually it's just a temporal issue and mining is not affected.</p>
</div>
</div>

91
www/app/templates/application.hbs

@ -1,95 +1,54 @@
<!-- Fixed navbar -->
<!-- Modal -->
{{!-- <div class="modal fade" id="coin_calculator" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">{{t "calculator.title"}}</h4>
</div>
<div class="modal-body text-center">
<iframe src=https://www.coincalculators.io/widget.aspx?crypto=perkle height ="270" width="325" frameBorder="0" style="overflow:hidden;" scrolling="no" ></iframe>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger btn-block" data-dismiss="modal">{{t "calculator.close"}}</button>
</div>
</div>
</div>
</div> --}}
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{config.HttpHost}}"><span class="logo-1">{{config.PoolName}}</span><span class="logo-2"> @ {{config.CompanyName}}</span></a>
<a class="navbar-brand center" href="/">
<object type="image/svg+xml" data="etc.svg" class="etc"></object></a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav text-center pull-right">
{{!-- <li>
<a href="#" data-toggle="modal" data-target="#coin_calculator"> <i class="fa fa-calculator"></i> {{t "menu.calculator"}}</a>
</li> --}}
{{#active-li currentWhen='blocks'}}
<ul class="nav navbar-nav">
{{#active-li currentWhen='index' }}
{{#link-to 'index'}}
<i class="fa fa-home"></i> Home
{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='blocks' }}
{{#link-to 'blocks'}}
<i class="fa fa-cubes"></i> {{t 'menu.pool_blocks'}}
<i class="fa fa-cubes"></i> Pool Blocks
{{#if immatureTotal}}
<span class="badge alert-success">{{immatureTotal}}</span>
<span class="ul">{{immatureTotal}}</span>
{{/if}}
{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='payments'}}
{{#active-li currentWhen='payments' }}
{{#link-to 'payments'}}
<i class="fa fa-paper-plane-o"></i> {{t 'menu.payments'}}
<i class="fa fa-paper-plane"></i> Payments
{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='miners'}}
{{#active-li currentWhen='miners' }}
{{#link-to 'miners'}}
<i class="fa fa-users"></i> {{t 'menu.miners'}}
<i class="fa fa-users"></i> Miners
{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='about'}}
{{#link-to (t 'menu.i18n.about')}}
<i class="fa fa-comments"></i> {{t 'menu.about'}}
{{#active-li currentWhen='help' }}
{{#link-to 'help'}}
<i class="fa fa-rocket"></i> Help
{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='help'}}
{{#link-to (t 'menu.i18n.help')}}
<i class="fa fa-rocket"></i> {{t 'menu.help'}}
{{#active-li currentWhen='about' }}
{{#link-to 'about'}}
<i class="fa fa-comments"></i> About
{{/link-to}}
{{/active-li}}
<li>
<ul class="nav navbar-nav navbar-right">
<li>
<a class="nav-item nav-link dropdown-toggle" data-toggle="dropdown" id="selectedLanguage">
{{selectedLanguage}}
<b class="caret"></b>
</a>
<ul class="dropdown-menu dropdown-menu-right">
{{#each languages as |lang|}}
<li><a href="#" {{action 'selectLanguage' lang.value on='click'}}>{{lang.name}}</a></li>
{{/each}}
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div dir="{{t "general.direction"}}">
{{outlet}}
</div>
<footer class="footer">
<div class="container">
<p class="text-muted text-center">{{format-html-message "footer.copyrights"}}</p>
</div>
</footer>

19
www/app/templates/blocks.hbs

@ -1,24 +1,27 @@
<div class="jumbotron">
<div class="jumbotron-brand">
<div class="container">
<p class="lead">{{t "block.pool_rewards"}}</p>
<span>
{{format-html-message "block.pool_notice.html" success=520}}
</span>
<p class="lead">Pool always pays full block rewards including TX fees and uncle rewards.</p>
<strong>
Block maturity requires <u>up to</u> </strong> <span class="label label-success">520</span> <strong>blocks.
Usually it's less indeed.
</strong>
</div>
</div>
<div class="container">
{{#if model.luck}}
{{partial "luck"}}
{{/if}}
<ul class="nav nav-tabs">
{{#active-li currentWhen='blocks.index' role='presentation'}}
{{#link-to 'blocks.index'}}{{t "block.blocks"}} <span class="badge alert-success">{{format-number model.maturedTotal}}</span>{{/link-to}}
{{#link-to 'blocks.index'}}Blocks <span class="badge alert-success">{{format-number model.maturedTotal}}</span>{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='blocks.immature' role='presentation'}}
{{#link-to 'blocks.immature'}}{{t "block.immature"}} <span class="badge alert-success">{{format-number model.immatureTotal}}</span>{{/link-to}}
{{#link-to 'blocks.immature'}}Immature <span class="badge alert-success">{{format-number model.immatureTotal}}</span>{{/link-to}}
{{/active-li}}
{{#active-li currentWhen='blocks.pending' role='presentation'}}
{{#link-to 'blocks.pending'}}{{t "block.new"}} <span class="badge alert-info">{{format-number model.candidatesTotal}}</span>{{/link-to}}
{{#link-to 'blocks.pending'}}New Blocks <span class="badge alert-info">{{format-number model.candidatesTotal}}</span>{{/link-to}}
{{/active-li}}
</ul>
{{outlet}}

17
www/app/templates/blocks/block.hbs

@ -1,18 +1,18 @@
<tr>
<td>
{{#if block.uncle}}
<a href="{{t "links.blockExplorerLink_uncle"}}{{block.height}}" rel="nofollow" target="_blank">{{format-number block.height}}</a>
<a href="https://expedition.dev/uncle/{{block.uncleHeight}}" rel="nofollow" target="_blank">{{format-number block.height}}</a>
{{else}}
<a href="{{t "links.blockExplorerLink_block"}}{{block.height}}" rel="nofollow" target="_blank">{{format-number block.height}}</a>
<a href="https://expedition.dev/block/{{block.height}}" rel="nofollow" target="_blank">{{format-number block.height}}</a>
{{/if}}
</td>
<td>
{{#if block.uncle}}
<a href="{{t "links.blockExplorerLink_uncle"}}{{block.hash}}" class="hash" rel="nofollow" target="_blank">{{block.hash}}</a>
<a href="https://expedition.dev/uncle/{{block.hash}}" class="hash" rel="nofollow" target="_blank">{{block.hash}}</a>
{{else if block.orphan}}
<span class="label label-danger">{{t "block.orphan"}}</span>
<span class="label label-danger">Orphan</span>
{{else}}
<a href="{{t "links.blockExplorerLink_block"}}{{block.hash}}" class="hash" rel="nofollow" target="_blank">{{block.hash}}</a>
<a href="https://expedition.dev/block/{{block.hash}}" class="hash" rel="nofollow" target="_blank">{{block.hash}}</a>
{{/if}}
</td>
<td>{{format-date-locale block.timestamp}}</td>
@ -30,4 +30,11 @@
<span class="label label-primary">{{block.formatReward}}</span>
{{/if}}
</td>
<td>
{{#if block.uncle}}
<span class="label label-default">Uncle</span>
{{else if block.isOk}}
<span class="label label-primary">Block</span>
{{/if}}
</td>
</tr>

40
www/app/templates/blocks/immature.hbs

@ -1,24 +1,22 @@
{{#if model.immature}}
<h3>{{t "block.immature_blocks"}}</h3>
<div class="table-responsive">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{{t "block.height"}}</th>
<th>{{t "block.hash"}}</th>
<th>{{t "block.time_found"}}</th>
<th>{{t "block.variance"}}</th>
<th>{{t "block.reward"}}</th>
</tr>
</thead>
<tbody>
{{#each model.immature as |block|}}
{{partial "blocks/block"}}
{{/each}}
</tbody>
</table>
</div>
<h4>Immature Blocks</h4>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Height</th>
<th>Block Hash</th>
<th>Time Found</th>
<th>Variance</th>
<th>Reward</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{{#each model.immature as |block|}}
{{partial "blocks/block"}}
{{/each}}
</tbody>
</table>
{{else}}
<h3>{{t "block.no_immature_blocks_yet"}}</h3>
<h3>No immature blocks yet</h3>
{{/if}}

39
www/app/templates/blocks/index.hbs

@ -1,23 +1,22 @@
{{#if model.matured}}
<h3>{{t "block.matured"}}</h3>
<div class="table-responsive">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{{t "block.height"}}</th>
<th>{{t "block.hash"}}</th>
<th>{{t "block.time_found"}}</th>
<th>{{t "block.variance"}}</th>
<th>{{t "block.reward"}}</th>
</tr>
</thead>
<tbody>
{{#each model.matured as |block|}}
{{partial "blocks/block"}}
{{/each}}
</tbody>
</table>
</div>
<h4>Matured Blocks</h4>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Height</th>
<th>Block Hash</th>
<th>Time Found</th>
<th>Variance</th>
<th>Reward</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{{#each model.matured as |block|}}
{{partial "blocks/block"}}
{{/each}}
</tbody>
</table>
{{else}}
<h3>{{t "block.no_matured_blocks_yet"}}</h3>
<h3>No matured blocks yet</h3>
{{/if}}

12
www/app/templates/blocks/pending.hbs

@ -1,18 +1,18 @@
{{#if model.candidates}}
<h3>{{t "block.recently_found_blocks"}}</h3>
<h4>Recently Found Blocks</h4>
<div class="table-responsive">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{{t "block.height"}}</th>
<th>{{t "block.time_found"}}</th>
<th>{{t "block.variance"}}</th>
<th>Height</th>
<th>Time Found</th>
<th>Variance</th>
</tr>
</thead>
<tbody>
{{#each model.candidates as |block|}}
<tr>
<td><a href="{{t "links.blockExplorerLink_block"}}{{block.height}}" rel="nofollow" target="_blank">{{format-number block.height}}</a></td>
<td><a href="https://expedition.dev/block/{{block.height}}" rel="nofollow" target="_blank">{{format-number block.height}}</a></td>
<td>{{format-date-locale block.timestamp}}</td>
<td>
{{#if block.isLucky}}
@ -27,5 +27,5 @@
</table>
</div>
{{else}}
<h3>{{t "block.no_new_blocks_yet"}}</h3>
<h3>No new blocks yet</h3>
{{/if}}

3
www/app/templates/components/chart-diff.hbs

@ -0,0 +1,3 @@
{{!-- {{log "COMPONENT TEST SUMMARYDATA"}}
{{log summaryData summaryOptions}} --}}
{{high-charts chartOptions=summaryOptions content=summaryData}}

131
www/app/templates/help.hbs

@ -1,95 +1,62 @@
<div class="container">
<div class="page-header">
<h2>Getting Started with {{config.PoolName}} mining</h2>
<h1>How To Mine</h1>
</div>
<div class="stats-box">
<h3>GPU Miner Softwares</h3>
<p>Download one of the following GPU miners.
<ul>
<li>Claymore Dual Miner (AMD/NVIDIA) : <a href="https://bitcointalk.org/index.php?topic=1433925.0">[ANN] Bitcointalk.org</a></li>
<li>Ethminer (AMD/NVIDIA): <a href="https://github.com/ethereum-mining/ethminer/releasesi/latest">Ethminer source/binary at Github</a></li>
</ul>
</p>
</div>
<div class="col-md-12" style="border-bottom: 1px solid #eee; padding-bottom: 15px; margin-bottom: 15px;">
<div class="col-md-6">
<div class="page-header">
<h3>lolMiner</h3>
</div>
<p class="lead">In order to mine etchash you need
<a href="https://github.com/Lolliedieb/lolMiner-releases/releases" rel="nofollow" target="_blank">lolMiner</a> v1.12+
</p>
<p><code>./lolMiner --pool {{config.StratumHost}}:{{config.StratumPort}} --user [YOUR_ETC_ADDRESS] -c ETC {{if (equals config.Network 'mordor') "--ecip1099-activation 84"}} --ethstratum=ETHPROXY</code></p>
<dl class="dl-horizontal">
<dt>YOUR_ETC_ADDRESS</dt>
<dd>This is your address for payouts, generate one with core-geth, or mine directly to exchange like
<a href="http://bittrex.com" rel="nofollow" target="_blank">Bittrex</a>.<br/>
<strong>Example:</strong> <code>0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6</code>.
</dd>
</dl>
<p>
<strong>Full example:</strong>
<code>./lolMiner --pool {{config.StratumHost}}:{{config.StratumPort}} --user 0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6 -c ETC {{if (equals config.Network 'mordor') "--ecip1099-activation 84"}} --ethstratum=ETHPROXY</code>.<br/>
</p>
</div>
<div class="stats-box">
<h3>Prepare Wallet Address</h3>
<div>
<h4>Using geth</h4>
<ul>
<li>Unix/Linux shell <code>$ geth account new</code></li>
<li>Windows command line using cmd.exe (command shell) <code>&gt; geth account new</code></li>
</ul>
<div class="col-md-6">
<div class="page-header">
<h3>nanominer <small>mainnet only</small> </h3>
</div>
<p class="lead">In order to mine etchash you need
<a href="https://github.com/nanopool/nanominer/releases" rel="nofollow" target="_blank">nanominer</a> v1.12.0+
</p>
<h4>example config</h4>
<pre>
; Address to send funds to. Change this address to yours!
wallet = 0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6
<h4>Using online wallet</h4>
<ul>
<li>{{format-html-message "wallet.online_html"}}</li>
</ul>
; Coin to mine.
coin = ETC
<h4>Wallet Dapp (aka. Mist)</h4>
<ul>
<li>Wallet dapp: {{format-html-message "wallet.dapp_html"}}</li>
</ul>
pool1={{config.StratumHost}}:{{config.StratumPort}}
</pre>
</div>
</div>
<h3>Usage examples</h3>
<dl class="dl-horizontal">
<dt><em>0x0000000000000000000000000000000000000000</em></dt>
<dd>This is your address for payouts<br/>
<strong>Example:</strong> <code>0x8b92c50e1c39466f900a578edb20a49356c4fe24</code>.
</dd>
<dt><em>your-worker-1</em><br /><em>your-worker-2</em></dt>
<dd>
ID of your PC/mining-rig to distinguish it from your other rigs. If you have just one rig, feel free to omit this param.<br />
This param must be short alphanumeric string with optional dashes and underscores.<br/>
<strong>Example:</strong> <code>worker-1</code>
</dd>
</dl>
<h4>Claymore Example</h4>
<p><strong>Download Claymore Dual Miner (AMD/NVIDIA)</strong>: <a href="https://bitcointalk.org/index.php?topic=1433925.0">[ANN] Bitcointalk.org</a></p>
<p>
<code><em>EthDcrMiner64</em> -epool <em>{{config.StratumHost}}</em>:<em>{{config.StratumPort}}</em> <em>-esm 0</em> -ewal <em>0x0000000000000000000000000000000000000000</em> -eworker <em>your-worker-1</em> -allcoins 1 -allpools 1</code>
<div class="col-md-12">
<div class="col-md-6">
<p class="text-justify">
<h4>Advice</h4>
<p>CPU mining is not recommended.</p>
<h4>Terms of Service</h4>
<p>By using the pool you accept all possible risks related to experimental software usage.<br/>
Pool owner can't compensate any irreversible losses, but will do his best to prevent worst case.
</p>
</p>
<ul>
<li><code><em>EthDcrMiner64</em></code> - executable name under Windows. use <code><em>./ethdcrminer64</em></code> under Linux/Ubuntu</li>
<li><code><em>{{config.StratumHost}}</em></code> - Stratum Server name</li>
<li>use <code><em>-esm 0</em></code> and <code><em>{{config.StratumPort}}</em></code> port number for Stratum Server.</li>
<li><code><em>0x0000000000000000000000000000000000000000</em></code> - Your wallet address</li>
<li><code><em>your-worker-1</em></code> - Your worker name</li>
</ul>
</div>
</div>
<h4>Ethminer Examples</h4>
<p>
<strong>Download Ethminer (AMD/NVIDIA)</strong>: <a href="https://github.com/ethereum-mining/ethminer/releasesi/latest">Ethminer source/binary at Github</a>
</p>
<h5>Stratum method</h5>
<p>
<code>ethminer -SP 1 <em>-U</em> -S <em>{{config.StratumHost}}</em>:<em>{{config.StratumPort}}</em> -O <em>0x0000000000000000000000000000000000000000</em>.<em>your-worker-1</em> <em>--farm-recheck 2000</em></code>
</p>
<ul>
<li><code>-SP 1</code> - option for Stratum server</li>
<li><code><em>-U</em></code> - NVIDIA GPU or <code><em>-G</em></code> for AMD GPU</li>
<li><code>-S <em>{{config.StratumHost}}</em>:<em>{{config.StratumPort}}</em></code> - stratum_server_name:stratum_port_number</li>
<li><code>-O <em>0x0000000000000000000000000000000000000000</em>.<em>your-worker-1</em></code> - your_wallet_address.your_worker_name</li>
<li><code><em>--farm-recheck 2000</em></code> - Leave 2000 ms between checks for changed work (default 500ms. use higher value to use stratum for stability)</li>
</ul>
<h5>Legacy HTTP method</h5>
<p>
<code>ethminer <em>-U</em> -F <em>{{config.HttpHost}}</em>:<em>{{config.HttpPort}}</em>/<em>0x0000000000000000000000000000000000000000</em>/<em>your-worker-1</em> <em>--farm-recheck 200</em></code>
</p>
<ul>
<li><code><em>-U</em></code> - NVIDIA GPU or <code><em>-G</em></code> for AMD GPU</li>
<li><code>-F <em>{{config.HttpHost}}</em>:<em>{{config.HttpPort}}</em>/<em>0x0000000000000000000000000000000000000000</em>/<em>your-worker-1</em></code>
<ul><li>Legacy HTTP method. <em>http_server_name</em>:<em>http_port_number</em></li>
<li><code><em>0x0000000000000000000000000000000000000000</em>/<em>your-worker-1</em></code> - <em>your_wallet_address</em>.<em>your_worker_name</em></li>
</ul>
</li>
</ul>
</div>

198
www/app/templates/index.hbs

@ -1,169 +1,73 @@
<div class="jumbotron">
<div class="jumbotron-brand">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="alert alert-info text-center join_telegram">
<a href="https://t.me/poolnode" target="_blank"><i class="fab fa-telegram"></i> Telegram</a>
<div class="col-md-5">
<div class="row">
<h1 style="font-size: 24px">
<div class="etc-green">
<strong><span class="logo-2">ETC Pool</span></strong>
</div>
</h1>
</div>
<div class="row" style="padding:15px 5px;">
<strong>Min. payout threshold: {{config.PayoutThreshold}}</strong>, Payouts are continuos throughout the day.
<br/>
<span class="label label-success">PPLNS</span> Stable and profitable pool with regular payouts.
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<strong>{{t "home.min_payout_threshold"}}: {{config.PayoutThreshold}} {{config.Unit}}</strong> / {{t "home.payouts_run" interval=config.PayoutInterval}}<br/>
<span class="label label-success">PPLNS</span> {{t "home.payout_scheme_detail"}}
<div class="col-md-3 stats">
<div><i class="fa fa-users"></i> Miners Online: <span id="poolHashrate">{{format-number stats.model.minersTotal}}</span></div>
<div><i class="fa fa-tachometer"></i> Pool Hash Rate: <span id="poolHashrate">{{format-hashrate stats.model.hashrate}}</span></div>
<div><i class="fa fa-money"></i> Pool Fee: <span id="poolFee" class="label label-success">{{config.PoolFee}}</span></div>
{{#if stats.model.stats.lastBlockFound}}
<div><i class="fa fa-clock-o"></i> Last Block Found: <span>{{format-relative (seconds-to-ms stats.model.stats.lastBlockFound)}}</span></div>
{{/if}}
</div>
<div class="col-md-4 stats">
<div class="stats-box">
<div><i class="fa fa-users"></i> {{t "home.miners_online"}}: <span id="poolHashrate">{{format-number stats.model.minersTotal}}</span></div>
<div><i class="fa fa-tachometer"></i> {{t "home.pool_hashrate"}}: <span id="poolHashrate">{{format-hashrate stats.model.hashrate}}</span></div>
<div><i class="fa fa-money"></i> {{t "home.pool_fee"}}: <span id="poolFee" class="label label-success">{{config.PoolFee}}</span></div>
{{#if stats.model.stats.lastBlockFound}}
<div><i class="fa fa-clock-o"></i> {{t "home.last_block_found"}}: <span>{{format-relative (seconds-to-ms stats.model.stats.lastBlockFound)}}</span></div>
{{/if}}
</div>
</div>
<div class="col-md-4 stats">
<div class="stats-box">
<div><i class="fa fa-unlock-alt"></i> {{t "home.network_difficulty"}}: <span>{{with-metric-prefix stats.difficulty}}</span></div>
<div><i class="fa fa-tachometer"></i> {{t "home.network_hashrate"}}: <span>{{format-hashrate stats.hashrate}}</span></div>
<div><i class="fa fa-bars"></i> {{t "home.blockchain_height"}}: <span>{{format-number stats.height}}</span></div>
<div><i class="fa fa-clock-o"></i> {{t "home.current_round_variance"}}: <span>{{format-number stats.roundVariance style='percent'}}</span></div>
</div>
<div><i class="fa fa-unlock-alt"></i> Network Difficulty: <span>{{with-metric-prefix stats.difficulty}}</span></div>
<div><i class="fa fa-tachometer"></i> Network Hash Rate: <span>{{format-hashrate stats.hashrate}}</span></div>
<div><i class="fa fa-bars"></i> Blockchain Height: <span>{{format-number stats.height}}</span></div>
<div><i class="fa fa-clock-o"></i> Current Round Variance: <span>{{format-number stats.roundVariance style='percent'}}</span></div>
</div>
</div>
</div>
</div>
<div class="container">
<div>
{{high-charts mode=chartMode chartOptions=chartOptions content=chartData}}
</div>
<div class="stats">
<h3>{{t "home.query_history"}}</h3>
<h4>Your Stats &amp; Payment History</h4>
<div class="input-group">
{{input value=cachedLogin class="form-control" placeholder=(t "home.input.enter_your_wallet_address")}}
{{input value=cachedLogin class="form-control" placeholder="Enter Your Ethereum Classic Address"}}
<span class="input-group-btn">
<button class="btn btn-primary" type="button" {{action 'lookup' cachedLogin}}>
<span style="display: inline;"><i class="fa fa-search"></i> {{t "home.button.lookup"}}</span>
</button>
<button class="btn btn-primary btn-etc" type="button" {{action 'lookup' cachedLogin}}>
<span style="display: inline;"><i class="fa fa-search"></i> Lookup</span>
</button>
</span>
</div>
</div>
<div>
<hr/>
<div class="row">
<div class="col-md-4 col-sm-12">
<h5 class="note note-info text-center">
<span class="label label-success">2b</span>&nbsp;<a href="./#/help" title={{t "home.settings.help"}}><b>{{t "home.settings.title" hashes="< 150"}}</b></a>
<br>
{{t "home.settings.difficulty" diff="2"}}
<br><br>
<b>{{t "home.settings.stratum_mining"}}:</b>
<br><br>
<code>stratum+tcp://{{config.StratumHost}}:8002</code>
</h5>
</div>
<div class="col-md-4 col-sm-12">
<h5 class="note note-info text-center">
<span class="label label-success">4b</span>&nbsp;<a href="./#/help" title={{t "home.settings.help"}}><b>{{t "home.settings.title" hashes="150 - 800"}}</b></a>
<br>
{{t "home.settings.difficulty" diff="4"}}
<br><br>
<b>{{t "home.settings.stratum_mining"}}:</b>
<br><br>
<code>stratum+tcp://{{config.StratumHost}}:8004</code>
</h5>
</div>
<div class="col-md-4 col-sm-12">
<h5 class="note note-info text-center">
<span class="label label-success">9b</span>&nbsp;<a href="./#/help" title={{t "home.settings.help"}}><b>{{t "home.settings.title" hashes="> 800"}} & NiceHash</b></a>
<br>
{{t "home.settings.difficulty" diff="9"}}
<br><br>
<b>{{t "home.settings.stratum_mining"}}:</b>
<br><br>
<code>stratum+tcp://{{config.StratumHost}}:8009</code>
</h5>
<div class="container">
{{high-charts mode=chartMode chartOptions=chartOptions content=chartData}}
</div>
{{#if (equals config.Network 'mordor')}}
<div class="bs-callout bs-callout-danger">
<h4>
Warning
</h4>
<p>This is an experimental pool running on the <strong>Mordor</strong> testnet! All mined coins are for testing purposes only.</p>
</div>
</div>
<div class="row command_lines">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#windows_settings" aria-controls="home" role="tab" data-toggle="tab">{{t "home.settings.commands.windows"}}</a></li>
<li role="presentation"><a href="#smos_settings" aria-controls="profile" role="tab" data-toggle="tab">{{t "home.settings.commands.smos"}}</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="windows_settings">
<div style="padding:15px;">
<h5>{{t "home.settings.commands.windows_instruction"}}</h5>
<div class="panel-group" id="windows_settings_windows_tab" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="win_headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#windows_settings_windows_tab" href="#win_collapseOne" aria-expanded="true" aria-controls="win_collapseOne">
Claymore
</a>
</h4>
</div>
<div id="win_collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
<input type="text" class="form-control" value="EthDcrMiner64.exe -epool stratum+tcp://perkle-pool.esprezzo.io:8002 -esm 0 -ewal &lt;address&gt; -eworker &lt;worker&gt; -allcoins 1 -allpools 1">
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="win_headingTwo">
<h4 class="panel-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#windows_settings_windows_tab" href="#win_collapseTwo" aria-expanded="false" aria-controls="win_collapseTwo">
Ethminer 0.14
</a>
</h4>
</div>
<div id="win_collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
<div class="panel-body">
<input type="text" class="form-control" value="ethminer.exe -U -SP 1 -S perkle-pool.esprezzo.io:8004 -O &lt;address&gt;.&lt;worker&gt; --farm-recheck 1000 -v 2">
</div>
</div>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="smos_settings">
<div style="padding:15px;">
<h5>{{t "home.settings.commands.windows_instruction"}}</h5>
<div class="panel-group" id="smos_settings_smos_tab" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="smos_headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#smos_settings_smos_tab" href="#smos_collapseOne" aria-expanded="true" aria-controls="smos_collapseOne">
Claymore
</a>
</h4>
</div>
<div id="smos_collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
<input type="text" class="form-control" value="-wd 1 -r 1 -epool stratum+tcp://perkle-pool.esprezzo.io:8002 -ewal &lt;address&gt; -eworker $rigName -esm 0 -epsw x -allpools 1 -asm 1 -mode 1">
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="smos_headingTwo">
<h4 class="panel-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#smos_settings_smos_tab" href="#smos_collapseTwo" aria-expanded="false" aria-controls="smos_collapseTwo">
Ethminer 0.14
</a>
</h4>
</div>
<div id="smos_collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
<div class="panel-body">
<input type="text" class="form-control" value="-U -P stratum1+tcp://&lt;address&gt;.$rigName@perkle-pool.esprezzo.io:8002 --farm-recheck 200 --api-port -3333">
</div>
</div>
</div>
</div>
{{/if}}
<div class="jumbotron">
<div class="container">
<h3 class="text-center" style="padding: 0 0 5px 0; margin: 0 0 40px 0;"> Instructions</h3>
<div class="row">
<div class="col-md-12">
<p class="lead text-center">
<a href="#/help" class="ul-link">
<span class="ul ul-danger">stratum</span><br />
<code>{{config.StratumHost}}:{{config.StratumPort}}</code>
</a>
</p>
</div>
</div>
</div>

8
www/app/templates/luck.hbs

@ -2,10 +2,10 @@
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{{t "luck.blocks"}}</th>
<th>{{t "luck.shares_diff"}}</th>
<th>{{t "luck.uncle_rate"}}</th>
<th>{{t "luck.orphan_rate"}}</th>
<th>Blocks</th>
<th>Shares/Diff</th>
<th>Uncle Rate</th>
<th>Orphan Rate</th>
</tr>
</thead>
<tbody>

16
www/app/templates/miners.hbs

@ -1,19 +1,19 @@
<div class="jumbotron">
<div class="jumbotron-brand">
<div class="container">
<p class="lead">{{t "miners.total_hashrate"}}: {{format-hashrate model.hashrate}}.</p>
<strong>{{t "miners.total_miners"}}:</strong> <span class="label label-info">{{model.minersTotal}}</span>
<p class="lead">Total hashrate: {{format-hashrate model.hashrate}}.</p>
<strong>Total miners:</strong> <span class="label label-info">{{model.minersTotal}}</span>
</div>
</div>
<div class="container">
{{#if model.miners}}
<h3>{{t "miners.miners"}}</h3>
<h4>Miners</h4>
<div class="table-responsive">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{{t "miners.login"}}</th>
<th>{{t "miners.hashrate"}}</th>
<th>{{t "miners.last_beat"}}</th>
<th>Login</th>
<th>Hashrate</th>
<th>Last Beat</th>
</tr>
</thead>
<tbody>
@ -28,6 +28,6 @@
</table>
</div>
{{else}}
<h3>{{t "miners.no_miners"}}</h3>
<h3>No miners</h3>
{{/if}}
</div>

22
www/app/templates/payments.hbs

@ -1,20 +1,20 @@
<div class="jumbotron">
<div class="jumbotron-brand">
<div class="container">
<p class="lead">{{t "payments.pay_tx"}}</p>
<strong>{{t "payments.total_payments_sent"}}:</strong> <span class="label label-info">{{model.paymentsTotal}}</span>
<p class="lead">Pool always pays tx fees from it's own pocket for now.</p>
<strong>Total payments sent:</strong> <span class="label label-info">{{model.paymentsTotal}}</span>
</div>
</div>
<div class="container">
{{#if model.payments}}
<h3>{{t "payments.latest_payouts"}}</h3>
<h4>Latest Payouts</h4>
<div class="table-responsive">
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{{t "payments.time"}}</th>
<th>{{t "payments.amount"}}</th>
<th>{{t "payments.address"}}</th>
<th>{{t "payments.txid"}}</th>
<th>Time</th>
<th>Amount</th>
<th>Address</th>
<th>Tx ID</th>
</tr>
</thead>
<tbody>
@ -23,10 +23,10 @@
<td>{{format-date-locale tx.timestamp}}</td>
<td>{{format-number tx.formatAmount}}</td>
<td>
<a href="{{t "links.blockExplorerLink_address"}}{{tx.address}}" class="hash" rel="nofollow" target="_blank">{{tx.address}}</a>
<a href="https://expedition.dev/address/{{tx.address}}" class="hash" rel="nofollow" target="_blank">{{tx.address}}</a>
</td>
<td>
<a href="{{t "links.blockExplorerLink_tx"}}{{tx.tx}}" class="hash" rel="nofollow" target="_blank">{{format-tx tx.tx}}</a>
<a href="https://expedition.dev/tx/{{tx.tx}}" class="hash" rel="nofollow" target="_blank">{{format-tx tx.tx}}</a>
</td>
</tr>
{{/each}}
@ -34,6 +34,6 @@
</table>
</div>
{{else}}
<h3>{{t "payments.no_payouts_yet"}}</h3>
<h3>No payouts yet</h3>
{{/if}}
</div>

6
www/build.sh

@ -1,4 +1,8 @@
#!/bin/bash
# This is dirty af but will do for now.
cp fix/intl-format-cache/src/* node_modules/intl-format-cache/src/
cp fix/intl-format-cache/lib/* node_modules/intl-format-cache/lib/
./node_modules/.bin/ember build --environment production
sudo rsync -a dist/* /var/www/etcpool/ --delete
sudo rsync -a dist/* /var/www/etc2pool/ --delete

53
www/config/ember-intl.js

@ -1,53 +0,0 @@
/*jshint node:true*/
module.exports = function(environment) {
return {
/**
* The locales that are application supports.
*
* This is optional and is automatically set if project stores translations
* where ember-intl is able to look them up (<project root>/translations/).
*
* If the project relies on side-loading translations, then you must explicitly
* list out the locales. i.e: ['en-us', 'en-gb', 'fr-fr']
*
* @property locales
* @type {Array?}
* @default "null"
*/
locales: ['en-us'],
/**
* baseLocale is used to determine if translation keys are missing from other locales.
* This is property is optional, and if you rely on sideloading translations then
* this should be null
*
* @property baseLocale
* @type {String?}
* @default "null"
*/
baseLocale: "en-us",
/**
* disablePolyfill prevents the polyfill from being bundled in the asset folder of the build
*
* @property disablePolyfill
* @type {Boolean}
* @default "false"
*/
disablePolyfill: false,
/**
* prevents the translations from being bundled with the application code.
* This enables asynchronously loading the translations for the active locale
* by fetching them from the asset folder of the build.
*
* See: https://github.com/jasonmit/ember-intl/wiki/Asynchronously-loading-translations
*
* @property publicOnly
* @type {Boolean}
* @default "false"
*/
publicOnly: false
};
};

70
www/config/environment.js

@ -1,70 +0,0 @@
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
modulePrefix: 'open-social-pool',
environment: environment,
rootURL: '/',
locationType: 'hash',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// API host and port
ApiUrl: '//127.0.0.1/',
PoolName: 'yuriy0803',
CompanyName: '127.0.0.1',
// HTTP mining endpoint
HttpHost: 'https://127.0.0.1',
HttpPort: 8882,
// Stratum mining endpoint
StratumHost: '127.0.0.1',
StratumPort: 8002,
// Fee and payout details
PoolFee: '0.5%',
PayoutThreshold: '1.0',
PayoutInterval: '3h',
// For network hashrate (change for your favourite fork)
BlockTime: 14.4,
BlockReward: 3.2,
Unit: 'ETC:',
}
};
if (environment === 'development') {
/* Override ApiUrl just for development, while you are customizing
frontend markup and css theme on your workstation.
*/
ENV.APP.ApiUrl = 'http://localhost:8080/'
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
if (environment === 'production') {
}
return ENV;
};

80
www/fix/intl-format-cache/lib/es5.js vendored

@ -0,0 +1,80 @@
/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/
/* jslint esnext: true */
// Function.prototype.bind implementation from Mozilla Developer Network:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill
"use strict";
var bind = Function.prototype.bind || function (oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype) {
// native functions don't have a prototype
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
// Purposely using the same implementation as the Intl.js `Intl` polyfill.
// Copyright 2013 Andy Earnshaw, MIT License
var hop = Object.prototype.hasOwnProperty;
var realDefineProp = (function () {
try { return !!Object.defineProperty({}, 'a', {}); }
catch (e) { return false; }
})();
var es3 = !realDefineProp && !Object.prototype.__defineGetter__;
var defineProperty = realDefineProp ? Object.defineProperty :
function (obj, name, desc) {
if ('get' in desc && obj.__defineGetter__) {
obj.__defineGetter__(name, desc.get);
} else if (!hop.call(obj, name) || 'value' in desc) {
obj[name] = desc.value;
}
};
var objCreate = Object.create || function (proto, props) {
var obj, k;
function F() {}
F.prototype = proto;
obj = new F();
for (k in props) {
if (hop.call(props, k)) {
defineProperty(obj, k, props[k]);
}
}
return obj;
};
exports.bind = bind, exports.defineProperty = defineProperty, exports.objCreate = objCreate;
//# sourceMappingURL=es5.js.map

1
www/fix/intl-format-cache/lib/es5.js.map vendored

File diff suppressed because one or more lines are too long

83
www/fix/intl-format-cache/lib/memoizer.js vendored

@ -0,0 +1,83 @@
/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/
/* jshint esnext: true */
"use strict";
var src$es5$$ = require("./es5");
exports["default"] = createFormatCache;
// -----------------------------------------------------------------------------
function createFormatCache(FormatConstructor) {
var cache = src$es5$$.objCreate(null);
return function () {
var args = Array.prototype.slice.call(arguments);
var cacheId = getCacheId(args);
var format = cacheId && cache[cacheId];
if (!format) {
format = new (src$es5$$.bind.apply(FormatConstructor, [null].concat(args)))();
if (cacheId) {
cache[cacheId] = format;
}
}
return format;
};
}
// -- Utilities ----------------------------------------------------------------
function getCacheId(inputs) {
// When JSON is not available in the runtime, we will not create a cache id.
if (typeof JSON === 'undefined') { return; }
var cacheId = [];
var i, len, input;
for (i = 0, len = inputs.length; i < len; i += 1) {
input = inputs[i];
if (input && typeof input === 'object') {
cacheId.push(orderedProps(input));
} else {
cacheId.push(input);
}
}
return JSON.stringify(cacheId);
}
function orderedProps(obj) {
var props = [],
keys = [];
var key, i, len, prop;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
keys.push(key);
}
}
var orderedKeys = keys.sort();
for (i = 0, len = orderedKeys.length; i < len; i += 1) {
key = orderedKeys[i];
prop = {};
prop[key] = obj[key];
props[i] = prop;
}
return props;
}
//# sourceMappingURL=memoizer.js.map

1
www/fix/intl-format-cache/lib/memoizer.js.map vendored

File diff suppressed because one or more lines are too long

76
www/fix/intl-format-cache/src/es5.js vendored

@ -0,0 +1,76 @@
/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/
/* jslint esnext: true */
// Function.prototype.bind implementation from Mozilla Developer Network:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill
var bind = Function.prototype.bind || function (oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype) {
// native functions don't have a prototype
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
// Purposely using the same implementation as the Intl.js `Intl` polyfill.
// Copyright 2013 Andy Earnshaw, MIT License
var hop = Object.prototype.hasOwnProperty;
var realDefineProp = (function () {
try { return !!Object.defineProperty({}, 'a', {}); }
catch (e) { return false; }
})();
var es3 = !realDefineProp && !Object.prototype.__defineGetter__;
var defineProperty = realDefineProp ? Object.defineProperty :
function (obj, name, desc) {
if ('get' in desc && obj.__defineGetter__) {
obj.__defineGetter__(name, desc.get);
} else if (!hop.call(obj, name) || 'value' in desc) {
obj[name] = desc.value;
}
};
var objCreate = Object.create || function (proto, props) {
var obj, k;
function F() {}
F.prototype = proto;
obj = new F();
for (k in props) {
if (hop.call(props, k)) {
defineProperty(obj, k, props[k]);
}
}
return obj;
};
export {bind, defineProperty, objCreate};

81
www/fix/intl-format-cache/src/memoizer.js vendored

@ -0,0 +1,81 @@
/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/
/* jshint esnext: true */
import {bind, objCreate} from './es5';
export default createFormatCache;
// -----------------------------------------------------------------------------
function createFormatCache(FormatConstructor) {
var cache = objCreate(null);
return function () {
var args = Array.prototype.slice.call(arguments);
var cacheId = getCacheId(args);
var format = cacheId && cache[cacheId];
if (!format) {
format = new (bind.apply(FormatConstructor, [null].concat(args)))();
if (cacheId) {
cache[cacheId] = format;
}
}
return format;
};
}
// -- Utilities ----------------------------------------------------------------
function getCacheId(inputs) {
// When JSON is not available in the runtime, we will not create a cache id.
if (typeof JSON === 'undefined') { return; }
var cacheId = [];
var i, len, input;
for (i = 0, len = inputs.length; i < len; i += 1) {
input = inputs[i];
if (input && typeof input === 'object') {
cacheId.push(orderedProps(input));
} else {
cacheId.push(input);
}
}
return JSON.stringify(cacheId);
}
function orderedProps(obj) {
var props = [],
keys = [];
var key, i, len, prop;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
keys.push(key);
}
}
var orderedKeys = keys.sort();
for (i = 0, len = orderedKeys.length; i < len; i += 1) {
key = orderedKeys[i];
prop = {};
prop[key] = obj[key];
props[i] = prop;
}
return props;
}

18792
www/package-lock.json generated

File diff suppressed because it is too large Load Diff

17
www/package.json

@ -1,7 +1,8 @@
{
"name": "open-social-pool",
"name": "open-etc-pool",
"version": "0.0.0",
"description": "Ethereum Social Pool",
"description": "Open ETC Pool",
"private": true,
"directories": {
"doc": "doc",
"test": "tests"
@ -13,7 +14,7 @@
},
"repository": "",
"engines": {
"node": "8.11.1"
"node": ">= 0.12.0"
},
"author": "",
"license": "MIT",
@ -21,9 +22,10 @@
"broccoli-asset-rev": "^2.4.5",
"broccoli-funnel": "^1.0.9",
"ember-ajax": "^2.4.1",
"ember-cli": "2.9.1",
"ember-cli": "2.13",
"ember-cli-app-version": "^2.0.0",
"ember-cli-babel": "^5.1.7",
"ember-cli-cookie": "^0.2.0",
"ember-cli-dependency-checker": "^1.3.0",
"ember-cli-htmlbars": "^1.0.10",
"ember-cli-htmlbars-inline-precompile": "^0.3.3",
@ -34,14 +36,15 @@
"ember-cli-sri": "^2.1.0",
"ember-cli-test-loader": "^1.1.0",
"ember-cli-uglify": "^1.2.0",
"ember-data": "2.10.0",
"ember-export-application-global": "^1.0.5",
"ember-highcharts": "0.6.0",
"ember-inflector": "1.9.6",
"ember-intl": "2.15.1",
"ember-load-initializers": "^0.5.1",
"ember-resolver": "^2.0.3",
"ember-welcome-page": "^1.0.3",
"highcharts": "^6.0.7",
"loader.js": "^4.0.10",
"ember-intl": "2.15.1",
"ember-cli-cookie": "^0.2.0"
"loader.js": "^4.0.10"
}
}

BIN
www/public/bg_legacy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

BIN
www/public/escheresque.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

BIN
www/public/escheresque_ste.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

1
www/public/etc.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1543 2499.2"><defs><style>.cls-1{fill:#3ab83a;}.cls-2{fill:#0b8311;}.cls-3{fill:#146714;}</style></defs><title>e</title><g id="Layer_2" data-name="Layer 2"><g id="svg8"><g id="layer5"><g id="g1627"><path id="path1599" class="cls-1" d="M0,1361.05c271.87,144.38,555.56,295.51,774.67,412.45L1543,1361.05c-278.2,413.29-510,757.36-768.33,1138.15C515.88,2119.25,230.08,1700,0,1361.05Zm29.55-114L775.51,849l736.25,395.14L775.93,1642.63ZM774.67,721.47,0,1129.28,771.29,0,1543,1131.81,774.67,721.47Z"/><path id="path1593" class="cls-2" d="M774.67,1773.5,1543,1361.05C1264.8,1774.34,774.67,2499.2,774.67,2499.2ZM775.51,849l736.25,395.14L775.93,1642.63,775.51,849Zm-.84-127.5L771.29,0,1543,1131.81Z"/><path id="path1603" class="cls-2" d="M29.55,1247.06l746,61.22,736.25-63.75L775.93,1643.05Z"/><path id="path1606" class="cls-3" d="M775.51,1308.28l736.25-63.75L775.93,1643.05l-.42-334.77Z"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 954 B

BIN
www/public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

BIN
www/public/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
www/public/sayagata-400px.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

12
www/tests/unit/adapters/application-test.js

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('adapter:application', 'Unit | Adapter | application', {
// Specify the other units that are required for this test.
// needs: ['serializer:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let adapter = this.subject();
assert.ok(adapter);
});

12
www/tests/unit/helpers/format-difficulty-test.js

@ -1,12 +0,0 @@
import { formatDifficulty } from 'open-social-pool/helpers/format-difficulty';
import { module, test } from 'qunit';
module('Unit | Helper | format difficulty');
// Replace this with your real tests.
test('it works', function(assert) {
let result = formatDifficulty([42]);
assert.ok(result);
});

12
www/tests/unit/helpers/worker-colorizer-test.js

@ -1,12 +0,0 @@
import { workerColorizer } from 'open-social-pool/helpers/worker-colorizer';
import { module, test } from 'qunit';
module('Unit | Helper | worker colorizer');
// Replace this with your real tests.
test('it works', function(assert) {
let result = workerColorizer([42]);
assert.ok(result);
});

12
www/tests/unit/helpers/worker-earnperday-test.js

@ -1,12 +0,0 @@
import { workerEarnperday } from 'open-social-pool/helpers/worker-earnperday';
import { module, test } from 'qunit';
module('Unit | Helper | worker earnperday');
// Replace this with your real tests.
test('it works', function(assert) {
let result = workerEarnperday([42]);
assert.ok(result);
});

15
www/tests/unit/serializers/chart-test.js

@ -0,0 +1,15 @@
import { moduleForModel, test } from 'ember-qunit';
moduleForModel('chart', 'Unit | Serializer | chart', {
// Specify the other units that are required for this test.
needs: ['serializer:chart']
});
// Replace this with your real tests.
test('it serializes records', function(assert) {
let record = this.subject();
let serializedRecord = record.serialize();
assert.ok(serializedRecord);
});

20
www/tests/units/charts/difficulty-test.js

@ -0,0 +1,20 @@
import {
moduleForComponent,
test
} from 'ember-qunit';
moduleForComponent('difficulty', 'difficulty', {
needs: [ 'component:high-charts' ]
});
test('it renders', function(assert) {
assert.expect(2);
// creates the component instance
let component = this.subject();
assert.equal(component._state, 'preRender');
// appends the component to the page
this.render(assert);
assert.equal(component._state, 'inDOM');
});

159
www/translations/ar-sa.yaml

@ -1,159 +0,0 @@
general:
direction: rtl
calculator:
title: كم ممكن تربح?
close: سكر الباب وراك
menu:
home: الرئيسية
help: مساعدة
calculator: الحاسبة
pool_blocks: حزم المجمع
payments: المدفوعات
miners: المعدنين
about: حول
i18n:
about: about
help: help
language: اللغة
lang:
arabic: عربي
english: English
home:
min_payout_threshold: الحد الأدنى للدفع
payouts_run: كل {interval}.
payout_scheme_detail: دفع فوري بنظام PPLNS.
miners_online: معدنين متصلين
pool_hashrate: سرعة معالجة المجمع
pool_fee: رسوم المجمع
last_block_found: آخر حزمة تم إيجادها
network_difficulty: صعوبة الشبكة
network_hashrate: سرعة معالجة الشبكة
blockchain_height: إرتفاع سلسلة الحزم
current_round_variance: تباين الجولة الحالية
query_history: إحصائياتك وسجل مدفوعاتك
input:
enter_your_wallet_address: أدخل رقم محفظتك
button:
lookup: بحث
help:
minimal: help-minimal
settings:
difficulty: (الصعوبة {diff} بليون)
help: إضغط للمساعدة
title: جهاز {hashes} MH/s
stratum_mining: رابط الـStratum
commands:
windows: Windows
smos: SimpleMining
windows_instruction: أضف هذا لملف الـbat الخاص بك
smos_instruction: أضف هذه الإعدادات لـ Rig Group
account:
immature:
balance: الرصيد الغير جاهز
description: رصيد مبدئي بإنتظار تأكيد الحزمة
pending:
balance: الرصيد المعلق
description: رصيد بإنتظار عملية الدفع.
current: الدفع الحالي
last_share_submitted: آخر حصة مرسلة
online: أجهزة متصلة
hashrate: سرعة المعالجة
earnings:
miner: الأرباح اليومية (3h معدل)
worker: أرباح / يوم (معدل قصير)
blocks:
found: الحزم المعدنة
total:
payments: إجمالي المدفوعات
paid: إجمالي ماتم دفعه
round_share: حصتك في الجولة
round_share_description: نسبة مشاركتك في مجمع التعدين.
epoch_switch: تبديل العهد
workers: أجهزة
payouts: مدفوعات
your_workers: الأجهزة
server: الخادم
difficulty: الصعوبة
last_share: آخر ظهور
short_average: غير دقيق, معدل قصير
short_average_abbrv: 30m
long_average: دقيق, معدل طويل
long_average_abbrv: 3h
no_workers_online: لايوجد أجهزة متصلة
notice: تنويه
notice_html: سيتم إعادة ضبط سرعة معالجتك بسلاسة إلى أن يكون لديك حصص كافية لتدقيق الحساب.<br/>هناك فترتين حساب, قصيرة, وطويلة, القصيرة مدتها 30 دقيقة تقريبا, والطويلة 3 ساعات.<br/>الأجهزة الميتة (الغير متصلة) سيتم تمييزها في الجدول إذا لم تقم بإرسال حصص خلال نصف وقت المدة القصيرة, أي 15 دقيقة, لتسهل عليك صيانتها
json_api_url: رابط إحصائياتك بصيغة JSON
payout:
latest_payouts: آخر مدفوعاتك
time: الوقت
txid: رقم العملية
amount: القيمة
no_payouts_yet: لايوجد مدفوعات حتى الآن
block:
pool_rewards: يقوم المجمع دائماً بدفع مكافأة الحزمة كاملة شاملة رسوم الحوالة ومكافأة uncle
pool_notice:
html: يتطلب تأكيد الحزمة<em style="text-decoration:underline">حتى</em> <span class="label label-success">{success}</span> حزمة. وبالعادة أقل من ذلك.
blocks: الحزم
immature: غير مؤكدة
new: الحزم الجديدة
orphan: غير محسوبة
no_matured_blocks_yet: لايوجد حزم مؤكدة حتى الآن
reward: المكافأة
height: الإرتفاع
hash: هاش
time_found: وقت الإيجاد
variance: تباين الحصص (حصة/صعوبة)
matured: حزم مؤكدة
immature_blocks: حزم غير مؤكدة
no_immature_blocks_yet: لايوجد حزم غير مؤكدة حتى الآن
no_new_blocks_yet: لايوجد حزم حتى الآن
recently_found_blocks: الحزم التي تم إيجادها مؤخراً
luck:
blocks: الحزم
shares_diff: الحصة/الصعوبة
uncle_rate: معدل Uncle
orphan_rate: معدل الغير محسوب
payments:
pay_tx: يقوم المجمع بدفع رسوم الحوالة من رصيده الخاص.
total_payments_sent: إجمالي المدفوعات المرسلة
latest_payouts: آخر المدفوعات
time: الوقت
amount: المبلغ
address: العنوان
txid: رقم الحوالة
no_payouts_yet: لايوجد مدفوعات حتى الآن
miners:
total_hashrate: إجمالي سرعة معالجة المجمع
total_miners: إجمالي المعدنين
login: العنوان
hashrate: سرعة المعالجة
last_beat: آخر ظهور
no_miners: لايوجد معدنين
miners: المعدنين
wallet:
dapp_html: <a href="https://ethereum.org/">https://ethereum.org/</a>
online_html: <a href="https://www.myetherwallet.com/">https://www.myetherwallet.com</a> Online wallet at myetherwallet.com
errors:
header: الموقع متوقف مؤقتاً
message: بالعادة هذه مشكلة مؤقتة والتعدين لا يتأثر.
links:
blockExplorerLink: 'https://etc1.trezor.io/'
blockExplorerLink_tx: 'https://etc1.trezor.io/tx/'
blockExplorerLink_uncle: 'https://etc1.trezor.io/uncle/'
blockExplorerLink_block: 'https://etc1.trezor.io/block/'
blockExplorerLink_address: 'https://etc1.trezor.io/address/'
footer:
copyrights: '&copy; Brought to you.'

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save