From 4badf93cddf26bd2b97fb56e50a5334eea0df1e5 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Mon, 2 Apr 2018 03:57:51 +0900 Subject: [PATCH 01/30] support hashLimit --- config.example.json | 1 + proxy/config.go | 1 + proxy/handlers.go | 14 ++++++++++++-- proxy/miner.go | 26 +++++++++++++++++++------- proxy/stratum.go | 18 +++++++++++------- storage/redis.go | 13 +++++++++++++ 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/config.example.json b/config.example.json index 1a264b8cc..f4c625601 100644 --- a/config.example.json +++ b/config.example.json @@ -4,6 +4,7 @@ "name": "main", "proxy": { + "hashLimit" : 240000000, "enabled": true, "listen": "0.0.0.0:8888", "limitHeadersSize": 1024, diff --git a/proxy/config.go b/proxy/config.go index 6248c538c..06c024153 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -29,6 +29,7 @@ type Config struct { } type Proxy struct { + HashLimit int64 `json:"hashLimit"` Enabled bool `json:"enabled"` Listen string `json:"listen"` LimitHeadersSize int `json:"limitHeadersSize"` diff --git a/proxy/handlers.go b/proxy/handlers.go index 6f1fa902c..4ba4400bf 100644 --- a/proxy/handlers.go +++ b/proxy/handlers.go @@ -1,6 +1,7 @@ package proxy import ( + "fmt" "log" "regexp" "strings" @@ -69,14 +70,23 @@ func (s *ProxyServer) handleSubmitRPC(cs *Session, login, id string, params []st return false, &ErrorReply{Code: -1, Message: "Malformed PoW result"} } t := s.currentBlockTemplate() - exist, validShare := s.processShare(login, id, cs.ip, t, params) - ok := s.policy.ApplySharePolicy(cs.ip, !exist && validShare) + exist, validShare, extraErr := s.processShare(login, id, cs.ip, t, params) + ok := s.policy.ApplySharePolicy(cs.ip, !exist && validShare && extraErr == nil) if exist { log.Printf("Duplicate share from %s@%s %v", login, cs.ip, params) return false, &ErrorReply{Code: 22, Message: "Duplicate share"} } + if extraErr != nil { + log.Printf("Invalid share from %s@%s: %v", login, cs.ip, extraErr) + // Bad shares limit reached, return error and close + if !ok { + return false, &ErrorReply{Code: 23, Message: "Invalid share"} + } + return false, &ErrorReply{Code: 20, Message: fmt.Sprintf("Invalid share : %v", extraErr)} + } + if !validShare { log.Printf("Invalid share from %s@%s", login, cs.ip) // Bad shares limit reached, return error and close diff --git a/proxy/miner.go b/proxy/miner.go index 8d312f9d8..bddcfee3c 100644 --- a/proxy/miner.go +++ b/proxy/miner.go @@ -1,6 +1,7 @@ package proxy import ( + "fmt" "log" "math/big" "strconv" @@ -12,7 +13,7 @@ import ( var hasher = ethash.New() -func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, params []string) (bool, bool) { +func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, params []string) (bool, bool, error) { nonceHex := params[0] hashNoNonce := params[1] mixDigest := params[2] @@ -22,7 +23,7 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param h, ok := t.headers[hashNoNonce] if !ok { log.Printf("Stale share from %v@%v", login, ip) - return false, false + return false, false, nil } share := Block{ @@ -42,7 +43,7 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param } if !hasher.Verify(share) { - return false, false + return false, false, nil } if hasher.Verify(block) { @@ -51,12 +52,12 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param log.Printf("Block submission failure at height %v for %v: %v", h.height, t.Header, err) } else if !ok { log.Printf("Block rejected at height %v for %v", h.height, t.Header) - return false, false + return false, false, nil } else { s.fetchBlockTemplate() exist, err := s.backend.WriteBlock(login, id, params, shareDiff, h.diff.Int64(), h.height, s.hashrateExpiration) if exist { - return true, false + return true, false, nil } if err != nil { log.Println("Failed to insert block candidate into backend:", err) @@ -66,13 +67,24 @@ func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, param log.Printf("Block found by miner %v@%v at height %d", login, ip, h.height) } } else { + // check hashrate limit + if s.config.Proxy.HashLimit > 0 { + currentHashrate, _ := s.backend.GetCurrentHashrate(login) + + if s.config.Proxy.HashLimit > 0 && currentHashrate > s.config.Proxy.HashLimit { + err := fmt.Errorf("hashLimit exceed: %v(current) > %v(hashLimit)", currentHashrate, s.config.Proxy.HashLimit) + log.Println("Failed to insert share data into backend:", err) + return false, false, err + } + } + exist, err := s.backend.WriteShare(login, id, params, shareDiff, h.height, s.hashrateExpiration) if exist { - return true, false + return true, false, nil } if err != nil { log.Println("Failed to insert share data into backend:", err) } } - return false, true + return false, true, nil } diff --git a/proxy/stratum.go b/proxy/stratum.go index ff3b61ac7..9ad26db64 100644 --- a/proxy/stratum.go +++ b/proxy/stratum.go @@ -114,13 +114,13 @@ func (cs *Session) handleTCPMessage(s *ProxyServer, req *StratumReq) error { if errReply != nil { return cs.sendTCPError(req.Id, errReply) } - return cs.sendTCPResult(req.Id, reply) + return cs.sendTCPResult(req.Id, reply, nil) case "eth_getWork": reply, errReply := s.handleGetWorkRPC(cs) if errReply != nil { return cs.sendTCPError(req.Id, errReply) } - return cs.sendTCPResult(req.Id, &reply) + return cs.sendTCPResult(req.Id, &reply, nil) case "eth_submitWork": var params []string err := json.Unmarshal(req.Params, ¶ms) @@ -130,22 +130,26 @@ func (cs *Session) handleTCPMessage(s *ProxyServer, req *StratumReq) error { } reply, errReply := s.handleTCPSubmitRPC(cs, req.Worker, params) if errReply != nil { - return cs.sendTCPError(req.Id, errReply) + if errReply.Code != 20 { + return cs.sendTCPError(req.Id, errReply) + } else { + return cs.sendTCPResult(req.Id, &reply, errReply) + } } - return cs.sendTCPResult(req.Id, &reply) + return cs.sendTCPResult(req.Id, &reply, nil) case "eth_submitHashrate": - return cs.sendTCPResult(req.Id, true) + return cs.sendTCPResult(req.Id, true, nil) default: errReply := s.handleUnknownRPC(cs, req.Method) return cs.sendTCPError(req.Id, errReply) } } -func (cs *Session) sendTCPResult(id json.RawMessage, result interface{}) error { +func (cs *Session) sendTCPResult(id json.RawMessage, result interface{}, reply *ErrorReply) error { cs.Lock() defer cs.Unlock() - message := JSONRpcResp{Id: id, Version: "2.0", Error: nil, Result: result} + message := JSONRpcResp{Id: id, Version: "2.0", Error: reply, Result: result} return cs.enc.Encode(&message) } diff --git a/storage/redis.go b/storage/redis.go index 449b58fcc..05e004ab2 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -761,6 +761,9 @@ func (r *RedisClient) CollectWorkersStats(sWindow, lWindow time.Duration, login stats["workersOffline"] = offline stats["hashrate"] = totalHashrate stats["currentHashrate"] = currentHashrate + + tx.HSet(r.formatKey("currenthashrate", login), "hashrate", strconv.FormatInt(currentHashrate, 10)) + tx.Expire(r.formatKey("currenthashrate", login), lWindow) return stats, nil } @@ -957,3 +960,13 @@ func convertPaymentsResults(raw *redis.ZSliceCmd) []map[string]interface{} { } return result } + +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() +} From ae0051bf03a2d3e4606ac4b768bcc76b65dfe0ea Mon Sep 17 00:00:00 2001 From: hackyminer Date: Sun, 1 Apr 2018 23:57:07 +0900 Subject: [PATCH 02/30] support Ethersocial network --- payouts/unlocker.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/payouts/unlocker.go b/payouts/unlocker.go index c073ef0b3..246a30011 100644 --- a/payouts/unlocker.go +++ b/payouts/unlocker.go @@ -29,7 +29,8 @@ type UnlockerConfig struct { } const minDepth = 16 -const byzantiumHardForkHeight = 4370000 + +var byzantiumHardForkHeight int64 = 4370000 var homesteadReward = math.MustParseBig256("5000000000000000000") var byzantiumReward = math.MustParseBig256("3000000000000000000") @@ -58,6 +59,23 @@ func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient) *BlockU } u := &BlockUnlocker{config: cfg, backend: backend} u.rpc = rpc.NewRPCClient("BlockUnlocker", cfg.Daemon, cfg.Timeout) + + block, err := u.rpc.GetBlockByHeight(0) + if err != nil || block == nil { + log.Fatalf("Error while retrieving genesis block from node: %v", err) + } + + // EtherSocial Network + if block.Hash == "0x310dd3c4ae84dd89f1b46cfdd5e26c8f904dfddddc73f323b468127272e20e9f" { + log.Printf("Found genesis.hash is %v", block.Hash) + byzantiumHardForkHeight = 600000 + homesteadReward = math.MustParseBig256("9000000000000000000") + byzantiumReward = math.MustParseBig256("5000000000000000000") + + log.Printf("Set byzantiumHardForkHeight to %v", byzantiumHardForkHeight) + log.Printf("Set homesteadReward to %v", homesteadReward) + log.Printf("Set byzantiumReward to %v", byzantiumReward) + } return u } From 1d10621d46ff9eade8c1622b0d93765fdb73e068 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Sat, 7 Apr 2018 00:36:01 +0900 Subject: [PATCH 03/30] redis: support unix domain socket --- storage/redis.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/storage/redis.go b/storage/redis.go index 05e004ab2..667b06022 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -79,12 +79,16 @@ type Worker struct { } func NewRedisClient(cfg *Config, prefix string) *RedisClient { - client := redis.NewClient(&redis.Options{ + options := 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} } From a683efd13abe8dd204e87f2f2fde87e0dda64032 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Sat, 14 Apr 2018 07:48:40 +0900 Subject: [PATCH 04/30] support /api/settings * DonateFee/PoolFee info. * EthProxy/Stratum enabled info. --- api/server.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++- main.go | 4 +++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/api/server.go b/api/server.go index dd021a1e3..26cd62c7c 100644 --- a/api/server.go +++ b/api/server.go @@ -12,6 +12,7 @@ import ( "github.com/gorilla/mux" + "github.com/sammy007/open-ethereum-pool/rpc" "github.com/sammy007/open-ethereum-pool/storage" "github.com/sammy007/open-ethereum-pool/util" ) @@ -30,6 +31,7 @@ type ApiConfig struct { } type ApiServer struct { + settings map[string]interface{} config *ApiConfig backend *storage.RedisClient hashrateWindow time.Duration @@ -38,6 +40,8 @@ type ApiServer struct { miners map[string]*Entry minersMu sync.RWMutex statsIntv time.Duration + rpc *rpc.RPCClient + genesisHash string } type Entry struct { @@ -45,15 +49,27 @@ type Entry struct { updatedAt int64 } -func NewApiServer(cfg *ApiConfig, backend *storage.RedisClient) *ApiServer { +func NewApiServer(cfg *ApiConfig, settings map[string]interface{}, backend *storage.RedisClient) *ApiServer { + rpcDaemon := settings["BlockUnlocker"].(map[string]interface{})["Daemon"].(string) + rpcTimeout := settings["BlockUnlocker"].(map[string]interface{})["Timeout"].(string) + rpc := rpc.NewRPCClient("BlockUnlocker", rpcDaemon, rpcTimeout) + + block, err := rpc.GetBlockByHeight(0) + if err != nil || block == nil { + log.Fatalf("Error while retrieving genesis block from node: %v", err) + } + hashrateWindow := util.MustParseDuration(cfg.HashrateWindow) hashrateLargeWindow := util.MustParseDuration(cfg.HashrateLargeWindow) return &ApiServer{ + settings: settings, config: cfg, backend: backend, hashrateWindow: hashrateWindow, hashrateLargeWindow: hashrateLargeWindow, miners: make(map[string]*Entry), + rpc: rpc, + genesisHash: block.Hash, } } @@ -107,6 +123,7 @@ func (s *ApiServer) listen() { r.HandleFunc("/api/miners", s.MinersIndex) r.HandleFunc("/api/blocks", s.BlocksIndex) r.HandleFunc("/api/payments", s.PaymentsIndex) + r.HandleFunc("/api/settings", s.Settings) r.HandleFunc("/api/accounts/{login:0x[0-9a-fA-F]{40}}", s.AccountIndex) r.NotFoundHandler = http.HandlerFunc(notFound) err := http.ListenAndServe(s.config.Listen, r) @@ -296,6 +313,40 @@ func (s *ApiServer) AccountIndex(w http.ResponseWriter, r *http.Request) { } } +func (s *ApiServer) Settings(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Cache-Control", "max-age=600") + w.WriteHeader(http.StatusOK) + + reply := make(map[string]interface{}) + + reply["HashLimit"] = s.settings["Proxy"].(map[string]interface{})["HashLimit"] + reply["Difficulty"] = s.settings["Proxy"].(map[string]interface{})["Difficulty"] + + reply["PoolFee"] = s.settings["BlockUnlocker"].(map[string]interface{})["PoolFee"] + reply["PoolFeeAddress"] = s.settings["BlockUnlocker"].(map[string]interface{})["PoolFeeAddress"] + reply["Donate"] = s.settings["BlockUnlocker"].(map[string]interface{})["Donate"] + reply["DonateFee"] = s.settings["BlockUnlocker"].(map[string]interface{})["DonateFee"] + reply["DonateAddress"] = s.settings["BlockUnlocker"].(map[string]interface{})["DonateAddress"] + reply["KeyTxFees"] = s.settings["BlockUnlocker"].(map[string]interface{})["KeepTxFees"] + reply["BlockUnlockDepth"] = s.settings["BlockUnlocker"].(map[string]interface{})["Depth"] + + reply["EthProxy"] = s.settings["Proxy"].(map[string]interface{})["Enabled"] + reply["EthProxyPool"] = s.settings["Proxy"].(map[string]interface{})["Listen"] + reply["Stratum"] = s.settings["Proxy"].(map[string]interface{})["Stratum"].(map[string]interface{})["Enabled"] + reply["StratumPool"] = s.settings["Proxy"].(map[string]interface{})["Stratum"].(map[string]interface{})["Listen"] + reply["PayoutThreshold"] = s.settings["Payouts"].(map[string]interface{})["Threshold"] + reply["PayoutInterval"] = s.settings["Payouts"].(map[string]interface{})["Interval"] + + reply["GenesisHash"] = s.genesisHash + + err := json.NewEncoder(w).Encode(reply) + if err != nil { + log.Println("Error serializing API response: ", err) + } +} + func (s *ApiServer) getStats() map[string]interface{} { stats := s.stats.Load() if stats != nil { diff --git a/main.go b/main.go index faff57e60..f226e9115 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "runtime" "time" + "github.com/fatih/structs" "github.com/yvasiyarov/gorelic" "github.com/sammy007/open-ethereum-pool/api" @@ -28,7 +29,8 @@ func startProxy() { } func startApi() { - s := api.NewApiServer(&cfg.Api, backend) + settings := structs.Map(&cfg) + s := api.NewApiServer(&cfg.Api, settings, backend) s.Start() } From fce7676d28049f94d0f639e9b8a680c8cb8859d3 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Tue, 17 Apr 2018 14:42:58 +0900 Subject: [PATCH 05/30] support Callisto Network --- payouts/unlocker.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/payouts/unlocker.go b/payouts/unlocker.go index 246a30011..eaffe041f 100644 --- a/payouts/unlocker.go +++ b/payouts/unlocker.go @@ -75,6 +75,15 @@ func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient) *BlockU log.Printf("Set byzantiumHardForkHeight to %v", byzantiumHardForkHeight) log.Printf("Set homesteadReward to %v", homesteadReward) log.Printf("Set byzantiumReward to %v", byzantiumReward) + } else if block.Hash == "0x82270b80fc90beb005505a9ef95039639968a0e81b2904ad30128c93d713d2c4" { + // CLO Network + log.Printf("Found genesis.hash is %v", block.Hash) + byzantiumHardForkHeight = 0 + byzantiumReward = math.MustParseBig256("420000000000000000000") + + log.Printf("Set byzantiumHardForkHeight(not used) to %v", byzantiumHardForkHeight) + log.Printf("Set homesteadReward(not used) to %v", homesteadReward) + log.Printf("Set byzantiumReward(CLO reward) to %v", byzantiumReward) } return u } From bd505b0a44389f1a3d40ba536b638a68d5c7b2cf Mon Sep 17 00:00:00 2001 From: hackyminer Date: Thu, 19 Apr 2018 06:47:33 +0900 Subject: [PATCH 06/30] i18nized --- www/app/controllers/application.js | 15 +++ www/app/router.js | 2 + www/app/routes/application.js | 53 ++++++++++- www/app/templates/about-ko.hbs | 22 +++++ www/app/templates/account.hbs | 34 +++---- www/app/templates/account/index.hbs | 19 ++-- www/app/templates/account/payouts.hbs | 12 +-- www/app/templates/application.hbs | 29 ++++-- www/app/templates/blocks.hbs | 15 ++- www/app/templates/blocks/block.hbs | 10 +- www/app/templates/blocks/immature.hbs | 14 +-- www/app/templates/blocks/index.hbs | 14 +-- www/app/templates/blocks/pending.hbs | 12 +-- www/app/templates/help-ko.hbs | 90 ++++++++++++++++++ www/app/templates/help-minimal-ko.hbs | 16 ++++ www/app/templates/help-minimal.hbs | 16 ++++ www/app/templates/help.hbs | 105 +++++++++++++++------ www/app/templates/index.hbs | 36 ++++--- www/app/templates/luck.hbs | 8 +- www/app/templates/miners.hbs | 14 +-- www/app/templates/payments.hbs | 20 ++-- www/config/environment.js | 14 ++- www/translations/en-us.yaml | 126 +++++++++++++++++++++++++ www/translations/ko.yaml | 130 ++++++++++++++++++++++++++ 24 files changed, 677 insertions(+), 149 deletions(-) create mode 100644 www/app/templates/about-ko.hbs create mode 100644 www/app/templates/help-ko.hbs create mode 100644 www/app/templates/help-minimal-ko.hbs create mode 100644 www/app/templates/help-minimal.hbs create mode 100644 www/translations/ko.yaml diff --git a/www/app/controllers/application.js b/www/app/controllers/application.js index 1e40cd9eb..20fcd3152 100644 --- a/www/app/controllers/application.js +++ b/www/app/controllers/application.js @@ -2,6 +2,7 @@ import Ember from 'ember'; import config from '../config/environment'; export default Ember.Controller.extend({ + intl: Ember.inject.service(), get config() { return config.APP; }, @@ -65,6 +66,20 @@ export default Ember.Controller.extend({ } }), + // FIXME + languages: Ember.computed({ + get() { + let intl = this.get('intl'); + return [ { name: intl.t('lang.korean'), value: 'ko'}, { name: intl.t('lang.english'), value: 'en-us'} ]; + } + }), + + selectedLanguage: Ember.computed({ + get() { + return Ember.$.cookie('lang'); + } + }), + roundVariance: Ember.computed('model', { get() { var percent = this.get('model.stats.roundShares') / this.get('difficulty'); diff --git a/www/app/router.js b/www/app/router.js index 9eac95d62..7b1970c4e 100644 --- a/www/app/router.js +++ b/www/app/router.js @@ -17,9 +17,11 @@ Router.map(function() { }); this.route('help'); + this.route('help-ko'); this.route('payments'); this.route('miners'); this.route('about'); + this.route('about-ko'); }); export default Router; diff --git a/www/app/routes/application.js b/www/app/routes/application.js index 293d5ce61..c2f793391 100644 --- a/www/app/routes/application.js +++ b/www/app/routes/application.js @@ -1,11 +1,62 @@ import Ember from 'ember'; import config from '../config/environment'; +function selectLocale(selected) { + // FIXME + let supported = ['en', 'ko', '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, beforeModel() { - this.get('intl').setLocale('en-us'); + 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); + } + }, + + actions: { + selectLanguage: function() { + let selected = Ember.$('option:selected').attr('value'); + if (typeof selected === 'undefined') { + return true; + } + let locale = selectLocale(selected); + this.get('intl').setLocale(locale); + this.set('selectedLanguage', locale); + Ember.$.cookie('lang', locale); + + return true; + } }, model: function() { diff --git a/www/app/templates/about-ko.hbs b/www/app/templates/about-ko.hbs new file mode 100644 index 000000000..ef5a4aa0d --- /dev/null +++ b/www/app/templates/about-ko.hbs @@ -0,0 +1,22 @@ +
+ +

이용약관

+

이곳에서 사용하고 있는 자유 소프트웨어 때문에 발생가능할 수 있는 문제에 대해서 보증하지 않습니다.
+ 이 풀을 이용하는 사용자는 이로 인하여 발생할 수 있는 문제에 대하여 인정하는 것으로 간주합니다.
+ 풀 운영자는 되돌릴 수 없는 손실에 대해 보상할 수 없습니다만, 그렇지 않은 경우에 대해서는 최악의 상황을 막기 위해서 최선을 다하고 있습니다. +

+ +

풀의 지원 사항

+

+

    +
  • Go 언어로 작성되어 매우 빠른 동시처리와 낮은 메모리 사용률을 가집니다
  • +
  • 최상의 성능의 프록시
  • +
  • 출금 및 블럭 unlocking 모듈 지원
  • +
  • 100% 분산 환경으로 디자인 됨
  • +
  • Strict policy 정책 모듈 지원
  • +
  • 아름다운 현대적 Ember.js 프론트엔드 사용
  • +
+

+
diff --git a/www/app/templates/account.hbs b/www/app/templates/account.hbs index 2f67af466..e80dd6345 100644 --- a/www/app/templates/account.hbs +++ b/www/app/templates/account.hbs @@ -4,40 +4,40 @@
- Immature Balance: {{format-balance model.stats.immature}}
- Preliminary balance awaiting blocks to mature. + {{t "account.immature.balance"}}: {{format-balance model.stats.immature}}
+ {{t "account.immature.description"}}
- Pending Balance: {{format-balance model.stats.balance}}
- Credited coins awaiting payout. + {{t "account.pending.balance"}}: {{format-balance model.stats.balance}}
+ {{t "account.pending.description"}}
{{#if model.stats.pending}}
- Current Payment: {{format-balance model.stats.pending}}
+ {{t "account.current"}}: {{format-balance model.stats.pending}}
{{/if}} -
Total Paid: {{format-balance model.stats.paid}}
+
{{t "account.total.paid"}}: {{format-balance model.stats.paid}}
{{#if model.stats.lastShare}}
- Last Share Submitted: {{format-relative (seconds-to-ms (string-to-int model.stats.lastShare))}} + {{t "account.last_share_submitted"}}: {{format-relative (seconds-to-ms (string-to-int model.stats.lastShare))}}
{{/if}} -
Workers Online: {{format-number model.workersOnline}}
-
Hashrate (30m): {{format-hashrate model.currentHashrate}}
-
Hashrate (3h): {{format-hashrate model.hashrate}}
+
{{t "account.online"}}: {{format-number model.workersOnline}}
+
{{t "account.hashrate"}} (30m): {{format-hashrate model.currentHashrate}}
+
{{t "account.hashrate"}} (3h): {{format-hashrate model.hashrate}}
-
Blocks Found: {{format-number model.stats.blocksFound fallback='0'}}
-
Total Payments: {{format-number model.paymentsTotal}}
+
{{t "account.blocks.found"}}: {{format-number model.stats.blocksFound fallback='0'}}
+
{{t "account.total.payments"}}: {{format-number model.paymentsTotal}}
- Your Round Share: {{format-number roundPercent style='percent' maximumFractionDigits='6'}}
- Percent of your contribution to current round. + {{t "account.round_share"}}: {{format-number roundPercent style='percent' maximumFractionDigits='6'}}
+ {{t "account.round_share_description"}}
- Epoch Switch: {{format-relative applicationController.nextEpoch units="hour"}} + {{t "account.epoch_switch"}}: {{format-relative applicationController.nextEpoch units="hour"}}
@@ -47,10 +47,10 @@
diff --git a/www/app/templates/account/index.hbs b/www/app/templates/account/index.hbs index 4fb7975f1..d42821475 100644 --- a/www/app/templates/account/index.hbs +++ b/www/app/templates/account/index.hbs @@ -1,14 +1,14 @@
{{#if model.workers}} -

Your Workers

+

{{t "account.your_workers"}}

- - - + + + @@ -24,16 +24,13 @@
IDHashrate (rough, short average)Hashrate (accurate, long average)Last Share{{t "account.hashrate"}} ({{t "account.short_average"}}){{t "account.hashrate"}} ({{t "account.long_average"}}){{t "account.last_share"}}
{{else}} -

No workers online

+

{{t "account.no_workers_online"}}

{{/if}}
diff --git a/www/app/templates/account/payouts.hbs b/www/app/templates/account/payouts.hbs index 7ef5b7f88..9341bd2ad 100644 --- a/www/app/templates/account/payouts.hbs +++ b/www/app/templates/account/payouts.hbs @@ -1,13 +1,13 @@
{{#if model.payments}} -

Your Latest Payouts

+

{{t "payout.latest_payouts"}}

- - - + + + @@ -15,7 +15,7 @@ @@ -24,6 +24,6 @@
TimeTx IDAmount{{t "payout.time"}}{{t "payout.txid"}}{{t "payout.amount"}}
{{format-date-locale tx.timestamp}} - {{tx.tx}} + {{tx.tx}} {{format-balance tx.amount}}
{{else}} -

No payouts yet

+

{{t "payout.no_payouts_yet"}}

{{/if}}
diff --git a/www/app/templates/application.hbs b/www/app/templates/application.hbs index a85d241ab..029e01054 100644 --- a/www/app/templates/application.hbs +++ b/www/app/templates/application.hbs @@ -8,23 +8,23 @@ - ΞthereumPool + {{config.PoolName}} Pool diff --git a/www/app/templates/blocks.hbs b/www/app/templates/blocks.hbs index e062465ae..688a44b42 100644 --- a/www/app/templates/blocks.hbs +++ b/www/app/templates/blocks.hbs @@ -1,10 +1,9 @@
-

Pool always pay full block reward including TX fees and uncle rewards.

- - Block maturity requires up to 520 blocks. - Usually it's less indeed. - +

{{t "block.pool_rewards"}}

+ + {{format-html-message "block.pool_notice.html" success=520}} +
@@ -13,13 +12,13 @@ {{/if}} {{outlet}} diff --git a/www/app/templates/blocks/block.hbs b/www/app/templates/blocks/block.hbs index 747b96c89..228fe6e37 100644 --- a/www/app/templates/blocks/block.hbs +++ b/www/app/templates/blocks/block.hbs @@ -1,18 +1,18 @@ {{#if block.uncle}} - {{format-number block.height}} + {{format-number block.height}} {{else}} - {{format-number block.height}} + {{format-number block.height}} {{/if}} {{#if block.uncle}} - {{block.hash}} + {{block.hash}} {{else if block.orphan}} - Orphan + {{t "block.orphan"}} {{else}} - {{block.hash}} + {{block.hash}} {{/if}} {{format-date-locale block.timestamp}} diff --git a/www/app/templates/blocks/immature.hbs b/www/app/templates/blocks/immature.hbs index 5552d4d82..2784b784f 100644 --- a/www/app/templates/blocks/immature.hbs +++ b/www/app/templates/blocks/immature.hbs @@ -1,15 +1,15 @@ {{#if model.immature}} -

Immature Blocks

+

{{t "block.immature_blocks"}}

- - - - - + + + + + @@ -20,5 +20,5 @@
HeightBlock HashTime FoundVarianceReward{{t "block.height"}}{{t "block.hash"}}{{t "block.time_found"}}{{t "block.variance"}}{{t "block.reward"}}
{{else}} -

No immature blocks yet

+

{{t "block.no_immature_blocks_yet"}}

{{/if}} diff --git a/www/app/templates/blocks/index.hbs b/www/app/templates/blocks/index.hbs index e1a833535..6b49fb3db 100644 --- a/www/app/templates/blocks/index.hbs +++ b/www/app/templates/blocks/index.hbs @@ -1,14 +1,14 @@ {{#if model.matured}} -

Matured Blocks

+

{{t "block.matured"}}

- - - - - + + + + + @@ -19,5 +19,5 @@
HeightBlock HashTime FoundVarianceReward{{t "block.height"}}{{t "block.hash"}}{{t "block.time_found"}}{{t "block.variance"}}{{t "block.reward"}}
{{else}} -

No matured blocks yet

+

{{t "block.no_matured_blocks_yet"}}

{{/if}} diff --git a/www/app/templates/blocks/pending.hbs b/www/app/templates/blocks/pending.hbs index 486abc598..967717a4a 100644 --- a/www/app/templates/blocks/pending.hbs +++ b/www/app/templates/blocks/pending.hbs @@ -1,18 +1,18 @@ {{#if model.candidates}} -

Recently Found Blocks

+

{{t "block.recently_found_blocks"}}

- - - + + + {{#each model.candidates as |block|}} - +
HeightTime FoundVariance{{t "block.height"}}{{t "block.time_found"}}{{t "block.variance"}}
{{format-number block.height}}{{format-number block.height}} {{format-date-locale block.timestamp}} {{#if block.isLucky}} @@ -27,5 +27,5 @@
{{else}} -

No new blocks yet

+

{{t "block.no_new_blocks_yet"}}

{{/if}} diff --git a/www/app/templates/help-ko.hbs b/www/app/templates/help-ko.hbs new file mode 100644 index 000000000..559a13fda --- /dev/null +++ b/www/app/templates/help-ko.hbs @@ -0,0 +1,90 @@ +
+ +

GPU 마이너 프로그램

+ +

다음의 GPU 마이너 프로그램중 하나를 다운로드하세요. +

+

+ +

지갑 주소를 준비합니다

+
+

geth 사용하기

+
    +
  • cmd.exe를 사용하는 윈도우 명령행 실행 (커맨드 쉘) > geth account new
  • +
  • 유닉스/리눅스 쉘 $ geth account new
  • +
+ +

온라인 지갑 사용하기

+
    +
  • {{format-html-message "wallet.online_html"}}
  • +
+ +

지갑 Dapp (aka. Mist)

+
    +
  • {{format-html-message "wallet.dapp_html"}}
  • +
+
+ +

사용 예제

+
+
0x0000000000000000000000000000000000000000
+
출금을 위한 지갑 주소
+ 예: 0x8b92c50e1c39466f900a578edb20a49356c4fe24. +
+
your-worker-1
your-worker-2
+
+ PC/마이닝리그를 다른 것과 구별하기 위한 ID. 하나밖에 없다면 생략해도 됩니다.
+ 반드시 알파벳/숫자와 추가적으로 대시와 밑줄을 사용할 수 있습니다.
+ 예: worker-1 +
+
+ +

Claymore 예시

+

Claymore 듀얼 마이너 다운로드 (AMD/NVIDIA): [ANN] Bitcointalk.org

+

+ EthDcrMiner64 -epool {{config.StratumHost}}:{{config.StratumPort}} -esm 0 -ewal 0x0000000000000000000000000000000000000000 -eworker your-worker-1 -allcoins 1 -allpools 1 +

+
    +
  • EthDcrMiner64 - 윈도우상의 마이너 실행파일 이름. 리눅스/우분투의 경우 ./ethdcrminer64.
  • +
  • {{config.StratumHost}} - Stratum Server name
  • +
  • -esm 0{{config.StratumPort}} 포트번호는 Stratum 서버의 경우.
  • +
  • -esm 3{{config.NicehashPort}} 포트번호는 Nicehash 지원 서버의 경우.
  • +
  • {{config.StratumPort}} - Stratum Port number
  • +
  • 0x0000000000000000000000000000000000000000 - Your wallet address
  • +
  • your-worker-1 - Your worker name
  • +
+ +

Ethminer 예시

+

+ Ethminer 다운로드 (AMD/NVIDIA): Ethminer 소스/바이너리 at Github +

+
Stratum 방식
+

+ ethminer -SP 1 -U -S {{config.StratumHost}}:{{config.StratumPort}} -O 0x0000000000000000000000000000000000000000.your-worker-1 --farm-recheck 2000 +

+
    +
  • -SP 1 - Stratum 서버일 경우
  • +
  • -U - NVIDIA GPU 의 경우 사용. AMD GPU의 경우는 -G.
  • +
  • -S {{config.StratumHost}}:{{config.StratumPort}} - stratum_server_name:stratum_port_number
  • +
  • -O 0x0000000000000000000000000000000000000000.your-worker-1 - your_wallet_address.your_worker_name
  • +
  • --farm-recheck 2000 - 작업이 바뀔 때마다 2000ms간격을 두고 체크한다. (기본값 500ms. Stratum의 경우 2000ms으로 해야 안정적. eth-proxy의 경우 적은 값 사용)
  • +
+
레거시 HTTP 방식
+

+ ethminer -U -F {{config.HttpHost}}:{{config.HttpPort}}/0x0000000000000000000000000000000000000000/your-worker-1 --farm-recheck 200 +

+
    +
  • -U - NVIDIA GPU 의 경우 사용. AMD GPU의 경우는 -G.
  • +
  • -F {{config.HttpHost}}:{{config.HttpPort}}/0x0000000000000000000000000000000000000000/your-worker-1 +
    • 레거시 HTTP 방식. http_server_name:http_port_number
    • +
    • 0x0000000000000000000000000000000000000000/your-worker-1 - your_wallet_address.your_worker_name
    • +
    +
  • +
+ +
diff --git a/www/app/templates/help-minimal-ko.hbs b/www/app/templates/help-minimal-ko.hbs new file mode 100644 index 000000000..e9e5471f8 --- /dev/null +++ b/www/app/templates/help-minimal-ko.hbs @@ -0,0 +1,16 @@ +
+

Claymore 듀얼 마이너

+

+ EthDcrMiner64 -epool {{config.StratumHost}}:{{config.StratumPort}} -esm 0 -ewal 0x0000000000000000000000000000000000000000 -eworker your-worker-1 -allcoins 1 -allpools 1 +

+

Hint 리눅스/우분투의 경우 ./ethdcrminer64 명령 사용
+

+
+ +
+

Ethminer

+

+ ethminer -U -F {{config.HttpHost}}:{{config.HttpPort}}/0x0000000000000000000000000000000000000000/your-worker-1 --farm-recheck 200 +

+

Hint -U 옵션은 NVIDIA GPU 사용, -G 옵션은 AMD GPU 사용

+
diff --git a/www/app/templates/help-minimal.hbs b/www/app/templates/help-minimal.hbs new file mode 100644 index 000000000..957390542 --- /dev/null +++ b/www/app/templates/help-minimal.hbs @@ -0,0 +1,16 @@ +
+

Claymore Dual Miner

+

+ EthDcrMiner64 -epool {{config.StratumHost}}:{{config.StratumPort}} -esm 0 -ewal 0x0000000000000000000000000000000000000000 -eworker your-worker-1 -allcoins 1 -allpools 1 +

+

Hint use ./ethdcrminer64 under Linux/ubuntu
+

+
+ +
+

Ethminer

+

+ ethminer -U -F {{config.HttpHost}}:{{config.HttpPort}}/0x0000000000000000000000000000000000000000/your-worker-1 --farm-recheck 200 +

+

Hint use -U option for NVIDIA GPU, -G option for AMD GPU.

+
diff --git a/www/app/templates/help.hbs b/www/app/templates/help.hbs index f2041e852..91fef23b2 100644 --- a/www/app/templates/help.hbs +++ b/www/app/templates/help.hbs @@ -1,43 +1,88 @@
-

In order to mine on this pool you need to have an - ethminer installation - pointed to
{{config.HttpHost}}:{{config.HttpPort}}/YOUR_ETH_ADDRESS/RIG_ID +

GPU Miner Softwares

+ +

Download one of the following GPU miners. +

+ +

Prepare Wallet Address

+
+

Using geth

+
    +
  • Unix/Linux shell $ geth account new
  • +
  • Windows command line using cmd.exe (command shell) > geth account new
  • +
+ +

Using online wallet

+
    +
  • {{format-html-message "wallet.online_html"}}
  • +
+ +

Wallet Dapp (aka. Mist)

+
    +
  • Wallet dapp: {{format-html-message "wallet.dapp_html"}}
  • +
+
+ +

Usage examples

-
YOUR_ETH_ADDRESS
-
This is your address for payouts, generate one with geth, or mine directly to exchange like - Poloniex - or Bittrex.
- Example: 0xb85150eb365e7df0941f0cf08235f987ba91506a. +
0x0000000000000000000000000000000000000000
+
This is your address for payouts
+ Example: 0x8b92c50e1c39466f900a578edb20a49356c4fe24.
-
RIG_ID
+
your-worker-1
your-worker-2
- ID of your farm to distinguish it from your other rig. If you have just one rig, feel free to omit this param. + 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.
This param must be short alphanumeric string with optional dashes and underscores.
- Example: rig-1 + Example: worker-1
-

- Full example: - ethminer -F {{config.HttpHost}}:{{config.HttpPort}}/0xb85150eb365e7df0941f0cf08235f987ba91506a/myfarm -G --farm-recheck 200.
- Hint: If you are compiling ethminer from latest source, please also use - extra --disable-submit-hashrate option. -

-

Stratum Mining with Stratum Proxy for Ethereum

-

Grab proxy from eth-proxy GitHub repo.

-

Edit eth-proxy.conf and specify our pool's HOST: {{config.StratumHost}}, PORT: {{config.StratumPort}} and your WALLET.

+

Claymore Example

+

Download Claymore Dual Miner (AMD/NVIDIA): [ANN] Bitcointalk.org

+

+ EthDcrMiner64 -epool {{config.StratumHost}}:{{config.StratumPort}} -esm 0 -ewal 0x0000000000000000000000000000000000000000 -eworker your-worker-1 -allcoins 1 -allpools 1 +

+
    +
  • EthDcrMiner64 - executable name under Windows. use ./ethdcrminer64 under Linux/Ubuntu
  • +
  • {{config.StratumHost}} - Stratum Server name
  • +
  • use -esm 0 and {{config.StratumPort}} port number for Stratum Server.
  • +
  • use -esm 3 and {{config.NicehashPort}} port number for Nicehash Server.
  • +
  • 0x0000000000000000000000000000000000000000 - Your wallet address
  • +
  • your-worker-1 - Your worker name
  • +
-

Mining with Ether-Proxy

-

Use stable release of Ethereum Solo/Pool Mining Proxy.

- -

Advice

-

CPU mining is not recommended.

-

Terms of Service

-

By using the pool you accept all possible risks related to experimental software usage.
- Pool owner can't compensate any irreversible losses, but will do his best to prevent worst case. -

+

Ethminer Examples

+

+ Download Ethminer (AMD/NVIDIA): Ethminer source/binary at Github +

+
Stratum method
+

+ ethminer -SP 1 -U -S {{config.StratumHost}}:{{config.StratumPort}} -O 0x0000000000000000000000000000000000000000.your-worker-1 --farm-recheck 2000 +

+
    +
  • -SP 1 - option for Stratum server
  • +
  • -U - NVIDIA GPU or -G for AMD GPU
  • +
  • -S {{config.StratumHost}}:{{config.StratumPort}} - stratum_server_name:stratum_port_number
  • +
  • -O 0x0000000000000000000000000000000000000000.your-worker-1 - your_wallet_address.your_worker_name
  • +
  • --farm-recheck 2000 - Leave 2000 ms between checks for changed work (default 500ms. use higher value to use stratum for stability)
  • +
+
Legacy HTTP method
+

+ ethminer -U -F {{config.HttpHost}}:{{config.HttpPort}}/0x0000000000000000000000000000000000000000/your-worker-1 --farm-recheck 200 +

+
    +
  • -U - NVIDIA GPU or -G for AMD GPU
  • +
  • -F {{config.HttpHost}}:{{config.HttpPort}}/0x0000000000000000000000000000000000000000/your-worker-1 +
    • Legacy HTTP method. http_server_name:http_port_number
    • +
    • 0x0000000000000000000000000000000000000000/your-worker-1 - your_wallet_address.your_worker_name
    • +
    +
  • +
diff --git a/www/app/templates/index.hbs b/www/app/templates/index.hbs index 4490ea1aa..8aa5b61b3 100644 --- a/www/app/templates/index.hbs +++ b/www/app/templates/index.hbs @@ -3,24 +3,24 @@

- Open Ethereum Pool + {{config.PoolTitle}}

- Min. payout threshold: {{config.PayoutThreshold}}, Payouts run twice per day.
- PROP Stable and profitable pool with regular payouts. + {{t "home.min_payout_threshold"}}: {{config.PayoutThreshold}} {{config.Unit}} / {{t "home.payouts_run" interval=config.PayoutInterval}}
+ PROP {{t "home.payout_scheme_detail"}}
-
Miners Online: {{format-number stats.model.minersTotal}}
-
Pool Hash Rate: {{format-hashrate stats.model.hashrate}}
-
Pool Fee: {{config.PoolFee}}
+
{{t "home.miners_online"}}: {{format-number stats.model.minersTotal}}
+
{{t "home.pool_hashrate"}}: {{format-hashrate stats.model.hashrate}}
+
{{t "home.pool_fee"}}: {{config.PoolFee}}
{{#if stats.model.stats.lastBlockFound}} -
Last Block Found: {{format-relative (seconds-to-ms stats.model.stats.lastBlockFound)}}
+
{{t "home.last_block_found"}}: {{format-relative (seconds-to-ms stats.model.stats.lastBlockFound)}}
{{/if}}
-
Network Difficulty: {{with-metric-prefix stats.difficulty}}
-
Network Hash Rate: {{format-hashrate stats.hashrate}}
-
Blockchain Height: {{format-number stats.height}}
-
Current Round Variance: {{format-number stats.roundVariance style='percent'}}
+
{{t "home.network_difficulty"}}: {{with-metric-prefix stats.difficulty}}
+
{{t "home.network_hashrate"}}: {{format-hashrate stats.hashrate}}
+
{{t "home.blockchain_height"}}: {{format-number stats.height}}
+
{{t "home.current_round_variance"}}: {{format-number stats.roundVariance style='percent'}}
@@ -28,20 +28,16 @@
-

Your Stats & Payment History

+

{{t "home.query_history"}}

- {{input value=cachedLogin class="form-control" placeholder="Enter Your Ethereum Address"}} + {{input value=cachedLogin class="form-control" placeholder=(t "home.input.enter_your_wallet_address")}}
-
-
-

- ethminer.exe -F {{config.HttpHost}}:{{config.HttpPort}}/<address>/<worker> -G -

-
+ + {{partial (t "home.help.minimal")}}
diff --git a/www/app/templates/luck.hbs b/www/app/templates/luck.hbs index 56a1c8220..14e5ad029 100644 --- a/www/app/templates/luck.hbs +++ b/www/app/templates/luck.hbs @@ -2,10 +2,10 @@ - - - - + + + + diff --git a/www/app/templates/miners.hbs b/www/app/templates/miners.hbs index 1017826b7..1b2f52490 100644 --- a/www/app/templates/miners.hbs +++ b/www/app/templates/miners.hbs @@ -1,19 +1,19 @@
-

Total hashrate: {{format-hashrate model.hashrate}}.

- Total miners: {{model.minersTotal}} +

{{t "miners.total_hashrate"}}: {{format-hashrate model.hashrate}}.

+ {{t "miners.total_miners"}}: {{model.minersTotal}}
{{#if model.miners}} -

Miners

+

{{t "miners.miners"}}

BlocksShares/DiffUncle RateOrphan Rate{{t "luck.blocks"}}{{t "luck.shares_diff"}}{{t "luck.uncle_rate"}}{{t "luck.orphan_rate"}}
- - - + + + @@ -28,6 +28,6 @@
LoginHashrateLast Beat{{t "miners.login"}}{{t "miners.hashrate"}}{{t "miners.last_beat"}}
{{else}} -

No miners

+

{{t "miners.no_miners"}}

{{/if}} diff --git a/www/app/templates/payments.hbs b/www/app/templates/payments.hbs index a34d558cd..58d10cba7 100644 --- a/www/app/templates/payments.hbs +++ b/www/app/templates/payments.hbs @@ -1,20 +1,20 @@
-

Pool always pay tx fees from it's own pocket for now.

- Total payments sent: {{model.paymentsTotal}} +

{{t "payments.pay_tx"}}

+ {{t "payments.total_payments_sent"}}: {{model.paymentsTotal}}
{{#if model.payments}} -

Latest Payouts

+

{{t "payments.latest_payouts"}}

- - - - + + + + @@ -23,10 +23,10 @@ {{/each}} @@ -34,6 +34,6 @@
TimeAmountAddressTx ID{{t "payments.time"}}{{t "payments.amount"}}{{t "payments.address"}}{{t "payments.txid"}}
{{format-date-locale tx.timestamp}} {{format-number tx.formatAmount}} - {{tx.address}} + {{tx.address}} - {{format-tx tx.tx}} + {{format-tx tx.tx}}
{{else}} -

No payouts yet

+

{{t "payments.no_payouts_yet"}}

{{/if}}
diff --git a/www/config/environment.js b/www/config/environment.js index c3fcf8a9a..9d91e6e5a 100644 --- a/www/config/environment.js +++ b/www/config/environment.js @@ -14,6 +14,10 @@ module.exports = function(environment) { }, APP: { + // PoolName + PoolName: 'Ethereum', + // PoolTitle + PoolTitle: 'Open Ethereum Pool', // API host and port ApiUrl: '//example.net/', @@ -27,9 +31,17 @@ module.exports = function(environment) { // Fee and payout details PoolFee: '1%', - PayoutThreshold: '0.5 Ether', + PayoutThreshold: '1', + PayoutInterval: '2m', + Unit: 'ETH', + EtherUnit: 'ETH', // For network hashrate (change for your favourite fork) + BlockExplorerLink: 'https://myexplorer.net', + BlockExplorerAddrLink: 'https://myexplorer.net/addr', + DonationLink: false, + DonationAddress: '', + BlockReward: 5, BlockTime: 14.4 } }; diff --git a/www/translations/en-us.yaml b/www/translations/en-us.yaml index 5c7ef82c0..d7aed9372 100644 --- a/www/translations/en-us.yaml +++ b/www/translations/en-us.yaml @@ -3,3 +3,129 @@ product: title: 'Hello world!' html: info: '{product} will cost {price, number, USD} if ordered by {deadline, date, time}' + +menu: + home: Home + help: Help + pool_blocks: Pool Blocks + payments: Payments + miners: Miners + about: About + i18n: + about: about + help: help + language: language + +lang: + korean: Korean + english: English + +home: + min_payout_threshold: Min. payout threshold + payouts_run: Payouts run every {interval}. + payout_scheme_detail: Stable and profitable pool with regular payouts. + miners_online: Miners Online + pool_hashrate: Pool Hash Rate + pool_fee: Pool Fee + last_block_found: Last Block Found + network_difficulty: Network Difficulty + network_hashrate: Network Hash Rate + blockchain_height: Blockchain Height + current_round_variance: Current Round Variance + + query_history: Your Stats and Payment History + input: + enter_your_wallet_address: Enter Your Wallet Address + + button: + lookup: Lookup + + help: + minimal: help-minimal + +account: + immature: + balance: Immature Balance + description: Preliminary balance awaiting blocks to mature. + pending: + balance: Pending Balance + description: Credited coins awaiting payout. + current: Current Payment + last_share_submitted: Last Share Submitted + online: Workers Online + hashrate: Hashrate + blocks: + found: Blocks Found + total: + payments: Total Payments + paid: Total Paid + round_share: Your Round Share + round_share_description: Percent of your contribution to current round. + epoch_switch: Epoch Switch + workers: Workers + payouts: Payouts + + your_workers: Workers + last_share: Last Share + short_average: rough, short average + long_average: accurate, long average + no_workers_online: No workers online + notice: Notice + notice_html: Your average hashrate will be smoothly adjusted until you have shares to fullfill estimation window.
There are two windows, long and short, first is equal to about 30 minutes and long window is usually equal to 3 hours.
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. + json_api_url: Your bulk stats JSON API URL + +payout: + latest_payouts: Your Latest Payouts + time: Time + txid: Tx ID + amount: Amount + no_payouts_yet: No payouts yet + +block: + pool_rewards: Pool always pay full block reward including TX fees and uncle rewards. + pool_notice: + html: Block maturity requires up to {success} blocks. Usually it's less indeed. + blocks: Blocks + immature: Immature + new: New Blocks + orphan: Orphan + no_matured_blocks_yet: No matured blocks yet + reward: Reward + height: Height + hash: Hash + time_found: Time Found + variance: Share Variance (Shares/Diff) + matured: Matured Blocks + immature_blocks: Immature Blocks + no_immature_blocks_yet: No immature blocks yet + no_new_blocks_yet: No new blocks yet + recently_found_blocks: Recently Found Blocks + +luck: + blocks: Blocks + shares_diff: Shares/Diff + uncle_rate: Uncle Rate + orphan_rate: Orphan Rate + +payments: + pay_tx: Pool always pay tx fees from it's own pocket for now. + total_payments_sent: Total payments sent + latest_payouts: Latest Payouts + time: Time + amount: Amount + address: Address + txid: Tx ID + no_payouts_yet: No payouts yet + +miners: + total_hashrate: Total pool hashrate + total_miners: Total miners + login: Login + hashrate: Hashrate + last_beat: Last beat + no_miners: No miners + miners: Miners + +wallet: + dapp_html: https://ethereum.org/ + online_html: https://www.myetherwallet.com Online wallet at myetherwallet.com diff --git a/www/translations/ko.yaml b/www/translations/ko.yaml new file mode 100644 index 000000000..44e1db26f --- /dev/null +++ b/www/translations/ko.yaml @@ -0,0 +1,130 @@ +product: + info: '{product} will cost {price, number, USD} if ordered by {deadline, date, time}' + title: 'Hello world!' + html: + info: '{product} will cost {price, number, USD} if ordered by {deadline, date, time}' + +menu: + home: 홈 + help: 도움말 + pool_blocks: 블록 정보 + payments: 지급현황 + miners: 채굴현황 + about: 이곳은.. + i18n: + about: about-ko + help: help-ko + language: 언어 + +lang: + korean: 한국어 + english: English + +home: + min_payout_threshold: 최소 지급량 + payouts_run: 지급은 매 {interval} 간격으로 이루어집니다. + payout_scheme_detail: 정기적인 지급으로 안정적이고 수익성있는 풀입니다. + 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-ko + +account: + immature: + balance: 미승인 잔고 + description: 블록이 성숙되기를 기다리는 미승인 잔고입니다. + pending: + balance: 지급대기 잔고 + description: 지급 대기중인 잔고입니다. + current: 현 출금량 + last_share_submitted: 마지막 쉐어 제출 + online: 작동중인 워커 + hashrate: 해시량 + blocks: + found: 발견한 블록 + total: + payments: 총 출금회수 + paid: 총 출금량 + round_share: 라운드 쉐어율 + round_share_description: 현재 라운드에 대한 쉐어 비율. + epoch_switch: 에폭 전환 + workers: 워커현황 + payouts: 출금내역 + + your_workers: 워커 목록 + last_share: 마지막 쉐어 + short_average: 짧은 구간 평균 + long_average: 긴구간 평균 + no_workers_online: 작동중인 워커가 없습니다 + notice: 주의사항 + notice_html: 해시 평균값은 평가 구간안에서 쉐어가 충분히 제출되면 매끄럽게 조정됩니다.
여기서 두가지의 평가구간, 긴 구간과 짧은 구간이 있는데, 짧은 구간은 30분, 긴 구간은 3시간 구간입니다.
문제있는 워커는 짧은 구간에서 1/2의 쉐어가 제출되지 않는 경우이며 강조되어 표시되고, 해당 워커를 확인하셔야 할 필요가 있습니다. + json_api_url: 워커 상태에 대한 JSON API 링크 + +payout: + latest_payouts: 최근 출금 기록 + time: 시간 + txid: 트랜젝션 ID + amount: 출금량 + no_payouts_yet: 출금기록이 아직 없습니다 + +block: + pool_rewards: 이 풀은 TX 수수료 및 엉클 블록에 대한 모든 블록 보상을 하고 있습니다. + pool_notice: + html: 블록 확정은 최대 {success} 블록이 발견되어야 하며, 보통은 그 이전에 확정됩니다. + 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: 엉클 비율 + orphan_rate: 외토리 비율 + +payments: + pay_tx: 현재 전송 수수료를 이 풀에서 지급하고 있습니다. + total_payments_sent: 전체 출금건수 + latest_payouts: 최근 출금 내역 + time: 시간 + amount: 수량 + address: 주소 + txid: 전송 ID + no_payouts_yet: 출금이 아직 없습니다 + +miners: + total_hashrate: 전체 풀 해시 + total_miners: 접속중인 마이너 + login: 계정/지갑주소 + hashrate: 해시량 + last_beat: 최근 작동시간 + no_miners: 마이너가 없습니다 + miners: 마이너 현황 + +wallet: + dapp_html: https://ethereum.org/ + online_html: https://www.myetherwallet.com myetherwallet.com에서 제공하는 온라인 지갑 From 49e571b3ea151288d04e94282d0354512e14e49e Mon Sep 17 00:00:00 2001 From: hackyminer Date: Thu, 19 Apr 2018 07:11:33 +0900 Subject: [PATCH 07/30] support earnings per day --- www/app/controllers/account.js | 15 +++++++++++++++ www/app/controllers/account/index.js | 6 ++++++ www/app/controllers/account/payouts.js | 6 ++++++ www/app/controllers/blocks.js | 6 ++++++ www/app/controllers/blocks/block.js | 6 ++++++ www/app/controllers/blocks/immature.js | 6 ++++++ www/app/controllers/blocks/index.js | 6 ++++++ www/app/controllers/blocks/pending.js | 6 ++++++ www/app/controllers/help-ko.js | 6 ++++++ www/app/controllers/payments.js | 6 ++++++ www/app/helpers/worker-earnperday.js | 8 ++++++++ www/app/templates/account.hbs | 1 + www/app/templates/account/index.hbs | 2 ++ www/translations/en-us.yaml | 3 +++ www/translations/ko.yaml | 3 +++ 15 files changed, 86 insertions(+) create mode 100644 www/app/controllers/account/index.js create mode 100644 www/app/controllers/account/payouts.js create mode 100644 www/app/controllers/blocks.js create mode 100644 www/app/controllers/blocks/block.js create mode 100644 www/app/controllers/blocks/immature.js create mode 100644 www/app/controllers/blocks/index.js create mode 100644 www/app/controllers/blocks/pending.js create mode 100644 www/app/controllers/help-ko.js create mode 100644 www/app/controllers/payments.js create mode 100644 www/app/helpers/worker-earnperday.js diff --git a/www/app/controllers/account.js b/www/app/controllers/account.js index 79782f798..66687b6a1 100644 --- a/www/app/controllers/account.js +++ b/www/app/controllers/account.js @@ -3,6 +3,8 @@ import Ember from 'ember'; export default Ember.Controller.extend({ applicationController: Ember.inject.controller('application'), stats: Ember.computed.reads('applicationController.model.stats'), + config: Ember.computed.reads('applicationController.config'), + hashrate: Ember.computed.reads('applicationController.hashrate'), roundPercent: Ember.computed('stats', 'model', { get() { @@ -12,5 +14,18 @@ export default Ember.Controller.extend({ } 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'); + } }) }); diff --git a/www/app/controllers/account/index.js b/www/app/controllers/account/index.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/account/index.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/account/payouts.js b/www/app/controllers/account/payouts.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/account/payouts.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/blocks.js b/www/app/controllers/blocks.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/blocks.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/blocks/block.js b/www/app/controllers/blocks/block.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/blocks/block.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/blocks/immature.js b/www/app/controllers/blocks/immature.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/blocks/immature.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/blocks/index.js b/www/app/controllers/blocks/index.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/blocks/index.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/blocks/pending.js b/www/app/controllers/blocks/pending.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/blocks/pending.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/help-ko.js b/www/app/controllers/help-ko.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/help-ko.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/controllers/payments.js b/www/app/controllers/payments.js new file mode 100644 index 000000000..12b7f9a34 --- /dev/null +++ b/www/app/controllers/payments.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + applicationController: Ember.inject.controller('application'), + config: Ember.computed.reads('applicationController.config') +}); diff --git a/www/app/helpers/worker-earnperday.js b/www/app/helpers/worker-earnperday.js new file mode 100644 index 000000000..898374271 --- /dev/null +++ b/www/app/helpers/worker-earnperday.js @@ -0,0 +1,8 @@ +import Ember from 'ember'; +import config from '../config/environment'; + +export default Ember.Helper.extend({ + compute(hashrates) { + return 24 * 60 * 60 / config.APP.BlockTime * (hashrates[0] / hashrates[1]) * config.APP.BlockReward; + } +}); diff --git a/www/app/templates/account.hbs b/www/app/templates/account.hbs index e80dd6345..0187b7a0a 100644 --- a/www/app/templates/account.hbs +++ b/www/app/templates/account.hbs @@ -27,6 +27,7 @@
{{t "account.online"}}: {{format-number model.workersOnline}}
{{t "account.hashrate"}} (30m): {{format-hashrate model.currentHashrate}}
{{t "account.hashrate"}} (3h): {{format-hashrate model.hashrate}}
+
{{t "account.earnings.perday"}} (3h avg): {{format-number earnPerDay}} {{config.Unit}}
{{t "account.blocks.found"}}: {{format-number model.stats.blocksFound fallback='0'}}
diff --git a/www/app/templates/account/index.hbs b/www/app/templates/account/index.hbs index d42821475..a6e8ce502 100644 --- a/www/app/templates/account/index.hbs +++ b/www/app/templates/account/index.hbs @@ -8,6 +8,7 @@ ID {{t "account.hashrate"}} ({{t "account.short_average"}}) {{t "account.hashrate"}} ({{t "account.long_average"}}) + {{t "account.earnings.perday"}} ({{t "account.earnings.short_avg"}}) {{t "account.last_share"}} @@ -17,6 +18,7 @@ {{k}} {{format-hashrate v.hr}} {{format-hashrate v.hr2}} + {{format-number (worker-earnperday v.hr netstats.hashrate)}} {{format-relative (seconds-to-ms v.lastBeat)}} {{/each-in}} diff --git a/www/translations/en-us.yaml b/www/translations/en-us.yaml index d7aed9372..ae4e30c92 100644 --- a/www/translations/en-us.yaml +++ b/www/translations/en-us.yaml @@ -54,6 +54,9 @@ account: last_share_submitted: Last Share Submitted online: Workers Online hashrate: Hashrate + earnings: + perday: Earnings per day + short_avg: short avg. blocks: found: Blocks Found total: diff --git a/www/translations/ko.yaml b/www/translations/ko.yaml index 44e1db26f..880d7f84e 100644 --- a/www/translations/ko.yaml +++ b/www/translations/ko.yaml @@ -53,6 +53,9 @@ account: last_share_submitted: 마지막 쉐어 제출 online: 작동중인 워커 hashrate: 해시량 + earnings: + perday: 하루 채굴 예상량 + short_avg: 단기 평균 blocks: found: 발견한 블록 total: From 48d8b985bea2a8edaf3f7ceddcf8dfc9683a9966 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Fri, 20 Apr 2018 08:18:47 +0900 Subject: [PATCH 08/30] fixed select languages menu --- www/app/routes/application.js | 5 +++-- www/app/templates/application.hbs | 18 ++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/www/app/routes/application.js b/www/app/routes/application.js index c2f793391..47cefbd79 100644 --- a/www/app/routes/application.js +++ b/www/app/routes/application.js @@ -45,8 +45,8 @@ export default Ember.Route.extend({ }, actions: { - selectLanguage: function() { - let selected = Ember.$('option:selected').attr('value'); + selectLanguage: function(lang) { + let selected = lang; if (typeof selected === 'undefined') { return true; } @@ -54,6 +54,7 @@ export default Ember.Route.extend({ this.get('intl').setLocale(locale); this.set('selectedLanguage', locale); Ember.$.cookie('lang', locale); + Ember.$('#selectedLanguage').html(locale + ''); return true; } diff --git a/www/app/templates/application.hbs b/www/app/templates/application.hbs index 029e01054..7e5e779b4 100644 --- a/www/app/templates/application.hbs +++ b/www/app/templates/application.hbs @@ -47,16 +47,18 @@ {{/active-li}} - + + +
From 8a53f11a5859acffe78a90d74d5d4f2ba135f30d Mon Sep 17 00:00:00 2001 From: hackyminer Date: Thu, 19 Apr 2018 06:57:54 +0900 Subject: [PATCH 09/30] fixup config.unit --- www/app/templates/account.hbs | 8 ++++---- www/app/templates/account/payouts.hbs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/www/app/templates/account.hbs b/www/app/templates/account.hbs index 0187b7a0a..2d90823df 100644 --- a/www/app/templates/account.hbs +++ b/www/app/templates/account.hbs @@ -4,19 +4,19 @@
- {{t "account.immature.balance"}}: {{format-balance model.stats.immature}}
+ {{t "account.immature.balance"}}: {{format-balance model.stats.immature}} {{config.Unit}}
{{t "account.immature.description"}}
- {{t "account.pending.balance"}}: {{format-balance model.stats.balance}}
+ {{t "account.pending.balance"}}: {{format-balance model.stats.balance}} {{config.Unit}}
{{t "account.pending.description"}}
{{#if model.stats.pending}}
- {{t "account.current"}}: {{format-balance model.stats.pending}}
+ {{t "account.current"}}: {{format-balance model.stats.pending}} {{config.Unit}}
{{/if}} -
{{t "account.total.paid"}}: {{format-balance model.stats.paid}}
+
{{t "account.total.paid"}}: {{format-balance model.stats.paid}} {{config.Unit}}
{{#if model.stats.lastShare}} diff --git a/www/app/templates/account/payouts.hbs b/www/app/templates/account/payouts.hbs index 9341bd2ad..abecbe9f7 100644 --- a/www/app/templates/account/payouts.hbs +++ b/www/app/templates/account/payouts.hbs @@ -17,7 +17,7 @@ {{tx.tx}} - {{format-balance tx.amount}} + {{format-balance tx.amount}} {{config.Unit}} {{/each}} From e4c09af7f21994dbaf17b932120d076ba7cf1537 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Mon, 19 Mar 2018 20:59:27 +0900 Subject: [PATCH 10/30] fixup style --- www/app/styles/app.css | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/www/app/styles/app.css b/www/app/styles/app.css index ab6b45cc5..4cde4b84b 100644 --- a/www/app/styles/app.css +++ b/www/app/styles/app.css @@ -130,6 +130,14 @@ span.logo-3 { font-weight: 200; } +h1, h2, h3, h4, h4, h6 { + margin-top: 0px; +} + +.container>h2, .container>h3 { + margin-top:2.0rem; +} + .note { margin: 0 0 20px 0; padding: 15px 30px 15px 15px; @@ -142,6 +150,12 @@ span.logo-3 { border-color: #57b5e3; } +.note code { + background-color: transparent; + font-size: 16px; + color: #000; +} + .note-danger { background-color: #ff9999; border-color: #ff0000; @@ -176,3 +190,22 @@ h4.note { .stats > div > span:first-of-type{ font-weight: bold; } + +/* tabs */ +.tab-content>.tab-pane { + background-color: #ffffff; + padding: 1em; + border-radius: 0 0 5px 5px; + border: 1px #eeeeee solid; + border-width: 0 1px 1px 1px; +} + +code { + font-size: 100%; + /* color: #444f67; /* */ + background-color: #f7f7f7; +} + +code em { + color: #3b9218; +} From 24e3c8c002bc50a335a7ea7ee3aa12ec4b160031 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Thu, 19 Apr 2018 05:23:12 +0900 Subject: [PATCH 11/30] app: change default font --- www/app/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/www/app/index.html b/www/app/index.html index d161fdc8d..5082c92c9 100644 --- a/www/app/index.html +++ b/www/app/index.html @@ -9,6 +9,7 @@ {{content-for "head"}} + {{content-for "head-footer"}} From a84c4e24a23b3d04c22a3855f07f8feb12e00861 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Thu, 19 Apr 2018 05:23:54 +0900 Subject: [PATCH 12/30] www/app: change icons --- www/app/templates/account.hbs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/www/app/templates/account.hbs b/www/app/templates/account.hbs index 2d90823df..05027496d 100644 --- a/www/app/templates/account.hbs +++ b/www/app/templates/account.hbs @@ -16,15 +16,15 @@ {{t "account.current"}}: {{format-balance model.stats.pending}} {{config.Unit}}
{{/if}} -
{{t "account.total.paid"}}: {{format-balance model.stats.paid}} {{config.Unit}}
+
{{t "account.total.paid"}}: {{format-balance model.stats.paid}} {{config.Unit}}
{{#if model.stats.lastShare}} -
+
{{t "account.last_share_submitted"}}: {{format-relative (seconds-to-ms (string-to-int model.stats.lastShare))}}
{{/if}} -
{{t "account.online"}}: {{format-number model.workersOnline}}
+
{{t "account.online"}}: {{format-number model.workersOnline}}
{{t "account.hashrate"}} (30m): {{format-hashrate model.currentHashrate}}
{{t "account.hashrate"}} (3h): {{format-hashrate model.hashrate}}
{{t "account.earnings.perday"}} (3h avg): {{format-number earnPerDay}} {{config.Unit}}
@@ -33,11 +33,11 @@
{{t "account.blocks.found"}}: {{format-number model.stats.blocksFound fallback='0'}}
{{t "account.total.payments"}}: {{format-number model.paymentsTotal}}
- {{t "account.round_share"}}: {{format-number roundPercent style='percent' maximumFractionDigits='6'}}
+ {{t "account.round_share"}}: {{format-number roundPercent style='percent' maximumFractionDigits='4'}}
{{t "account.round_share_description"}}
- + {{t "account.epoch_switch"}}: {{format-relative applicationController.nextEpoch units="hour"}}
From 5895a277f6fbfce46f1aeff5fc37f0f8bd3a4a8b Mon Sep 17 00:00:00 2001 From: hackyminer Date: Thu, 19 Apr 2018 05:24:21 +0900 Subject: [PATCH 13/30] www/app: fixed api URL --- www/app/templates/account/index.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/app/templates/account/index.hbs b/www/app/templates/account/index.hbs index a6e8ce502..675896c15 100644 --- a/www/app/templates/account/index.hbs +++ b/www/app/templates/account/index.hbs @@ -33,6 +33,6 @@ {{format-html-message "account.notice_html"}}
From 0abfd95d9b55b9210e1b8ce057a38a64e42bdddd Mon Sep 17 00:00:00 2001 From: hackyminer Date: Fri, 20 Apr 2018 04:21:19 +0900 Subject: [PATCH 14/30] www/app: read the external pool settings from /api/settings --- www/app/controllers/blocks.js | 14 +++++++++++++- www/app/controllers/index.js | 33 +++++++++++++++++++++++++++++++++ www/app/routes/application.js | 25 +++++++++++++++++++++++++ www/app/templates/blocks.hbs | 2 +- www/app/templates/index.hbs | 4 ++-- www/config/environment.js | 1 + 6 files changed, 75 insertions(+), 4 deletions(-) diff --git a/www/app/controllers/blocks.js b/www/app/controllers/blocks.js index 12b7f9a34..384e3ac3b 100644 --- a/www/app/controllers/blocks.js +++ b/www/app/controllers/blocks.js @@ -2,5 +2,17 @@ import Ember from 'ember'; export default Ember.Controller.extend({ applicationController: Ember.inject.controller('application'), - config: Ember.computed.reads('applicationController.config') + config: Ember.computed.reads('applicationController.config'), + settings: Ember.computed.reads('applicationController.model.settings'), + + BlockUnlockDepth: Ember.computed('settings', { + get() { + var depth = this.get('settings.BlockUnlockDepth'); + if (depth) { + return depth; + } + return this.get('config').BlockUnlockDepth; + } + }), + }); diff --git a/www/app/controllers/index.js b/www/app/controllers/index.js index 9654c41fd..b112cdb1d 100644 --- a/www/app/controllers/index.js +++ b/www/app/controllers/index.js @@ -4,6 +4,39 @@ export default Ember.Controller.extend({ applicationController: Ember.inject.controller('application'), stats: Ember.computed.reads('applicationController'), config: Ember.computed.reads('applicationController.config'), + settings: Ember.computed.reads('applicationController.model.settings'), + + // try to read some settings from the model.settings + PayoutThreshold: Ember.computed('settings', { + get() { + var threshold = this.get('settings.PayoutThreshold'); + if (threshold) { + // in shannon (10**9) + return threshold / 1000000000; + } + return this.get('config').PayoutThreshold; + } + }), + + PayoutInterval: Ember.computed('settings', { + get() { + var interval = this.get('settings.PayoutInterval'); + if (interval) { + return interval; + } + return this.get('config').PayoutInterval; + } + }), + + PoolFee: Ember.computed('settings', { + get() { + var poolfee = this.get('settings.PoolFee'); + if (poolfee) { + return poolfee + '%'; + } + return this.get('config').PoolFee; + } + }), cachedLogin: Ember.computed('login', { get() { diff --git a/www/app/routes/application.js b/www/app/routes/application.js index 47cefbd79..3d050f90b 100644 --- a/www/app/routes/application.js +++ b/www/app/routes/application.js @@ -28,6 +28,7 @@ function selectLocale(selected) { export default Ember.Route.extend({ intl: Ember.inject.service(), selectedLanguage: null, + poolSettings: null, beforeModel() { let locale = this.get('selectedLanguage'); @@ -42,6 +43,28 @@ export default Ember.Route.extend({ console.log('INFO: locale selected - ' + locale); this.set('selectedLanguage', locale); } + + let settings = this.get('poolSettings'); + if (!settings) { + let self = this; + let url = config.APP.ApiUrl + 'api/settings'; + Ember.$.ajax({ + url: url, + type: 'GET', + header: { + 'Accept': 'application/json' + }, + success: function(data) { + settings = Ember.Object.create(data); + self.set('poolSettings', settings); + console.log('INFO: pool settings loaded..'); + }, + error: function(request, status, e) { + console.log('ERROR: fail to load pool settings: ' + e); + self.set('poolSettings', {}); + } + }); + } }, actions: { @@ -68,6 +91,8 @@ export default Ember.Route.extend({ }, setupController: function(controller, model) { + let settings = this.get('poolSettings'); + model.settings = settings; this._super(controller, model); Ember.run.later(this, this.refresh, 5000); } diff --git a/www/app/templates/blocks.hbs b/www/app/templates/blocks.hbs index 688a44b42..7a0b79fd6 100644 --- a/www/app/templates/blocks.hbs +++ b/www/app/templates/blocks.hbs @@ -2,7 +2,7 @@

{{t "block.pool_rewards"}}

- {{format-html-message "block.pool_notice.html" success=520}} + {{format-html-message "block.pool_notice.html" success=BlockUnlockDepth}}
diff --git a/www/app/templates/index.hbs b/www/app/templates/index.hbs index 8aa5b61b3..5e1649463 100644 --- a/www/app/templates/index.hbs +++ b/www/app/templates/index.hbs @@ -5,13 +5,13 @@

{{config.PoolTitle}}

- {{t "home.min_payout_threshold"}}: {{config.PayoutThreshold}} {{config.Unit}} / {{t "home.payouts_run" interval=config.PayoutInterval}}
+ {{t "home.min_payout_threshold"}}: {{PayoutThreshold}} {{config.Unit}} / {{t "home.payouts_run" interval=PayoutInterval}}
PROP {{t "home.payout_scheme_detail"}}
{{t "home.miners_online"}}: {{format-number stats.model.minersTotal}}
{{t "home.pool_hashrate"}}: {{format-hashrate stats.model.hashrate}}
-
{{t "home.pool_fee"}}: {{config.PoolFee}}
+
{{t "home.pool_fee"}}: {{PoolFee}}
{{#if stats.model.stats.lastBlockFound}}
{{t "home.last_block_found"}}: {{format-relative (seconds-to-ms stats.model.stats.lastBlockFound)}}
{{/if}} diff --git a/www/config/environment.js b/www/config/environment.js index 9d91e6e5a..ed3dcfe13 100644 --- a/www/config/environment.js +++ b/www/config/environment.js @@ -42,6 +42,7 @@ module.exports = function(environment) { DonationLink: false, DonationAddress: '', BlockReward: 5, + BlockUnlockDepth: 120, BlockTime: 14.4 } }; From 5ea9c4cf13628e5ff772ea079d42f807985f3275 Mon Sep 17 00:00:00 2001 From: Andrei Date: Tue, 16 Jan 2018 17:04:06 +0300 Subject: [PATCH 15/30] support Highcharts [manually applied by hackyminer 2018/04/13] Conflicts: www/app/controllers/account.js www/app/templates/index.hbs --- api/server.go | 65 ++++++++++++++++++ config.example.json | 6 +- storage/redis.go | 117 +++++++++++++++++++++++++++++++++ www/app/controllers/account.js | 112 ++++++++++++++++++++++++++++++- www/app/controllers/index.js | 89 ++++++++++++++++++++++++- www/app/templates/account.hbs | 3 + www/app/templates/index.hbs | 4 ++ www/package.json | 2 + 8 files changed, 395 insertions(+), 3 deletions(-) diff --git a/api/server.go b/api/server.go index 26cd62c7c..40c9c399a 100644 --- a/api/server.go +++ b/api/server.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "fmt" "log" "net/http" "sort" @@ -11,6 +12,7 @@ import ( "time" "github.com/gorilla/mux" + "github.com/robfig/cron" "github.com/sammy007/open-ethereum-pool/rpc" "github.com/sammy007/open-ethereum-pool/storage" @@ -20,6 +22,10 @@ import ( type ApiConfig struct { Enabled bool `json:"enabled"` Listen string `json:"listen"` + PoolCharts string `json:"poolCharts"` + PoolChartsNum int64 `json:"poolChartsNum"` + MinerChartsNum int64 `json:"minerChartsNum"` + MinerCharts string `json:"minerCharts"` StatsCollectInterval string `json:"statsCollectInterval"` HashrateWindow string `json:"hashrateWindow"` HashrateLargeWindow string `json:"hashrateLargeWindow"` @@ -112,11 +118,67 @@ func (s *ApiServer) Start() { } }() + go func() { + c := cron.New() + + poolCharts := s.config.PoolCharts + log.Printf("pool charts config is :%v", poolCharts) + c.AddFunc(poolCharts, func() { + s.collectPoolCharts() + }) + + minerCharts := s.config.MinerCharts + log.Printf("miner charts config is :%v", minerCharts) + c.AddFunc(minerCharts, func() { + + miners, err := s.backend.GetAllMinerAccount() + if err != nil { + log.Println("Get all miners account error: ", err) + } + for _, login := range miners { + miner, _ := s.backend.CollectWorkersStats(s.hashrateWindow, s.hashrateLargeWindow, login) + s.collectMinerCharts(login, miner["currentHashrate"].(int64), miner["hashrate"].(int64), miner["workersOnline"].(int64)) + } + }) + + c.Start() + }() + if !s.config.PurgeOnly { s.listen() } } +func (s *ApiServer) collectPoolCharts() { + ts := util.MakeTimestamp() / 1000 + now := time.Now() + year, month, day := now.Date() + hour, min, _ := now.Clock() + t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min) + stats := s.getStats() + hash := fmt.Sprint(stats["hashrate"]) + log.Println("Pool Hash is ", ts, t2, hash) + err := s.backend.WritePoolCharts(ts, t2, hash) + if err != nil { + log.Printf("Failed to fetch pool charts from backend: %v", err) + return + } +} + +func (s *ApiServer) collectMinerCharts(login string, hash int64, largeHash int64, workerOnline int64) { + ts := util.MakeTimestamp() / 1000 + now := time.Now() + year, month, day := now.Date() + hour, min, _ := now.Clock() + t2 := fmt.Sprintf("%d-%02d-%02d %02d_%02d", year, month, day, hour, min) + + log.Println("Miner "+login+" Hash is", ts, t2, hash, largeHash) + err := s.backend.WriteMinerCharts(ts, t2, login, hash, largeHash, workerOnline) + if err != nil { + log.Printf("Failed to fetch miner %v charts from backend: %v", login, err) + } +} + func (s *ApiServer) listen() { r := mux.NewRouter() r.HandleFunc("/api/stats", s.StatsIndex) @@ -163,6 +225,7 @@ func (s *ApiServer) collectStats() { return } } + stats["poolCharts"], err = s.backend.GetPoolCharts(s.config.PoolChartsNum) s.stats.Store(stats) log.Printf("Stats collection finished %s", time.Since(start)) } @@ -184,6 +247,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["hashrate"] = stats["hashrate"] reply["minersTotal"] = stats["minersTotal"] reply["maturedTotal"] = stats["maturedTotal"] @@ -302,6 +366,7 @@ 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) reply = &Entry{stats: stats, updatedAt: now} s.miners[login] = reply } diff --git a/config.example.json b/config.example.json index f4c625601..be789a1d6 100644 --- a/config.example.json +++ b/config.example.json @@ -57,7 +57,11 @@ "hashrateLargeWindow": "3h", "luckWindow": [64, 128, 256], "payments": 30, - "blocks": 50 + "blocks": 50, + "poolCharts":"0 */20 * * * *", + "poolChartsNum":74, + "minerCharts":"0 */20 * * * *", + "minerChartsNum":74 }, "upstreamCheckInterval": "5s", diff --git a/storage/redis.go b/storage/redis.go index 667b06022..7eb08de6c 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -24,6 +24,20 @@ type RedisClient struct { prefix string } +type PoolCharts struct { + Timestamp int64 `json:"x"` + TimeFormat string `json:"timeFormat"` + PoolHash int64 `json:"y"` +} + +type MinerCharts struct { + Timestamp int64 `json:"x"` + TimeFormat string `json:"timeFormat"` + MinerHash int64 `json:"minerHash"` + MinerLargeHash int64 `json:"minerLargeHash"` + WorkerOnline string `json:"workerOnline"` +} + type BlockData struct { Height int64 `json:"height"` Timestamp int64 `json:"timestamp"` @@ -122,6 +136,109 @@ func (r *RedisClient) GetWhitelist() ([]string, error) { return cmd.Val(), nil } +func (r *RedisClient) WritePoolCharts(time1 int64, time2 string, poolHash string) error { + s := join(time1, time2, poolHash) + cmd := r.client.ZAdd(r.formatKey("charts", "pool"), redis.Z{Score: float64(time1), Member: s}) + return cmd.Err() +} + +func (r *RedisClient) WriteMinerCharts(time1 int64, time2, k string, hash, largeHash, workerOnline int64) error { + s := join(time1, time2, hash, largeHash, workerOnline) + cmd := r.client.ZAdd(r.formatKey("charts", "miner", k), redis.Z{Score: float64(time1), Member: s}) + return cmd.Err() +} + +func (r *RedisClient) GetPoolCharts(poolHashLen int64) (stats []*PoolCharts, err error) { + + tx := r.client.Multi() + defer tx.Close() + + now := util.MakeTimestamp() / 1000 + + cmds, err := tx.Exec(func() error { + tx.ZRemRangeByScore(r.formatKey("charts", "pool"), "-inf", fmt.Sprint("(", now-172800)) + tx.ZRevRangeWithScores(r.formatKey("charts", "pool"), 0, poolHashLen) + return nil + }) + + if err != nil { + return nil, err + } + + stats = convertPoolChartsResults(cmds[1].(*redis.ZSliceCmd)) + return stats, nil +} + +func convertPoolChartsResults(raw *redis.ZSliceCmd) []*PoolCharts { + var result []*PoolCharts + for _, v := range raw.Val() { + // "Timestamp:TimeFormat:Hash" + pc := PoolCharts{} + pc.Timestamp = int64(v.Score) + str := v.Member.(string) + pc.TimeFormat = str[strings.Index(str, ":")+1 : strings.LastIndex(str, ":")] + pc.PoolHash, _ = strconv.ParseInt(str[strings.LastIndex(str, ":")+1:], 10, 64) + result = append(result, &pc) + } + return result +} + +func convertMinerChartsResults(raw *redis.ZSliceCmd) []*MinerCharts { + var result []*MinerCharts + for _, v := range raw.Val() { + // "Timestamp:TimeFormat:Hash:largeHash:workerOnline" + mc := MinerCharts{} + mc.Timestamp = int64(v.Score) + str := v.Member.(string) + mc.TimeFormat = strings.Split(str, ":")[1] + mc.MinerHash, _ = strconv.ParseInt(strings.Split(str, ":")[2], 10, 64) + mc.MinerLargeHash, _ = strconv.ParseInt(strings.Split(str, ":")[3], 10, 64) + mc.WorkerOnline = strings.Split(str, ":")[4] + result = append(result, &mc) + } + return result +} + +func (r *RedisClient) GetAllMinerAccount() (account []string, err error) { + var c int64 + for { + now := util.MakeTimestamp() / 1000 + c, keys, err := r.client.Scan(c, r.formatKey("miners", "*"), now).Result() + + if err != nil { + return account, err + } + for _, key := range keys { + m := strings.Split(key, ":") + //if ( len(m) >= 2 && strings.Index(strings.ToLower(m[2]), "0x") == 0) { + if len(m) >= 2 { + account = append(account, m[2]) + } + } + if c == 0 { + break + } + } + return account, nil +} + +func (r *RedisClient) GetMinerCharts(hashNum int64, login string) (stats []*MinerCharts, err error) { + + tx := r.client.Multi() + defer tx.Close() + now := util.MakeTimestamp() / 1000 + cmds, err := tx.Exec(func() error { + tx.ZRemRangeByScore(r.formatKey("charts", "miner", login), "-inf", fmt.Sprint("(", now-172800)) + tx.ZRevRangeWithScores(r.formatKey("charts", "miner", login), 0, hashNum) + return nil + }) + if err != nil { + return nil, err + } + stats = convertMinerChartsResults(cmds[1].(*redis.ZSliceCmd)) + return stats, nil +} + func (r *RedisClient) WriteNodeState(id string, height uint64, diff *big.Int) error { tx := r.client.Multi() defer tx.Close() diff --git a/www/app/controllers/account.js b/www/app/controllers/account.js index 66687b6a1..86dcc0b2f 100644 --- a/www/app/controllers/account.js +++ b/www/app/controllers/account.js @@ -2,10 +2,120 @@ import Ember from 'ember'; export default Ember.Controller.extend({ applicationController: Ember.inject.controller('application'), - stats: Ember.computed.reads('applicationController.model.stats'), 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(255, 255, 255, 0.1)", + type: "spline", + marginRight: 10, + height: 400, + events: { + load: function() { + var series = this.series[0]; + setInterval(function() { + var x = (new Date).getTime(), + y = e.getWithDefault("model.currentHashrate") / 1000000; + series.addPoint([x, y], true, true) + }, 109000000) + } + } + }, + title: { + text: "" + }, + xAxis: { + ordinal: false, + type: "datetime", + dateTimeLabelFormats: { + millisecond: "%H:%M:%S", + second: "%H:%M:%S", + minute: "%H:%M", + hour: "%H:%M", + day: "%e. %b", + week: "%e. %b", + month: "%b '%y", + year: "%Y" + } + }, + yAxis: { + title: { + text: "HASHRATE" + }, + min: 0 + }, + plotLines: [{ + value: 0, + width: 1, + color: "#808080" + }], + legend: { + enabled: true + }, + tooltip: { + formatter: function() { + return this.y > 1000000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000000).toFixed(2) + " TH/s
" : this.y > 1000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000).toFixed(2) + " GH/s
" : this.y > 1000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000).toFixed(2) + " MH/s
" : "" + this.point.d + "
Hashrate " + this.y.toFixed(2) + " H/s" + + }, + useHTML: true + }, + exporting: { + enabled: false + }, + series: [{ + color: "#E99002", + name: "Average hashrate", + data: function() { + var e, a = []; + 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({ + x: r, + d: l, + y: n + }) + } else a.push({ + x: 0, + d: 0, + y: 0 + }); + return a + }() + }, { + name: "Current hashrate", + data: function() { + var e, a = []; + 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({ + x: r, + d: l, + y: n + }) + } else a.push({ + x: 0, + d: 0, + y: 0 + }); + return a + }() + }] + }; + return a + } + }), roundPercent: Ember.computed('stats', 'model', { get() { var percent = this.get('model.roundShares') / this.get('stats.roundShares'); diff --git a/www/app/controllers/index.js b/www/app/controllers/index.js index b112cdb1d..9946b3717 100644 --- a/www/app/controllers/index.js +++ b/www/app/controllers/index.js @@ -47,5 +47,92 @@ export default Ember.Controller.extend({ this.set('model.login', value); return value; } - }) + }), + chartOptions: Ember.computed("model.hashrate", { + get() { + var e = this, + t = e.getWithDefault("stats.model.poolCharts"), + a = { + chart: { + 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) + } + } + }, + title: { + text: "Our pool's hashrate" + }, + xAxis: { + labels: { + style: { + color: "#000" + } + }, + ordinal: false, + type: "datetime" + }, + yAxis: { + title: { + text: "HASHRATE", + style: { + color: "#000" + } + }, + min: 0, + labels: { + style: { + color: "#000" + } + } + }, + plotLines: [{ + value: 0, + width: 1, + color: "#000" + }], + legend: { + enabled: false + }, + tooltip: { + formatter: function() { + return this.y > 1000000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000000).toFixed(2) + " TH/s
" : this.y > 1000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000).toFixed(2) + " GH/s
" : this.y > 1000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000).toFixed(2) + " MH/s
" : "" + this.point.d + "
Hashrate " + this.y.toFixed(2) + " H/s" + }, + useHTML: true + }, + exporting: { + enabled: false + }, + series: [{ + color: "#15BD27", + name: "Hashrate", + data: function() { + var e, a = []; + 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({ + x: r, + d: l, + y: n + }) + } else a.push({ + x: 0, + d: 0, + y: 0 + }); + return a + }() + }] + }; + return a + } + }) }); diff --git a/www/app/templates/account.hbs b/www/app/templates/account.hbs index 05027496d..f75804202 100644 --- a/www/app/templates/account.hbs +++ b/www/app/templates/account.hbs @@ -46,6 +46,9 @@
+ +{{high-charts mode=chartMode chartOptions=chartOptions content=chartData}} +
+
+ {{high-charts mode=chartMode chartOptions=chartOptions content=chartData}} +
+

{{t "home.query_history"}}

diff --git a/www/package.json b/www/package.json index 9aebf089f..910d2a756 100644 --- a/www/package.json +++ b/www/package.json @@ -36,9 +36,11 @@ "ember-cli-test-loader": "^1.1.0", "ember-cli-uglify": "^1.2.0", "ember-export-application-global": "^1.0.5", + "ember-highcharts": "0.6.0", "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" From 3330c571f95aaedf830cfe96d6c8729f11221ebf Mon Sep 17 00:00:00 2001 From: hackyminer Date: Fri, 20 Apr 2018 13:14:59 +0900 Subject: [PATCH 16/30] highcharts: fixed jshint errors --- www/app/controllers/account.js | 44 +++++++++++++++++++++------------- www/app/controllers/index.js | 26 +++++++++++++------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/www/app/controllers/account.js b/www/app/controllers/account.js index 86dcc0b2f..52b1f304e 100644 --- a/www/app/controllers/account.js +++ b/www/app/controllers/account.js @@ -19,10 +19,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); } } }, @@ -59,7 +59,7 @@ export default Ember.Controller.extend({ }, tooltip: { formatter: function() { - return this.y > 1000000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000000).toFixed(2) + " TH/s
" : this.y > 1000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000).toFixed(2) + " GH/s
" : this.y > 1000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000).toFixed(2) + " MH/s
" : "" + this.point.d + "
Hashrate " + this.y.toFixed(2) + " H/s" + return this.y > 1000000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000000).toFixed(2) + " TH/s
" : this.y > 1000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000).toFixed(2) + " GH/s
" : this.y > 1000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000).toFixed(2) + " MH/s
" : "" + this.point.d + "
Hashrate " + this.y.toFixed(2) + " H/s"; }, @@ -73,47 +73,59 @@ 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', { diff --git a/www/app/controllers/index.js b/www/app/controllers/index.js index 9946b3717..aa66a299d 100644 --- a/www/app/controllers/index.js +++ b/www/app/controllers/index.js @@ -61,7 +61,10 @@ export default Ember.Controller.extend({ 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); } } }, @@ -101,7 +104,7 @@ export default Ember.Controller.extend({ }, tooltip: { formatter: function() { - return this.y > 1000000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000000).toFixed(2) + " TH/s
" : this.y > 1000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000).toFixed(2) + " GH/s
" : this.y > 1000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000).toFixed(2) + " MH/s
" : "" + this.point.d + "
Hashrate " + this.y.toFixed(2) + " H/s" + return this.y > 1000000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000000).toFixed(2) + " TH/s
" : this.y > 1000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000).toFixed(2) + " GH/s
" : this.y > 1000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000).toFixed(2) + " MH/s
" : "" + this.point.d + "
Hashrate " + this.y.toFixed(2) + " H/s"; }, useHTML: true }, @@ -113,26 +116,31 @@ 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; } }) }); From 9d28bbfb253719ee5b6ff61092a46291e53537f5 Mon Sep 17 00:00:00 2001 From: hackyminer Date: Fri, 20 Apr 2018 15:38:34 +0900 Subject: [PATCH 17/30] www/app: make highcharts configurable --- www/app/controllers/account.js | 9 ++++++++- www/app/controllers/index.js | 7 ++++++- www/app/templates/account.hbs | 2 ++ www/app/templates/index.hbs | 2 ++ www/config/environment.js | 20 +++++++++++++++++++- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/www/app/controllers/account.js b/www/app/controllers/account.js index 52b1f304e..dbb24c489 100644 --- a/www/app/controllers/account.js +++ b/www/app/controllers/account.js @@ -45,7 +45,7 @@ export default Ember.Controller.extend({ }, yAxis: { title: { - text: "HASHRATE" + text: "HashRate" }, min: 0 }, @@ -125,6 +125,13 @@ export default Ember.Controller.extend({ }() }] }; + a.title.text = this.get('config.highcharts.account.title') || ""; + a.yAxis.title.text = this.get('config.highcharts.account.ytitle') || "Hashrate"; + a.chart.height = this.get('config.highcharts.account.height') || 300; + a.chart.type = this.get('config.highcharts.account.type') || 'spline'; + var colors = this.get('config.highcharts.account.color'); + a.series[0].color = colors[0] || '#e99002'; + a.series[1].color = colors[1] || '#1994b8'; return a; } }), diff --git a/www/app/controllers/index.js b/www/app/controllers/index.js index aa66a299d..82375938a 100644 --- a/www/app/controllers/index.js +++ b/www/app/controllers/index.js @@ -82,7 +82,7 @@ export default Ember.Controller.extend({ }, yAxis: { title: { - text: "HASHRATE", + text: "Pool Hashrate", style: { color: "#000" } @@ -140,6 +140,11 @@ export default Ember.Controller.extend({ }() }] }; + a.title.text = this.get('config.highcharts.main.title') || ""; + a.yAxis.title.text = this.get('config.highcharts.main.ytitle') || "Pool Hashrate"; + a.chart.height = this.get('config.highcharts.main.height') || 300; + a.chart.type = this.get('config.highcharts.main.type') || 'spline'; + a.series[0].color = this.get('config.highcharts.main.color') || '#15b7bd'; return a; } }) diff --git a/www/app/templates/account.hbs b/www/app/templates/account.hbs index f75804202..11f28b540 100644 --- a/www/app/templates/account.hbs +++ b/www/app/templates/account.hbs @@ -47,7 +47,9 @@
+{{#if config.highcharts.account.enabled}} {{high-charts mode=chartMode chartOptions=chartOptions content=chartData}} +{{/if}}
+{{#if config.highcharts.main.enabled}}
{{high-charts mode=chartMode chartOptions=chartOptions content=chartData}}
+{{/if}}
diff --git a/www/config/environment.js b/www/config/environment.js index ed3dcfe13..34fa5f276 100644 --- a/www/config/environment.js +++ b/www/config/environment.js @@ -43,7 +43,25 @@ module.exports = function(environment) { DonationAddress: '', BlockReward: 5, BlockUnlockDepth: 120, - BlockTime: 14.4 + BlockTime: 14.4, + highcharts: { + main: { + enabled: true, + height: 200, + type: 'spline', + color: '', + title: '', + ytitle: '', + }, + account: { + enabled: true, + height: 200, + type: 'spline', + color: [ '', '' ], + title: '', + ytitle: '', + } + } } }; From 3e5424185643b028a5fa9347d67ca3e4a717eb22 Mon Sep 17 00:00:00 2001 From: ComBba Date: Fri, 13 Apr 2018 19:18:22 +0900 Subject: [PATCH 18/30] support payout charts Conflicts: www/app/controllers/account/index.js www/app/controllers/account/payouts.js www/app/templates/account.hbs --- api/server.go | 1 + storage/redis.go | 54 ++++++++++++ www/app/controllers/account/index.js | 117 ++++++++++++++++++++++++- www/app/controllers/account/payouts.js | 100 ++++++++++++++++++++- www/app/templates/account.hbs | 4 - www/app/templates/account/index.hbs | 3 + www/app/templates/account/payouts.hbs | 3 + 7 files changed, 276 insertions(+), 6 deletions(-) diff --git a/api/server.go b/api/server.go index 40c9c399a..fd405a676 100644 --- a/api/server.go +++ b/api/server.go @@ -367,6 +367,7 @@ func (s *ApiServer) AccountIndex(w http.ResponseWriter, r *http.Request) { } stats["pageSize"] = s.config.Payments 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 } diff --git a/storage/redis.go b/storage/redis.go index 7eb08de6c..7abd457a5 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -2,6 +2,7 @@ package storage import ( "fmt" + "math" "math/big" "strconv" "strings" @@ -38,6 +39,12 @@ type MinerCharts struct { WorkerOnline string `json:"workerOnline"` } +type PaymentCharts struct { + Timestamp int64 `json:"x"` + TimeFormat string `json:"timeFormat"` + Amount int64 `json:"amount"` +} + type BlockData struct { Height int64 `json:"height"` Timestamp int64 `json:"timestamp"` @@ -239,6 +246,22 @@ func (r *RedisClient) GetMinerCharts(hashNum int64, login string) (stats []*Mine return stats, nil } +func (r *RedisClient) GetPaymentCharts(login string) (stats []*PaymentCharts, err error) { + + tx := r.client.Multi() + defer tx.Close() + cmds, err := tx.Exec(func() error { + tx.ZRevRangeWithScores(r.formatKey("payments", login), 0, 360) + return nil + }) + if err != nil { + return nil, err + } + stats = convertPaymentChartsResults(cmds[0].(*redis.ZSliceCmd)) + //fmt.Println(stats) + return stats, nil +} + func (r *RedisClient) WriteNodeState(id string, height uint64, diff *big.Int) error { tx := r.client.Multi() defer tx.Close() @@ -1082,6 +1105,37 @@ func convertPaymentsResults(raw *redis.ZSliceCmd) []map[string]interface{} { return result } +/* +Timestamp int64 `json:"x"` +TimeFormat string `json:"timeFormat"` +Amount int64 `json:"amount"` +*/ +func convertPaymentChartsResults(raw *redis.ZSliceCmd) []*PaymentCharts { + var result []*PaymentCharts + for _, v := range raw.Val() { + pc := PaymentCharts{} + pc.Timestamp = int64(v.Score) + tm := time.Unix(pc.Timestamp, 0) + pc.TimeFormat = tm.Format("2006-01-02") + " 00_00" + fields := strings.Split(v.Member.(string), ":") + pc.Amount, _ = strconv.ParseInt(fields[1], 10, 64) + //fmt.Printf("%d : %s : %d \n", pc.Timestamp, pc.TimeFormat, pc.Amount) + + var chkAppend bool + for _, pcc := range result { + if pcc.TimeFormat == pc.TimeFormat { + pcc.Amount += pc.Amount + chkAppend = true + } + } + if !chkAppend { + pc.Timestamp -= int64(math.Mod(float64(v.Score), float64(86400))) + result = append(result, &pc) + } + } + return result +} + func (r *RedisClient) GetCurrentHashrate(login string) (int64, error) { hashrate := r.client.HGet(r.formatKey("currenthashrate", login), "hashrate") if hashrate.Err() == redis.Nil { diff --git a/www/app/controllers/account/index.js b/www/app/controllers/account/index.js index 12b7f9a34..50db31a68 100644 --- a/www/app/controllers/account/index.js +++ b/www/app/controllers/account/index.js @@ -2,5 +2,120 @@ import Ember from 'ember'; export default Ember.Controller.extend({ applicationController: Ember.inject.controller('application'), - config: Ember.computed.reads('applicationController.config') + config: Ember.computed.reads('applicationController.config'), + netstats: Ember.computed.reads('applicationController'), + 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(255, 255, 255, 0.1)", + type: "spline", + marginRight: 10, + height: 200, + events: { + load: function() { + var series = this.series[0]; + setInterval(function() { + var x = (new Date).getTime(), + y = e.getWithDefault("model.currentHashrate") / 1000000; + series.addPoint([x, y], true, true) + }, 1090000000) + } + } + }, + title: { + text: "" + }, + xAxis: { + ordinal: false, + type: "datetime", + dateTimeLabelFormats: { + millisecond: "%H:%M:%S", + second: "%H:%M:%S", + minute: "%H:%M", + hour: "%H:%M", + day: "%e. %b", + week: "%e. %b", + month: "%b '%y", + year: "%Y" + } + }, + yAxis: { + title: { + text: "Hashrate by Account" + }, + //softMin: e.getWithDefault("model.currentHashrate") / 1000000, + //softMax: e.getWithDefault("model.currentHashrate") / 1000000, + }, + plotLines: [{ + value: 0, + width: 1, + color: "#808080" + }], + legend: { + enabled: true + }, + tooltip: { + formatter: function() { + return this.y > 1000000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000000).toFixed(2) + " TH/s
" : this.y > 1000000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000000).toFixed(2) + " GH/s
" : this.y > 1000000 ? "" + this.point.d + "
Hashrate " + (this.y / 1000000).toFixed(2) + " MH/s
" : "" + this.point.d + "
Hashrate " + this.y.toFixed(2) + " H/s" + + }, + + useHTML: true + }, + exporting: { + enabled: false + }, + series: [{ + color: "#E99002", + name: "3 hours average hashrate", + data: function() { + var e, a = []; + 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({ + x: r, + d: l, + y: n + }) + } else a.push({ + x: 0, + d: 0, + y: 0 + }); + return a + }() + }, { + name: "30 minutes average hashrate", + data: function() { + var e, a = []; + 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({ + x: r, + d: l, + y: n + }) + } else a.push({ + x: 0, + d: 0, + y: 0 + }); + return a + }() + }] + }; + return a + } + }) }); diff --git a/www/app/controllers/account/payouts.js b/www/app/controllers/account/payouts.js index 12b7f9a34..ee7469997 100644 --- a/www/app/controllers/account/payouts.js +++ b/www/app/controllers/account/payouts.js @@ -2,5 +2,103 @@ import Ember from 'ember'; export default Ember.Controller.extend({ applicationController: Ember.inject.controller('application'), - config: Ember.computed.reads('applicationController.config') + config: Ember.computed.reads('applicationController.config'), + stats: Ember.computed.reads('applicationController.model.stats'), + intl: Ember.inject.service(), + + chartPaymentText: Ember.computed('model', { + get() { + var outText = this.get('model.paymentCharts'); + if (!outText) { + return 0; + } + return outText; + } + }), + + chartPayment: Ember.computed('intl', 'model.paymentCharts', { + get() { + var e = this, + t = e.getWithDefault("model.paymentCharts"), + a = { + chart: { + backgroundColor: "rgba(255, 255, 255, 0.1)", + type: "column", + marginRight: 10, + height: 200, + events: { + load: function() { + var series = this.series[0]; + setInterval(function() { + var x = (new Date).getDate(), + y = e.getWithDefault("model.paymentCharts"); + series.addPoint([x, y], true, true) + }, 1090000000) + } + } + }, + title: { + text: "" + }, + xAxis: { + ordinal: false, + type: "datetime", + dateTimeLabelFormats: { + day: "%e. %b", + week: "%e. %b", + month: "%b '%y", + year: "%Y" + } + }, + yAxis: { + title: { + text: "Payment by Account" + } + }, + plotLines: [{ + value: 0, + width: 1, + color: "#808080" + }], + legend: { + enabled: true + }, + tooltip: { + formatter: function() { + return "" + Highcharts.dateFormat('%Y-%m-%d', new Date(this.x)) + "
Payment " + this.y.toFixed(4) + " ESN" + }, + useHTML: true + }, + exporting: { + enabled: false + }, + series: [{ + color: "#E99002", + name: "Payment Series", + data: function() { + var e, a = []; + 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 + }() + }] + }; + return a + } +}) }); diff --git a/www/app/templates/account.hbs b/www/app/templates/account.hbs index 11f28b540..237a62a6b 100644 --- a/www/app/templates/account.hbs +++ b/www/app/templates/account.hbs @@ -47,10 +47,6 @@
-{{#if config.highcharts.account.enabled}} -{{high-charts mode=chartMode chartOptions=chartOptions content=chartData}} -{{/if}} -