diff --git a/configs/api.json b/configs/api.json index f01ec38..e869d31 100644 --- a/configs/api.json +++ b/configs/api.json @@ -3,7 +3,6 @@ "coin": "etc", "name": "main", "pplns": 9000, - "network": "classic", "proxy": { "enabled": false, "listen": "0.0.0.0:8888", @@ -93,7 +92,8 @@ "keepTxFees": false, "interval": "10m", "daemon": "http://127.0.0.1:8545", - "timeout": "10s" + "timeout": "10s", + "classic": true }, "payouts": { diff --git a/configs/payout.json b/configs/payout.json index b43bf1d..b1b9aa3 100644 --- a/configs/payout.json +++ b/configs/payout.json @@ -92,7 +92,8 @@ "keepTxFees": false, "interval": "10m", "daemon": "http://127.0.0.1:8545", - "timeout": "10s" + "timeout": "10s", + "classic": true }, "payouts": { diff --git a/configs/stratum2b.json b/configs/stratum2b.json index 4e01e8c..5b82654 100644 --- a/configs/stratum2b.json +++ b/configs/stratum2b.json @@ -88,7 +88,8 @@ "keepTxFees": false, "interval": "10m", "daemon": "http://127.0.0.1:8545", - "timeout": "10s" + "timeout": "10s", + "classic": true }, "payouts": { diff --git a/configs/stratum4b.json b/configs/stratum4b.json index 97a1139..cd62eee 100644 --- a/configs/stratum4b.json +++ b/configs/stratum4b.json @@ -93,7 +93,8 @@ "keepTxFees": false, "interval": "10m", "daemon": "http://127.0.0.1:8545", - "timeout": "10s" + "timeout": "10s", + "classic": true }, "payouts": { diff --git a/configs/stratum8b.json b/configs/stratum8b.json index f278024..d6cc342 100644 --- a/configs/stratum8b.json +++ b/configs/stratum8b.json @@ -93,7 +93,8 @@ "keepTxFees": false, "interval": "10m", "daemon": "http://127.0.0.1:8545", - "timeout": "10s" + "timeout": "10s", + "classic": true }, "payouts": { diff --git a/configs/stratum9b.json b/configs/stratum9b.json index e3eeb31..cf4168f 100644 --- a/configs/stratum9b.json +++ b/configs/stratum9b.json @@ -93,7 +93,8 @@ "keepTxFees": false, "interval": "10m", "daemon": "http://127.0.0.1:8545", - "timeout": "10s" + "timeout": "10s", + "classic": true }, "payouts": { diff --git a/configs/unlocker.json b/configs/unlocker.json index b45ebfa..d22658c 100644 --- a/configs/unlocker.json +++ b/configs/unlocker.json @@ -92,7 +92,8 @@ "keepTxFees": false, "interval": "10m", "daemon": "http://127.0.0.1:8545", - "timeout": "120s" + "timeout": "120s", + "classic": true }, "payouts": { diff --git a/main.go b/main.go index f60ab57..e8921bc 100644 --- a/main.go +++ b/main.go @@ -33,7 +33,7 @@ func startApi() { } func startBlockUnlocker() { - u := payouts.NewBlockUnlocker(&cfg.BlockUnlocker, backend, &cfg.Network) + u := payouts.NewBlockUnlocker(&cfg.BlockUnlocker, backend) u.Start() } diff --git a/payouts/unlocker.go b/payouts/unlocker.go index 43cac49..7d7a328 100644 --- a/payouts/unlocker.go +++ b/payouts/unlocker.go @@ -4,7 +4,7 @@ import ( "fmt" "log" "math/big" - + "os" "strconv" "strings" "time" @@ -17,28 +17,29 @@ import ( ) type UnlockerConfig struct { - Enabled bool `json:"enabled"` - PoolFee float64 `json:"poolFee"` - PoolFeeAddress string `json:"poolFeeAddress"` - Donate bool `json:"donate"` - Depth int64 `json:"depth"` - ImmatureDepth int64 `json:"immatureDepth"` - KeepTxFees bool `json:"keepTxFees"` - Interval string `json:"interval"` - Daemon string `json:"daemon"` - Timeout string `json:"timeout"` - Ecip1017FBlock int64 `json:"ecip1017FBlock"` - Ecip1017EraRounds *big.Int `json:"ecip1017EraRounds"` + Enabled bool `json:"enabled"` + PoolFee float64 `json:"poolFee"` + PoolFeeAddress string `json:"poolFeeAddress"` + Donate bool `json:"donate"` + Depth int64 `json:"depth"` + ImmatureDepth int64 `json:"immatureDepth"` + KeepTxFees bool `json:"keepTxFees"` + Interval string `json:"interval"` + Daemon string `json:"daemon"` + Timeout string `json:"timeout"` + Classic bool `json:"classic"` } const minDepth = 16 +const byzantiumHardForkHeight = 4370000 -var disinflationRateQuotient = big.NewInt(4) // Disinflation rate quotient for ECIP1017 -var disinflationRateDivisor = big.NewInt(5) // Disinflation rate divisor for ECIP1017 -var big32 = big.NewInt(32) -var big8 = big.NewInt(8) +var homesteadReward = math.MustParseBig256("5000000000000000000") +var byzantiumReward = math.MustParseBig256("3200000000000000000") +var classicReward = math.MustParseBig256("3200000000000000000") -var homesteadReward = math.MustParseBig256("3200000000000000000") +// Donate 10% from pool fees to developers +const donationFee = 10 +const donationAccount = "0xd92fa5a9732a0aec36dc8d5a6a1305dc2d3e09e6" type BlockUnlocker struct { config *UnlockerConfig @@ -48,17 +49,7 @@ type BlockUnlocker struct { lastFail error } -func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient, network *string) *BlockUnlocker { - if *network == "classic" { - cfg.Ecip1017FBlock = 5000000 - cfg.Ecip1017EraRounds = big.NewInt(5000000) - } else if *network == "mordor" { - cfg.Ecip1017FBlock = 0 - cfg.Ecip1017EraRounds = big.NewInt(2000000) - } else { - log.Fatalln("Invalid network set", network) - } - +func NewBlockUnlocker(cfg *UnlockerConfig, backend *storage.RedisClient) *BlockUnlocker { if len(cfg.PoolFeeAddress) != 0 && !util.IsValidHexAddress(cfg.PoolFeeAddress) { log.Fatalln("Invalid poolFeeAddress", cfg.PoolFeeAddress) } @@ -121,11 +112,6 @@ func (u *BlockUnlocker) unlockCandidates(candidates []*storage.BlockData) (*Unlo /* Search for a normal block with wrong height here by traversing 16 blocks back and forward. * Also we are searching for a block that can include this one as uncle. */ - if candidate.Height < minDepth { - orphan = false - // avoid scanning the first 16 blocks - continue - } for i := int64(minDepth * -1); i < minDepth; i++ { height := candidate.Height + i @@ -134,7 +120,6 @@ func (u *BlockUnlocker) unlockCandidates(candidates []*storage.BlockData) (*Unlo } block, err := u.rpc.GetBlockByHeight(height) - if err != nil { log.Printf("Error while retrieving block %v from node: %v", height, err) return nil, err @@ -177,7 +162,7 @@ func (u *BlockUnlocker) unlockCandidates(candidates []*storage.BlockData) (*Unlo orphan = false result.uncles++ - err := handleUncle(height, uncle, candidate, u.config) + err := handleUncle(height, uncle, candidate, u.config.Classic) if err != nil { u.halt = true u.lastFail = err @@ -227,13 +212,7 @@ func (u *BlockUnlocker) handleBlock(block *rpc.GetBlockReply, candidate *storage return err } candidate.Height = correctHeight - era := GetBlockEra(big.NewInt(candidate.Height), u.config.Ecip1017EraRounds) - reward := getConstReward(era) - - // Add reward for including uncles - uncleReward := getRewardForUncle(reward) - rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles)))) - reward.Add(reward, rewardForUncles) + reward := getConstReward(candidate.Height, u.config.Classic) // Add TX fees extraTxReward, err := u.getExtraRewardForTx(block) @@ -246,19 +225,23 @@ func (u *BlockUnlocker) handleBlock(block *rpc.GetBlockReply, candidate *storage reward.Add(reward, extraTxReward) } + // Add reward for including uncles + uncleReward := getRewardForUncle(candidate.Height, u.config.Classic) + rewardForUncles := big.NewInt(0).Mul(uncleReward, big.NewInt(int64(len(block.Uncles)))) + reward.Add(reward, rewardForUncles) + candidate.Orphan = false candidate.Hash = block.Hash candidate.Reward = reward return nil } -func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.BlockData, cfg *UnlockerConfig) error { +func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.BlockData, isClassic bool) error { uncleHeight, err := strconv.ParseInt(strings.Replace(uncle.Number, "0x", "", -1), 16, 64) if err != nil { return err } - era := GetBlockEra(big.NewInt(height), cfg.Ecip1017EraRounds) - reward := getUncleReward(new(big.Int).SetInt64(uncleHeight), new(big.Int).SetInt64(height), era, getConstReward(era)) + reward := getUncleReward(uncleHeight, height, isClassic) candidate.Height = height candidate.UncleHeight = uncleHeight candidate.Orphan = false @@ -270,6 +253,7 @@ func handleUncle(height int64, uncle *rpc.GetBlockReply, candidate *storage.Bloc func (u *BlockUnlocker) unlockPendingBlocks() { if u.halt { log.Println("Unlocking suspended due to last critical error:", u.lastFail) + os.Exit(1) return } @@ -325,7 +309,7 @@ func (u *BlockUnlocker) unlockPendingBlocks() { totalPoolProfit := new(big.Rat) for _, block := range result.maturedBlocks { - revenue, minersProfit, poolProfit, roundRewards, err := u.calculateRewards(block) + revenue, minersProfit, poolProfit, roundRewards, percents, err := u.calculateRewards(block) if err != nil { u.halt = true u.lastFail = err @@ -353,6 +337,11 @@ func (u *BlockUnlocker) unlockPendingBlocks() { entries := []string{logEntry} for login, reward := range roundRewards { entries = append(entries, fmt.Sprintf("\tREWARD %v: %v: %v Shannon", block.RoundKey(), login, reward)) + per := new(big.Rat) + if val, ok := percents[login]; ok { + per = val + } + u.backend.WriteReward(login, reward, per, true, block) } log.Println(strings.Join(entries, "\n")) } @@ -424,7 +413,7 @@ func (u *BlockUnlocker) unlockAndCreditMiners() { totalPoolProfit := new(big.Rat) for _, block := range result.maturedBlocks { - revenue, minersProfit, poolProfit, roundRewards, err := u.calculateRewards(block) + revenue, minersProfit, poolProfit, roundRewards, percents, err := u.calculateRewards(block) if err != nil { u.halt = true u.lastFail = err @@ -452,6 +441,11 @@ func (u *BlockUnlocker) unlockAndCreditMiners() { entries := []string{logEntry} for login, reward := range roundRewards { entries = append(entries, fmt.Sprintf("\tREWARD %v: %v: %v Shannon", block.RoundKey(), login, reward)) + per := new(big.Rat) + if val, ok := percents[login]; ok { + per = val + } + u.backend.WriteReward(login, reward, per, false, block) } log.Println(strings.Join(entries, "\n")) } @@ -464,16 +458,21 @@ func (u *BlockUnlocker) unlockAndCreditMiners() { ) } -func (u *BlockUnlocker) calculateRewards(block *storage.BlockData) (*big.Rat, *big.Rat, *big.Rat, map[string]int64, error) { +func (u *BlockUnlocker) calculateRewards(block *storage.BlockData) (*big.Rat, *big.Rat, *big.Rat, map[string]int64, map[string]*big.Rat, error) { revenue := new(big.Rat).SetInt(block.Reward) minersProfit, poolProfit := chargeFee(revenue, u.config.PoolFee) shares, err := u.backend.GetRoundShares(block.RoundHeight, block.Nonce) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, nil, nil, err + } + + totalShares := int64(0) + for _, val := range shares { + totalShares += val } - rewards := calculateRewardsForShares(shares, block.TotalShares, minersProfit) + rewards, percents := calculateRewardsForShares(shares, totalShares, minersProfit) if block.ExtraReward != nil { extraReward := new(big.Rat).SetInt(block.ExtraReward) @@ -481,23 +480,31 @@ func (u *BlockUnlocker) calculateRewards(block *storage.BlockData) (*big.Rat, *b revenue.Add(revenue, extraReward) } + if u.config.Donate { + var donation = new(big.Rat) + poolProfit, donation = chargeFee(poolProfit, donationFee) + login := strings.ToLower(donationAccount) + rewards[login] += weiToShannonInt64(donation) + } + if len(u.config.PoolFeeAddress) != 0 { address := strings.ToLower(u.config.PoolFeeAddress) rewards[address] += weiToShannonInt64(poolProfit) } - return revenue, minersProfit, poolProfit, rewards, nil + return revenue, minersProfit, poolProfit, rewards, percents, nil } -func calculateRewardsForShares(shares map[string]int64, total int64, reward *big.Rat) map[string]int64 { +func calculateRewardsForShares(shares map[string]int64, total int64, reward *big.Rat) (map[string]int64, map[string]*big.Rat) { rewards := make(map[string]int64) + percents := make(map[string]*big.Rat) for login, n := range shares { - percent := big.NewRat(n, total) - workerReward := new(big.Rat).Mul(reward, percent) + percents[login] = big.NewRat(n, total) + workerReward := new(big.Rat).Mul(reward, percents[login]) rewards[login] += weiToShannonInt64(workerReward) } - return rewards + return rewards, percents } // Returns new value after fee deduction and fee value. @@ -514,67 +521,35 @@ func weiToShannonInt64(wei *big.Rat) int64 { return value } -// GetRewardByEra gets a block reward at disinflation rate. -// Constants MaxBlockReward, DisinflationRateQuotient, and DisinflationRateDivisor assumed. -func GetBlockWinnerRewardByEra(era *big.Int, blockReward *big.Int) *big.Int { - if era.Cmp(big.NewInt(0)) == 0 { - return new(big.Int).Set(blockReward) - } - - // MaxBlockReward _r_ * (4/5)**era == MaxBlockReward * (4**era) / (5**era) - // since (q/d)**n == q**n / d**n - // qed - var q, d, r *big.Int = new(big.Int), new(big.Int), new(big.Int) - - q.Exp(disinflationRateQuotient, era, nil) - d.Exp(disinflationRateDivisor, era, nil) - - r.Mul(blockReward, q) - r.Div(r, d) - - return r -} - -// GetBlockEra gets which "Era" a given block is within, given an era length (ecip-1017 has era=5,000,000 blocks) -// Returns a zero-index era number, so "Era 1": 0, "Era 2": 1, "Era 3": 2 ... -func GetBlockEra(blockNum, eraLength *big.Int) *big.Int { - // If genesis block or impossible negative-numbered block, return zero-val. - if blockNum.Sign() < 1 { - return new(big.Int) +func getConstReward(height int64, isClassic bool) *big.Int { + if !isClassic { + if height >= byzantiumHardForkHeight { + return new(big.Int).Set(byzantiumReward) + } + return new(big.Int).Set(homesteadReward) + } else { + return new(big.Int).Set(classicReward) } - - remainder := big.NewInt(0).Mod(big.NewInt(0).Sub(blockNum, big.NewInt(1)), eraLength) - base := big.NewInt(0).Sub(blockNum, remainder) - - d := big.NewInt(0).Div(base, eraLength) - dremainder := big.NewInt(0).Mod(d, big.NewInt(1)) - - return new(big.Int).Sub(d, dremainder) } -func getConstReward(era *big.Int) *big.Int { - var blockReward = homesteadReward - wr := GetBlockWinnerRewardByEra(era, blockReward) - return wr +func getRewardForUncle(height int64, isClassic bool) *big.Int { + reward := getConstReward(height, isClassic) + return new(big.Int).Div(reward, new(big.Int).SetInt64(32)) } -func getRewardForUncle(blockReward *big.Int) *big.Int { - return new(big.Int).Div(blockReward, big32) //return new(big.Int).Div(reward, new(big.Int).SetInt64(32)) -} - -func getUncleReward(uHeight *big.Int, height *big.Int, era *big.Int, reward *big.Int) *big.Int { - // Era 1 (index 0): - // An extra reward to the winning miner for including uncles as part of the block, in the form of an extra 1/32 (0.15625ETC) per uncle included, up to a maximum of two (2) uncles. - if era.Cmp(big.NewInt(0)) == 0 { - r := new(big.Int) - r.Add(uHeight, big8) // 2,534,998 + 8 = 2,535,006 - r.Sub(r, height) // 2,535,006 - 2,534,999 = 7 - r.Mul(r, reward) // 7 * 5e+18 = 35e+18 - r.Div(r, big8) // 35e+18 / 8 = 7/8 * 5e+18 - - return r +func getUncleReward(uHeight, height int64, isClassic bool) *big.Int { + if !isClassic { + reward := getConstReward(height, isClassic) + k := height - uHeight + reward.Mul(big.NewInt(8-k), reward) + reward.Div(reward, big.NewInt(8)) + return reward + } else { + reward := getConstReward(height, isClassic) + reward.Mul(reward, big.NewInt(3125)) + reward.Div(reward, big.NewInt(100000)) + return reward } - return getRewardForUncle(reward) } func (u *BlockUnlocker) getExtraRewardForTx(block *rpc.GetBlockReply) (*big.Int, error) { diff --git a/proxy/config.go b/proxy/config.go index 0b09400..bc2c502 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -17,7 +17,6 @@ type Config struct { Threads int `json:"threads"` Coin string `json:"coin"` - Network string `json:"network"` Pplns int64 `json:"pplns"` Redis storage.Config `json:"redis"` diff --git a/proxy/miner.go b/proxy/miner.go index ffb2d94..68209f9 100644 --- a/proxy/miner.go +++ b/proxy/miner.go @@ -11,22 +11,11 @@ import ( ) var ecip1099FBlockClassic uint64 = 11700000 // classic mainnet -var ecip1099FBlockMordor uint64 = 2520000 // mordor +var ecip1099FBlockMordor uint64 = 2520000 // mordor testnet -var hasher *etchash.Etchash = nil +var hasher = etchash.New(&ecip1099FBlockClassic) func (s *ProxyServer) processShare(login, id, ip string, t *BlockTemplate, params []string) (bool, bool) { - if hasher == nil { - if s.config.Network == "classic" { - hasher = etchash.New(&ecip1099FBlockClassic) - } else if s.config.Network == "mordor" { - hasher = etchash.New(&ecip1099FBlockMordor) - } else { - // unknown network - log.Printf("Unknown network configuration %s", s.config.Network) - return false, false - } - } // Now, the function received some work with login id and worker name and all information, ready to be processed // and checked if it is a valid work or not, and if it is a block or not and write to db accordingly nonceHex := params[0] diff --git a/storage/redis.go b/storage/redis.go index 75d03bb..0617e9a 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -63,7 +63,6 @@ type RewardData struct { } type BlockData struct { - Finder string `json:"finder"` Height int64 `json:"height"` Timestamp int64 `json:"timestamp"` Difficulty int64 `json:"difficulty"` @@ -102,7 +101,7 @@ func (b *BlockData) RoundKey() string { } func (b *BlockData) key() string { - return join(b.UncleHeight, b.Orphan, b.Nonce, b.serializeHash(), b.Timestamp, b.Difficulty, b.TotalShares, b.Reward, b.Finder) + return join(b.UncleHeight, b.Orphan, b.Nonce, b.serializeHash(), b.Timestamp, b.Difficulty, b.TotalShares, b.Reward) } type Miner struct { @@ -1138,7 +1137,6 @@ func convertCandidateResults(raw *redis.ZSliceCmd) []*BlockData { block.Timestamp, _ = strconv.ParseInt(fields[3], 10, 64) block.Difficulty, _ = strconv.ParseInt(fields[4], 10, 64) block.TotalShares, _ = strconv.ParseInt(fields[5], 10, 64) - block.Finder = fields[6] block.candidateKey = v.Member.(string) result = append(result, &block) } @@ -1184,7 +1182,6 @@ func convertBlockResults(rows ...*redis.ZSliceCmd) []*BlockData { block.TotalShares, _ = strconv.ParseInt(fields[6], 10, 64) block.RewardString = fields[7] block.ImmatureReward = fields[7] - block.Finder = fields[8] block.immatureKey = v.Member.(string) result = append(result, &block) } diff --git a/www/app/templates/blocks/block.hbs b/www/app/templates/blocks/block.hbs index d6dd7a9..f621757 100644 --- a/www/app/templates/blocks/block.hbs +++ b/www/app/templates/blocks/block.hbs @@ -1,32 +1,26 @@ {{#if block.uncle}} - {{format-number block.height}} + {{format-number block.height}} {{else}} - {{format-number block.height}} + {{format-number block.height}} {{/if}} {{#if block.uncle}} - - {{#link-to 'account' block.finder class='hash'}}{{block.finder}}{{/link-to}} - + {{block.hash}} {{else if block.orphan}} - {{t "block.orphan"}} + {{t "block.orphan"}} {{else}} - - {{#link-to 'account' block.finder class='hash'}}{{block.finder}}{{/link-to}} - + {{block.hash}} {{/if}} {{format-date-locale block.timestamp}} {{#if block.isLucky}} - {{format-number block.variance style='percent'}} + {{format-number block.variance style='percent'}} {{else}} - {{format-number block.variance style='percent'}} + {{format-number block.variance style='percent'}} {{/if}} @@ -36,4 +30,4 @@ {{block.formatReward}} {{/if}} - \ No newline at end of file +