Skip to content
This repository has been archived by the owner on Oct 4, 2019. It is now read-only.

Commit

Permalink
Merge e5fb480 into 402c170
Browse files Browse the repository at this point in the history
  • Loading branch information
whilei committed Jun 4, 2017
2 parents 402c170 + e5fb480 commit e2b8789
Show file tree
Hide file tree
Showing 8 changed files with 1,068 additions and 44 deletions.
4 changes: 2 additions & 2 deletions core/chain_manager.go
Expand Up @@ -227,13 +227,13 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,

// Mutate the state and block according to any hard-fork specs
if config == nil {
config = MakeChainConfig()
config = DefaultConfig // MakeChainConfig()
}
// Execute any user modifications to the block and finalize it
if gen != nil {
gen(i, b)
}
AccumulateRewards(statedb, h, b.uncles)
AccumulateRewards(config, statedb, h, b.uncles)
root, err := statedb.Commit()
if err != nil {
panic(fmt.Sprintf("state write error: %v", err))
Expand Down
20 changes: 20 additions & 0 deletions core/config.go
Expand Up @@ -321,6 +321,26 @@ func (c *ChainConfig) GetFeature(num *big.Int, id string) (*ForkFeature, *Fork,
return okForkFeature, okFork, found
}

// HasFeature looks up if fork feature exists on any fork at any block in the configuration.
// In case of multiple same-'id'd features, returns latest (assuming forks are sorted).
func (c *ChainConfig) HasFeature(id string) (*ForkFeature, *Fork, bool) {
var okForkFeature = &ForkFeature{}
var okFork = &Fork{}
var found = false
if id != "" {
for _, f := range c.Forks {
for _, ff := range f.Features {
if ff.ID == id {
okForkFeature = ff
okFork = f
found = true
}
}
}
}
return okForkFeature, okFork, found
}

func (c *ChainConfig) HeaderCheck(h *types.Header) error {
for _, fork := range c.Forks {
if fork.Block.Cmp(h.Number) != 0 {
Expand Down
84 changes: 61 additions & 23 deletions core/config_test.go
Expand Up @@ -231,15 +231,44 @@ func TestMakeGenesisDump2(t *testing.T) {
}
}

func makeTestChainConfig() *ChainConfig {
func getDefaultChainConfigSorted() *ChainConfig {
return DefaultConfig.SortForks()
}

// Unit-y tests.

func TestChainConfig_HasFeature(t *testing.T) {
c := TestConfig.SortForks()
for _, id := range allAvailableTestnetConfigKeys {
if _, _, ok := c.HasFeature(id); !ok {
t.Errorf("feature not found: %v", id)
}
}
c = getDefaultChainConfigSorted()
for _, id := range allAvailableDefaultConfigKeys {
if _, _, ok := c.HasFeature(id); !ok {
t.Errorf("feature not found: %v", id)
}
}

// never gets unavailable keys
c = TestConfig.SortForks()
for _, id := range unavailableConfigKeys {
if _, _, ok := c.HasFeature(id); ok {
t.Errorf("nonexisting feature found: %v", id)
}
}
c = getDefaultChainConfigSorted()
for _, id := range unavailableConfigKeys {
if _, _, ok := c.HasFeature(id); ok {
t.Errorf("nonexisting feature found: %v", id)
}
}
}

// TestChainConfig_GetFeature should be able to get all features described in DefaultConfig.
func TestChainConfig_GetFeature(t *testing.T) {
c := makeTestChainConfig()
c := getDefaultChainConfigSorted()
var dict = make(map[*big.Int][]string)
for _, fork := range c.Forks {
for _, feat := range fork.Features {
Expand All @@ -256,43 +285,52 @@ func TestChainConfig_GetFeature(t *testing.T) {
}
}

var allAvailableConfigKeys = []string{
var allAvailableDefaultConfigKeys = []string{
"difficulty",
"gastable",
"eip155",
}
var allAvailableTestnetConfigKeys = []string{
"difficulty",
"gastable",
"eip155",
"reward",
}
var unavailableConfigKeys = []string{
"foo",
"bar",
"monkey",
}

// veryHighBlock is a block in the far distant future (so far, in fact, that it will never actually exist)
// Used to test cumulative aggregation functions, ie "eventually".
var veryHighBlock *big.Int = big.NewInt(250000000)

// TestChainConfig_EventuallyGetAllPossibleFeatures should aggregate all available features from previous branches
func TestChainConfig_GetFeature2_EventuallyGetAllPossibleFeatures(t *testing.T) {
c := makeTestChainConfig()
for _, id := range allAvailableConfigKeys {
if _, _, ok := c.GetFeature(big.NewInt(5000000), id); !ok {
c := getDefaultChainConfigSorted()
for _, id := range allAvailableDefaultConfigKeys {
if _, _, ok := c.GetFeature(veryHighBlock, id); !ok {
t.Errorf("could not get feature with id: %v, at block: %v", id, big.NewInt(5000000))
}
}
}

var unavailableConfigKeys = []string{
"foo",
"bar",
"monkey",
}

// TestChainConfig_NeverGetNonexistantFeatures should never eventually collect features that don't exist
func TestChainConfig_GetFeature3_NeverGetNonexistantFeatures(t *testing.T) {
c := makeTestChainConfig()
c := getDefaultChainConfigSorted()
for _, id := range unavailableConfigKeys {
if feat, _, ok := c.GetFeature(big.NewInt(5000000), id); ok {
if feat, _, ok := c.GetFeature(veryHighBlock, id); ok {
t.Errorf("found unexpected feature: %v, for name: %v, at block: %v", feat, id, big.NewInt(5000000))
}
}
}

func TestChainConfig_GetFeature4_WorkForHighNumbers(t *testing.T) {
c := makeTestChainConfig()
highBlock := big.NewInt(99999999999999999)
if _, _, ok := c.GetFeature(highBlock, "difficulty"); !ok {
t.Errorf("unexpected unfound difficulty feature for far-future block: %v", highBlock)
c := getDefaultChainConfigSorted()
ultraHighBlock := big.NewInt(99999999999999999)
if _, _, ok := c.GetFeature(ultraHighBlock, "difficulty"); !ok {
t.Errorf("unexpected unfound difficulty feature for far-future block: %v", ultraHighBlock)
}
}

Expand Down Expand Up @@ -339,7 +377,7 @@ func TestChainConfig_GetChainID(t *testing.T) {

// TestChainConfig_GetFeature_DefaultEIP155 should get the eip155 feature for (only and above) its default implemented block.
func TestChainConfig_GetFeature5_DefaultEIP155(t *testing.T) {
c := makeTestChainConfig()
c := getDefaultChainConfigSorted()
var tables = map[*big.Int]*big.Int{
big.NewInt(0).Sub(DefaultConfig.ForkByName("Homestead").Block, big.NewInt(1)): nil,
DefaultConfig.ForkByName("Homestead").Block: nil,
Expand Down Expand Up @@ -376,7 +414,7 @@ func TestChainConfig_GetFeature5_DefaultEIP155(t *testing.T) {

// TestChainConfig_GetFeature_DefaultGasTables sets that GetFeatures gets expected feature values for default fork configs.
func TestChainConfig_GetFeature6_DefaultGasTables(t *testing.T) {
c := makeTestChainConfig()
c := getDefaultChainConfigSorted()
var tables = map[*big.Int]string{
big.NewInt(0).Sub(DefaultConfig.ForkByName("Homestead").Block, big.NewInt(1)): "",
DefaultConfig.ForkByName("Homestead").Block: "homestead",
Expand Down Expand Up @@ -413,7 +451,7 @@ func TestChainConfig_GetFeature6_DefaultGasTables(t *testing.T) {

// TestChainConfig_GetFeature_DefaultGasTables sets that GetFeatures gets expected feature values for default fork configs.
func TestChainConfig_GetFeature7_DefaultDifficulty(t *testing.T) {
c := makeTestChainConfig()
c := getDefaultChainConfigSorted()
var tables = map[*big.Int]string{
big.NewInt(0).Sub(DefaultConfig.ForkByName("Homestead").Block, big.NewInt(1)): "",
DefaultConfig.ForkByName("Homestead").Block: "homestead",
Expand Down Expand Up @@ -450,7 +488,7 @@ func TestChainConfig_GetFeature7_DefaultDifficulty(t *testing.T) {

func TestChainConfig_SortForks(t *testing.T) {
// check code data default
c := makeTestChainConfig()
c := getDefaultChainConfigSorted()
n := big.NewInt(0)
for _, fork := range c.Forks {
if n.Cmp(fork.Block) > 0 {
Expand All @@ -475,7 +513,7 @@ func TestChainConfig_SortForks(t *testing.T) {
}

func TestChainConfig_GetSigner(t *testing.T) {
c := makeTestChainConfig()
c := getDefaultChainConfigSorted()
var forkBlocks []*big.Int
for _, fork := range c.Forks {
forkBlocks = append(forkBlocks, fork.Block)
Expand Down
7 changes: 7 additions & 0 deletions core/data_chainconfig.go
Expand Up @@ -176,6 +176,13 @@ var TestConfig = &ChainConfig{
"length": 2000000,
},
},
{
ID: "reward",
Options: ChainFeatureConfigOptions{
"type": "ecip1017",
"era": 5000000,
},
},
},
},
},
Expand Down
152 changes: 135 additions & 17 deletions core/state_processor.go
Expand Up @@ -19,7 +19,9 @@ package core
import (
"math/big"

"errors"
"fmt"

"github.com/ethereumproject/go-ethereum/core/state"
"github.com/ethereumproject/go-ethereum/core/types"
"github.com/ethereumproject/go-ethereum/core/vm"
Expand All @@ -29,9 +31,13 @@ import (
)

var (
BlockReward = big.NewInt(5e+18)
big8 = big.NewInt(8)
big32 = big.NewInt(32)
MaximumBlockReward = big.NewInt(5e+18) // that's shiny 5 ether
big8 = big.NewInt(8)
big32 = big.NewInt(32)
DisinflationRateQuotient = big.NewInt(4)
DisinflationRateDivisor = big.NewInt(5)

ErrConfiguration = errors.New("invalid configuration")
)

// StateProcessor is a basic Processor, which takes care of transitioning
Expand Down Expand Up @@ -87,7 +93,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB) (ty
receipts = append(receipts, receipt)
allLogs = append(allLogs, logs...)
}
AccumulateRewards(statedb, header, block.Uncles())
AccumulateRewards(p.config, statedb, header, block.Uncles())

return receipts, allLogs, totalUsedGas, err
}
Expand Down Expand Up @@ -128,18 +134,130 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb
// mining reward. The total reward consists of the static block reward
// and rewards for included uncles. The coinbase of each uncle block is
// also rewarded.
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
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)
statedb.AddBalance(uncle.Coinbase, r)

r.Div(BlockReward, big32)
reward.Add(reward, r)
func AccumulateRewards(config *ChainConfig, statedb *state.StateDB, header *types.Header, uncles []*types.Header) {

// An uncle is a block that would be considered an orphan because its not on the longest chain (it's an alternative block at the same height as your parent).
// https://www.reddit.com/r/ethereum/comments/3c9jbf/wtf_are_uncles_and_why_do_they_matter/

// uncle.Number = 2,535,998 // assuming "latest" uncle...
// block.Number = 2,534,999 // uncles can be at same height as each other
// ... as uncles get older (within validation; <=n-7), reward drops

// Since ECIP1017 impacts "Era 1" idempotently and with constant 0-block based eras,
// we don't care about where the block/fork implementing it is.
feat, _, configured := config.HasFeature("reward")
if !configured {
reward := new(big.Int).Set(MaximumBlockReward)
r := new(big.Int)

for _, uncle := range uncles {
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, MaximumBlockReward) // 7 * 5e+18 = 35e+18
r.Div(r, big8) // 35e+18 / 8 = 7/8 * 5e+18

statedb.AddBalance(uncle.Coinbase, r) // $$

r.Div(MaximumBlockReward, big32) // 5e+18 / 32
reward.Add(reward, r) // 5e+18 + (1/32*5e+18)
}
statedb.AddBalance(header.Coinbase, reward) // $$ => 5e+18 + (1/32*5e+18)
} else {
// Check that configuration specifies ECIP1017.
val, ok := feat.GetString("type")
if !ok || val != "ecip1017" {
panic(ErrConfiguration)
}

// Ensure value 'era' is configured.
eraLen, ok := feat.GetBigInt("era")
if !ok || eraLen.Cmp(big.NewInt(0)) <= 0 {
panic(ErrConfiguration)
}

era := GetBlockEra(header.Number, eraLen)

wr := GetBlockWinnerRewardByEra(era) // wr "winner reward". 5, 4, 3.2, 2.56, ...

wurs := GetBlockWinnerRewardForUnclesByEra(era, uncles) // wurs "winner uncle rewards"
wr.Add(wr, wurs)

statedb.AddBalance(header.Coinbase, wr) // $$

// Reward uncle miners.
for _, uncle := range uncles {
ur := GetBlockUncleRewardByEra(era, header, uncle)
statedb.AddBalance(uncle.Coinbase, ur) // $$
}
}
statedb.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) *big.Int {
return new(big.Int).Div(GetBlockWinnerRewardByEra(era), big32)
}

// GetBlockUncleRewardByEra gets called _for each uncle miner_ associated with a winner block's uncles.
func GetBlockUncleRewardByEra(era *big.Int, header, uncle *types.Header) *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, MaximumBlockReward) // 7 * 5e+18 = 35e+18
r.Div(r, big8) // 35e+18 / 8 = 7/8 * 5e+18

return r
}
return getEraUncleBlockReward(era)
}

// 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) *big.Int {
r := big.NewInt(0)

for range uncles {
r.Add(r, getEraUncleBlockReward(era)) // 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) *big.Int {
if era.Cmp(big.NewInt(0)) == 0 {
return new(big.Int).Set(MaximumBlockReward)
}

// 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(MaximumBlockReward, 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 blockNum.Cmp(big.NewInt(0)) <= 0 {
return big.NewInt(0)
}

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)
}

0 comments on commit e2b8789

Please sign in to comment.