From 9206a1ed6f19c73fe17c38cf639c030fe9618721 Mon Sep 17 00:00:00 2001 From: Ellaismer Date: Tue, 27 Mar 2018 05:19:52 -0400 Subject: [PATCH] ECIP1017 implementation for multi-geth --- consensus/ethash/consensus.go | 113 ++++++++++++++++++++++++++++++---- params/config.go | 21 +++++-- 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 6ec40a0a52..6d79a73541 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -40,6 +40,8 @@ var ( ByzantiumBlockReward *big.Int = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium maxUncles = 2 // Maximum number of uncles allowed in a single block allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks + DisinflationRateQuotient = big.NewInt(4) // Disinflation rate quotient for ECIP1017 + DisinflationRateDivisor = big.NewInt(5) // Disinflation rate divisor for ECIP1017 ) // Various error messages to mark blocks invalid. These should be private to @@ -579,18 +581,105 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header if config.IsByzantium(header.Number) { blockReward = ByzantiumBlockReward } - // Accumulate the rewards for the miner and any included uncles - reward := new(big.Int).Set(blockReward) - r := new(big.Int) - for _, uncle := range uncles { - r.Add(uncle.Number, big8) - r.Sub(r, header.Number) - r.Mul(r, blockReward) - r.Div(r, big8) - state.AddBalance(uncle.Coinbase, r) + if config.HasECIP1017() { + // Ensure value 'era' is configured. + eraLen := config.ECIP1017EraRounds + era := GetBlockEra(header.Number, eraLen) + wr := GetBlockWinnerRewardByEra(era, blockReward) // wr "winner reward". 5, 4, 3.2, 2.56, ... + wurs := GetBlockWinnerRewardForUnclesByEra(era, uncles, blockReward) // wurs "winner uncle rewards" + wr.Add(wr, wurs) + state.AddBalance(header.Coinbase, wr) // $$ - r.Div(blockReward, big32) - reward.Add(reward, r) + // Reward uncle miners. + for _, uncle := range uncles { + ur := GetBlockUncleRewardByEra(era, header, uncle, blockReward) + state.AddBalance(uncle.Coinbase, ur) // $$ + } + } else { + // Accumulate the rewards for the miner and any included uncles + reward := new(big.Int).Set(blockReward) + r := new(big.Int) + for _, uncle := range uncles { + r.Add(uncle.Number, big8) + r.Sub(r, header.Number) + r.Mul(r, blockReward) + r.Div(r, big8) + state.AddBalance(uncle.Coinbase, r) + + r.Div(blockReward, big32) + reward.Add(reward, r) + } + state.AddBalance(header.Coinbase, reward) } - state.AddBalance(header.Coinbase, reward) +} + +// As of "Era 2" (zero-index era 1), uncle miners and winners are rewarded equally for each included block. +// So they share this function. +func getEraUncleBlockReward(era *big.Int, blockReward *big.Int) *big.Int { + return new(big.Int).Div(GetBlockWinnerRewardByEra(era, blockReward), big32) +} + +// GetBlockUncleRewardByEra gets called _for each uncle miner_ associated with a winner block's uncles. +func GetBlockUncleRewardByEra(era *big.Int, header, uncle *types.Header, blockReward *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(uncle.Number, big8) // 2,534,998 + 8 = 2,535,006 + r.Sub(r, header.Number) // 2,535,006 - 2,534,999 = 7 + r.Mul(r, blockReward) // 7 * 5e+18 = 35e+18 + r.Div(r, big8) // 35e+18 / 8 = 7/8 * 5e+18 + + return r + } + return getEraUncleBlockReward(era, blockReward) +} + +// GetBlockWinnerRewardForUnclesByEra gets called _per winner_, and accumulates rewards for each included uncle. +// Assumes uncles have been validated and limited (@ func (v *BlockValidator) VerifyUncles). +func GetBlockWinnerRewardForUnclesByEra(era *big.Int, uncles []*types.Header, blockReward *big.Int) *big.Int { + r := big.NewInt(0) + + for range uncles { + r.Add(r, getEraUncleBlockReward(era, blockReward)) // can reuse this, since 1/32 for winner's uncles remain unchanged from "Era 1" + } + return r +} + +// 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) + } + + 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) } diff --git a/params/config.go b/params/config.go index 09209568d5..97643af5b5 100644 --- a/params/config.go +++ b/params/config.go @@ -59,6 +59,7 @@ var ( ByzantiumBlock: nil, DisposalBlock: big.NewInt(0), ConstantinopleBlock: nil, + ECIP1017EraRounds: big.NewInt(10000000), Ethash: new(EthashConfig), } @@ -102,16 +103,16 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -139,6 +140,8 @@ type ChainConfig struct { ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) + ECIP1017EraRounds *big.Int `json:"ecip1017EraRounds,omitempty"` // ECIP1017 era rounds + // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` @@ -174,7 +177,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v, Disposal: %v, Constantinople: %v Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v, Disposal: %v, ECIP1017: %v, Constantinople: %v, Engine: %v}", c.ChainId, c.HomesteadBlock, c.DAOForkBlock, @@ -184,11 +187,21 @@ func (c *ChainConfig) String() string { c.EIP158Block, c.ByzantiumBlock, c.DisposalBlock, + c.ECIP1017EraRounds, c.ConstantinopleBlock, engine, ) } +// HasECIP1017 returns whether the chain is configured with ECIP1017. +func (c *ChainConfig) HasECIP1017() bool { + if c.ECIP1017EraRounds == nil { + return false + } else { + return true + } +} + // IsHomestead returns whether num is either equal to the homestead block or greater. func (c *ChainConfig) IsHomestead(num *big.Int) bool { return isForked(c.HomesteadBlock, num)