diff --git a/core/chain_manager.go b/core/chain_manager.go index 2d520ca7b..83fa2dd1f 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -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)) diff --git a/core/config.go b/core/config.go index 43e564541..49f22bf9e 100644 --- a/core/config.go +++ b/core/config.go @@ -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 { diff --git a/core/config_test.go b/core/config_test.go index bf4e538dd..20c116b9b 100644 --- a/core/config_test.go +++ b/core/config_test.go @@ -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 { @@ -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) } } @@ -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, @@ -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", @@ -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", @@ -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 { @@ -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) diff --git a/core/data_chainconfig.go b/core/data_chainconfig.go index 1c0324137..0272c4300 100644 --- a/core/data_chainconfig.go +++ b/core/data_chainconfig.go @@ -176,6 +176,13 @@ var TestConfig = &ChainConfig{ "length": 2000000, }, }, + { + ID: "reward", + Options: ChainFeatureConfigOptions{ + "type": "ecip1017", + "era": 5000000, + }, + }, }, }, }, diff --git a/core/state_processor.go b/core/state_processor.go index e10b6785c..ff143e394 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -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" @@ -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 @@ -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 } @@ -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) } diff --git a/core/state_processor_test.go b/core/state_processor_test.go new file mode 100644 index 000000000..b07c9393c --- /dev/null +++ b/core/state_processor_test.go @@ -0,0 +1,835 @@ +package core + +import ( + "math/big" + "math/rand" + "testing" + "time" + + "github.com/ethereumproject/go-ethereum/common" + "github.com/ethereumproject/go-ethereum/core/state" + "github.com/ethereumproject/go-ethereum/core/types" + "github.com/ethereumproject/go-ethereum/ethdb" +) + +var defaultEraLength *big.Int = big.NewInt(5000000) + +// Use default era length 5,000,000 +func TestGetBlockEra1(t *testing.T) { + cases := map[*big.Int]*big.Int{ + big.NewInt(0): big.NewInt(0), + big.NewInt(1): big.NewInt(0), + big.NewInt(1914999): big.NewInt(0), + big.NewInt(1915000): big.NewInt(0), + big.NewInt(1915001): big.NewInt(0), + big.NewInt(4999999): big.NewInt(0), + big.NewInt(5000000): big.NewInt(0), + big.NewInt(5000001): big.NewInt(1), + big.NewInt(9999999): big.NewInt(1), + big.NewInt(10000000): big.NewInt(1), + big.NewInt(10000001): big.NewInt(2), + big.NewInt(14999999): big.NewInt(2), + big.NewInt(15000000): big.NewInt(2), + big.NewInt(15000001): big.NewInt(3), + big.NewInt(100000001): big.NewInt(20), + big.NewInt(123456789): big.NewInt(24), + } + + for bn, expectedEra := range cases { + gotEra := GetBlockEra(bn, defaultEraLength) + if gotEra.Cmp(expectedEra) != 0 { + t.Errorf("got: %v, want: %v", gotEra, expectedEra) + } + } +} + +// Use custom era length 2 +func TestGetBlockEra2(t *testing.T) { + cases := map[*big.Int]*big.Int{ + big.NewInt(0): big.NewInt(0), + big.NewInt(1): big.NewInt(0), + big.NewInt(2): big.NewInt(0), + big.NewInt(3): big.NewInt(1), + big.NewInt(4): big.NewInt(1), + big.NewInt(5): big.NewInt(2), + big.NewInt(6): big.NewInt(2), + big.NewInt(7): big.NewInt(3), + big.NewInt(8): big.NewInt(3), + big.NewInt(9): big.NewInt(4), + big.NewInt(10): big.NewInt(4), + big.NewInt(11): big.NewInt(5), + big.NewInt(12): big.NewInt(5), + } + + for bn, expectedEra := range cases { + gotEra := GetBlockEra(bn, big.NewInt(2)) + if gotEra.Cmp(expectedEra) != 0 { + t.Errorf("got: %v, want: %v", gotEra, expectedEra) + } + } +} + +func TestGetBlockWinnerRewardByEra(t *testing.T) { + + cases := map[*big.Int]*big.Int{ + big.NewInt(0): MaximumBlockReward, + big.NewInt(1): MaximumBlockReward, + big.NewInt(4999999): MaximumBlockReward, + big.NewInt(5000000): MaximumBlockReward, + big.NewInt(5000001): big.NewInt(4e+18), + big.NewInt(9999999): big.NewInt(4e+18), + big.NewInt(10000000): big.NewInt(4e+18), + big.NewInt(10000001): big.NewInt(3.2e+18), + big.NewInt(14999999): big.NewInt(3.2e+18), + big.NewInt(15000000): big.NewInt(3.2e+18), + big.NewInt(15000001): big.NewInt(2.56e+18), + } + + for bn, expectedReward := range cases { + gotReward := GetBlockWinnerRewardByEra(GetBlockEra(bn, defaultEraLength)) + if gotReward.Cmp(expectedReward) != 0 { + t.Errorf("@ %v, got: %v, want: %v", bn, gotReward, expectedReward) + } + if gotReward.Cmp(big.NewInt(0)) <= 0 { + t.Errorf("@ %v, got: %v, want: %v", bn, gotReward, expectedReward) + } + if gotReward.Cmp(MaximumBlockReward) > 0 { + t.Errorf("@ %v, got: %v, want %v", bn, gotReward, expectedReward) + } + } + +} + +func TestGetBlockUncleRewardByEra(t *testing.T) { + + var we1, we2, we3, we4 *big.Int = new(big.Int), new(big.Int), new(big.Int), new(big.Int) + + // manually divide maxblockreward/32 to compare to got + we2.Div(GetBlockWinnerRewardByEra(GetBlockEra(big.NewInt(5000001), defaultEraLength)), big.NewInt(32)) + we3.Div(GetBlockWinnerRewardByEra(GetBlockEra(big.NewInt(10000001), defaultEraLength)), big.NewInt(32)) + we4.Div(GetBlockWinnerRewardByEra(GetBlockEra(big.NewInt(15000001), defaultEraLength)), big.NewInt(32)) + + cases := map[*big.Int]*big.Int{ + big.NewInt(0): nil, + big.NewInt(1): nil, + big.NewInt(4999999): nil, + big.NewInt(5000000): nil, + big.NewInt(5000001): we2, + big.NewInt(9999999): we2, + big.NewInt(10000000): we2, + big.NewInt(10000001): we3, + big.NewInt(14999999): we3, + big.NewInt(15000000): we3, + big.NewInt(15000001): we4, + } + + for bn, want := range cases { + + era := GetBlockEra(bn, defaultEraLength) + + var header, uncle *types.Header = &types.Header{}, &types.Header{} + header.Number = bn + + rand.Seed(time.Now().UTC().UnixNano()) + uncle.Number = big.NewInt(0).Sub(header.Number, big.NewInt(int64(rand.Int31n(int32(7))))) + + got := GetBlockUncleRewardByEra(era, header, uncle) + + // "Era 1" + if want == nil { + we1.Add(uncle.Number, big8) // 2,534,998 + 8 = 2,535,006 + we1.Sub(we1, header.Number) // 2,535,006 - 2,534,999 = 7 + we1.Mul(we1, MaximumBlockReward) // 7 * 5e+18 = 35e+18 + we1.Div(we1, big8) // 35e+18 / 8 = 7/8 * 5e+18 + + if got.Cmp(we1) != 0 { + t.Errorf("@ %v, want: %v, got: %v", bn, we1, got) + } + } else { + if got.Cmp(want) != 0 { + t.Errorf("@ %v, want: %v, got: %v", bn, want, got) + } + } + } +} + +func TestGetBlockWinnerRewardForUnclesByEra(t *testing.T) { + + // "want era 1", "want era 2", ... + var we1, we2, we3, we4 *big.Int = new(big.Int), new(big.Int), new(big.Int), new(big.Int) + we1.Div(MaximumBlockReward, big.NewInt(32)) + we2.Div(GetBlockWinnerRewardByEra(big.NewInt(1)), big.NewInt(32)) + we3.Div(GetBlockWinnerRewardByEra(big.NewInt(2)), big.NewInt(32)) + we4.Div(GetBlockWinnerRewardByEra(big.NewInt(3)), big.NewInt(32)) + + cases := map[*big.Int]*big.Int{ + big.NewInt(0): we1, + big.NewInt(1): we1, + big.NewInt(4999999): we1, + big.NewInt(5000000): we1, + big.NewInt(5000001): we2, + big.NewInt(9999999): we2, + big.NewInt(10000000): we2, + big.NewInt(10000001): we3, + big.NewInt(14999999): we3, + big.NewInt(15000000): we3, + big.NewInt(15000001): we4, + } + + var uncleSingle, uncleDouble []*types.Header = []*types.Header{{}}, []*types.Header{{}, {}} + + for bn, want := range cases { + // test single uncle + got := GetBlockWinnerRewardForUnclesByEra(GetBlockEra(bn, defaultEraLength), uncleSingle) + if got.Cmp(want) != 0 { + t.Errorf("@ %v: want: %v, got: %v", bn, want, got) + } + + // test double uncle + got = GetBlockWinnerRewardForUnclesByEra(GetBlockEra(bn, defaultEraLength), uncleDouble) + dub := new(big.Int) + if got.Cmp(dub.Mul(want, big.NewInt(2))) != 0 { + t.Errorf("@ %v: want: %v, got: %v", bn, want, got) + } + } +} + +// Accruing over block cases simulates miner account winning many times. +// Uses maps of running sums for winner & 2 uncles to keep tally. +func TestAccumulateRewards1(t *testing.T) { + configs := []*ChainConfig{TestConfig} + for i, config := range configs { + db, _ := ethdb.NewMemDatabase() + + stateDB, err := state.New(common.Hash{}, db) + if err != nil { + t.Fatalf("could not open statedb: %v", err) + } + + var header *types.Header = &types.Header{} + var uncles []*types.Header = []*types.Header{{}, {}} + + if i == 0 { + header.Coinbase = common.StringToAddress("000d836201318ec6899a67540690382780743280") + uncles[0].Coinbase = common.StringToAddress("001762430ea9c3a26e5749afdb70da5f78ddbb8c") + uncles[1].Coinbase = common.StringToAddress("001d14804b399c6ef80e64576f657660804fec0b") + } else { + header.Coinbase = common.StringToAddress("0000000000000000000000000000000000000001") + uncles[0].Coinbase = common.StringToAddress("0000000000000000000000000000000000000002") + uncles[1].Coinbase = common.StringToAddress("0000000000000000000000000000000000000003") + } + + // Manual tallies for reward accumulation. + winnerB, totalB := new(big.Int), new(big.Int) + unclesB := []*big.Int{new(big.Int), new(big.Int)} + + winnerB = stateDB.GetBalance(header.Coinbase) + unclesB[0] = stateDB.GetBalance(uncles[0].Coinbase) + unclesB[1] = stateDB.GetBalance(uncles[1].Coinbase) + + totalB.Add(totalB, winnerB) + totalB.Add(totalB, unclesB[0]) + totalB.Add(totalB, unclesB[1]) + + if totalB.Cmp(big.NewInt(0)) != 0 { + t.Errorf("unexpected: %v", totalB) + } + + cases := []*big.Int{ + big.NewInt(11), // avoid messy if-switches for ommer availability/numbering + big.NewInt(4999999), + big.NewInt(5000000), + big.NewInt(5000001), + big.NewInt(9999999), + big.NewInt(10000000), + big.NewInt(10000000), + big.NewInt(10000001), + big.NewInt(14999999), + big.NewInt(15000000), + big.NewInt(15000001), + } + + for _, bn := range cases { + era := GetBlockEra(bn, defaultEraLength) + + header.Number = bn + + for i, uncle := range uncles { + + // Randomize uncle numbers with bound ( n-1 <= uncleNum <= n-7 ), where n is current head number + // See yellowpaper@11.1 for ommer validation reference. I expect n-7 is 6th-generation ommer. + // Note that ommer nth-generation impacts reward only for "Era 1". + rand.Seed(time.Now().UTC().UnixNano()) + + // 1 + [0..rand..7) == 1 + 0, 1 + 1, ... 1 + 6 + un := new(big.Int).Add(big.NewInt(1), big.NewInt(int64(rand.Int31n(int32(7))))) + uncle.Number = new(big.Int).Sub(header.Number, un) // n - un + + ur := GetBlockUncleRewardByEra(era, header, uncle) + unclesB[i].Add(unclesB[i], ur) + + totalB.Add(totalB, ur) + } + + wr := GetBlockWinnerRewardByEra(era) + wr.Add(wr, GetBlockWinnerRewardForUnclesByEra(era, uncles)) + winnerB.Add(winnerB, wr) + + totalB.Add(totalB, winnerB) + + AccumulateRewards(config, stateDB, header, uncles) + + // Check balances. + if wb := stateDB.GetBalance(header.Coinbase); wb.Cmp(winnerB) != 0 { + t.Errorf("winner balance @ %v, want: %v, got: %v (config: %v)", bn, winnerB, wb, i) + } + if uB0 := stateDB.GetBalance(uncles[0].Coinbase); unclesB[0].Cmp(uB0) != 0 { + t.Errorf("uncle1 balance @ %v, want: %v, got: %v (config: %v)", bn, unclesB[0], uB0, i) + } + if uB1 := stateDB.GetBalance(uncles[1].Coinbase); unclesB[1].Cmp(uB1) != 0 { + t.Errorf("uncle2 balance @ %v, want: %v, got: %v (config: %v)", bn, unclesB[1], uB1, i) + } + } + db.Close() + } +} + +var ( + WinnerCoinbase = common.StringToAddress("0000000000000000000000000000000000000001") + Uncle1Coinbase = common.StringToAddress("0000000000000000000000000000000000000002") + Uncle2Coinbase = common.StringToAddress("0000000000000000000000000000000000000003") + + Era1WinnerReward = big.NewInt(5e+18) + Era1WinnerUncleReward = big.NewInt(156250000000000000) + Era1UncleReward = big.NewInt(4375000000000000000) + + Era2WinnerReward = big.NewInt(4e+18) + Era2WinnerUncleReward = new(big.Int).Div(big.NewInt(4e+18), big32) + Era2UncleReward = new(big.Int).Div(big.NewInt(4e+18), big32) + + Era3WinnerReward = new(big.Int).Mul(new(big.Int).Div(Era2WinnerReward, big.NewInt(5)), big.NewInt(4)) + Era3WinnerUncleReward = new(big.Int).Div(new(big.Int).Mul(new(big.Int).Div(Era2WinnerReward, big.NewInt(5)), big.NewInt(4)), big32) + Era3UncleReward = new(big.Int).Div(new(big.Int).Mul(new(big.Int).Div(Era2WinnerReward, big.NewInt(5)), big.NewInt(4)), big32) + + Era4WinnerReward = new(big.Int).Mul(new(big.Int).Div(Era3WinnerReward, big.NewInt(5)), big.NewInt(4)) + Era4WinnerUncleReward = new(big.Int).Div(new(big.Int).Mul(new(big.Int).Div(Era3WinnerReward, big.NewInt(5)), big.NewInt(4)), big32) + Era4UncleReward = new(big.Int).Div(new(big.Int).Mul(new(big.Int).Div(Era3WinnerReward, big.NewInt(5)), big.NewInt(4)), big32) +) + +// Non-accruing over block cases simulates instance, +// ie. a miner wins once at different blocks. +// +// Tests winner includes 2 ommer headers. +func TestAccumulateRewards2_2Uncles(t *testing.T) { + + type rewards map[common.Address]*big.Int + + configs := []*ChainConfig{DefaultConfig, TestConfig} + cases := []struct { + block *big.Int + rewards rewards + }{ + { + block: big.NewInt(2), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era1UncleReward, + Uncle2Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(13), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era1UncleReward, + Uncle2Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(1914999), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era1UncleReward, + Uncle2Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(1915000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era1UncleReward, + Uncle2Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(1915001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era1UncleReward, + Uncle2Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(4999999), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era1UncleReward, + Uncle2Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(5000001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era2WinnerReward, new(big.Int).Mul(Era2WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era2UncleReward, + Uncle2Coinbase: Era2UncleReward, + }, + }, + { + block: big.NewInt(5000010), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era2WinnerReward, new(big.Int).Mul(Era2WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era2UncleReward, + Uncle2Coinbase: Era2UncleReward, + }, + }, + { + block: big.NewInt(10000000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era2WinnerReward, new(big.Int).Mul(Era2WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era2UncleReward, + Uncle2Coinbase: Era2UncleReward, + }, + }, + { + block: big.NewInt(10000001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era3WinnerReward, new(big.Int).Mul(Era3WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era3UncleReward, + Uncle2Coinbase: Era3UncleReward, + }, + }, + { + block: big.NewInt(15000000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era3WinnerReward, new(big.Int).Mul(Era3WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era3UncleReward, + Uncle2Coinbase: Era3UncleReward, + }, + }, + { + block: big.NewInt(15000001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era4WinnerReward, new(big.Int).Mul(Era4WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era4UncleReward, + Uncle2Coinbase: Era4UncleReward, + }, + }, + { + block: big.NewInt(20000000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era4WinnerReward, new(big.Int).Mul(Era4WinnerUncleReward, common.Big2)), + Uncle1Coinbase: Era4UncleReward, + Uncle2Coinbase: Era4UncleReward, + }, + }, + } + + for i, config := range configs { + for _, c := range cases { + + db, _ := ethdb.NewMemDatabase() + stateDB, err := state.New(common.Hash{}, db) + if err != nil { + t.Fatalf("could not open statedb: %v", err) + } + + var winner *types.Header = &types.Header{ + Number: c.block, + Coinbase: WinnerCoinbase, + } + var uncles []*types.Header = []*types.Header{{ + Number: new(big.Int).Sub(c.block, common.Big1), // use 1st-generation ommer, since random n-[1,7) is tested by accrual above + Coinbase: Uncle1Coinbase, + }, { + Number: new(big.Int).Sub(c.block, common.Big1), + Coinbase: Uncle2Coinbase, + }} + + gotWinnerBalance := stateDB.GetBalance(winner.Coinbase) + gotUncle1Balance := stateDB.GetBalance(Uncle1Coinbase) + gotUncle2Balance := stateDB.GetBalance(Uncle2Coinbase) + r := new(big.Int) + r.Add(gotWinnerBalance, gotUncle1Balance) + r.Add(r, gotUncle2Balance) + if r.Cmp(big.NewInt(0)) != 0 { + t.Errorf("unexpected: %v", r) + } + + AccumulateRewards(config, stateDB, winner, uncles) + gotWinnerBalance = stateDB.GetBalance(winner.Coinbase) + gotUncle1Balance = stateDB.GetBalance(Uncle1Coinbase) + gotUncle2Balance = stateDB.GetBalance(Uncle2Coinbase) + + // Use config if possible. Currently on testnet only. + eraLen := new(big.Int) + feat, _, configured := config.HasFeature("reward") + if !configured { + eraLen = defaultEraLength + } else { + elen, ok := feat.GetBigInt("era") + if !ok { + t.Error("unexpected reward length not configured") + } else { + eraLen = elen + } + } + era := GetBlockEra(c.block, eraLen) + + // Check balances. + if configured { + if gotWinnerBalance.Cmp(c.rewards[WinnerCoinbase]) != 0 { + t.Errorf("Config: %v | Era %v: winner balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, c.rewards[WinnerCoinbase], gotWinnerBalance, new(big.Int).Sub(gotWinnerBalance, c.rewards[WinnerCoinbase])) + } + if gotUncle1Balance.Cmp(c.rewards[Uncle1Coinbase]) != 0 { + t.Errorf("Config: %v | Era %v: uncle1 balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, c.rewards[Uncle1Coinbase], gotUncle1Balance, new(big.Int).Sub(gotUncle1Balance, c.rewards[Uncle1Coinbase])) + } + if gotUncle2Balance.Cmp(c.rewards[Uncle2Coinbase]) != 0 { + t.Errorf("Config: %v | Era %v: uncle2 balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, c.rewards[Uncle2Coinbase], gotUncle2Balance, new(big.Int).Sub(gotUncle2Balance, c.rewards[Uncle2Coinbase])) + } + } else { + if gotWinnerBalance.Cmp(new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, big.NewInt(2)))) != 0 { + t.Errorf("Config: %v | Era %v: winner balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, big.NewInt(2))), gotWinnerBalance, new(big.Int).Sub(gotWinnerBalance, c.rewards[WinnerCoinbase])) + } + if gotUncle1Balance.Cmp(Era1UncleReward) != 0 { + t.Errorf("Config: %v | Era %v: uncle1 balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, Era1UncleReward, gotUncle1Balance, new(big.Int).Sub(gotUncle1Balance, c.rewards[Uncle1Coinbase])) + } + if gotUncle2Balance.Cmp(Era1UncleReward) != 0 { + t.Errorf("Config: %v | Era %v: uncle2 balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, Era1UncleReward, gotUncle2Balance, new(big.Int).Sub(gotUncle2Balance, c.rewards[Uncle2Coinbase])) + } + } + + db.Close() + } + } +} + +// Non-accruing over block cases simulates instance, +// ie. a miner wins once at different blocks. +// +// Tests winner includes 1 ommer header. +func TestAccumulateRewards3_1Uncle(t *testing.T) { + + type rewards map[common.Address]*big.Int + + configs := []*ChainConfig{DefaultConfig, TestConfig} + cases := []struct { + block *big.Int + rewards rewards + }{ + { + block: big.NewInt(2), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, Era1WinnerUncleReward), + Uncle1Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(13), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, Era1WinnerUncleReward), + Uncle1Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(1914999), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, Era1WinnerUncleReward), + Uncle1Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(1915000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, Era1WinnerUncleReward), + Uncle1Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(1915001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, Era1WinnerUncleReward), + Uncle1Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(4999999), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era1WinnerReward, Era1WinnerUncleReward), + Uncle1Coinbase: Era1UncleReward, + }, + }, + { + block: big.NewInt(5000001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era2WinnerReward, Era2WinnerUncleReward), + Uncle1Coinbase: Era2UncleReward, + }, + }, + { + block: big.NewInt(5000010), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era2WinnerReward, Era2WinnerUncleReward), + Uncle1Coinbase: Era2UncleReward, + }, + }, + { + block: big.NewInt(10000000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era2WinnerReward, Era2WinnerUncleReward), + Uncle1Coinbase: Era2UncleReward, + }, + }, + { + block: big.NewInt(10000001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era3WinnerReward, Era3WinnerUncleReward), + Uncle1Coinbase: Era3UncleReward, + }, + }, + { + block: big.NewInt(15000000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era3WinnerReward, Era3WinnerUncleReward), + Uncle1Coinbase: Era3UncleReward, + }, + }, + { + block: big.NewInt(15000001), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era4WinnerReward, Era4WinnerUncleReward), + Uncle1Coinbase: Era4UncleReward, + }, + }, + { + block: big.NewInt(20000000), + rewards: rewards{ + WinnerCoinbase: new(big.Int).Add(Era4WinnerReward, Era4WinnerUncleReward), + Uncle1Coinbase: Era4UncleReward, + }, + }, + } + + for i, config := range configs { + for _, c := range cases { + + db, _ := ethdb.NewMemDatabase() + stateDB, err := state.New(common.Hash{}, db) + if err != nil { + t.Fatalf("could not open statedb: %v", err) + } + + var winner *types.Header = &types.Header{ + Number: c.block, + Coinbase: WinnerCoinbase, + } + var uncles []*types.Header = []*types.Header{{ + Number: new(big.Int).Sub(c.block, common.Big1), // use 1st-generation ommer, since random n-[1,7) is tested by accrual above + Coinbase: Uncle1Coinbase, + }} + + gotWinnerBalance := stateDB.GetBalance(winner.Coinbase) + gotUncle1Balance := stateDB.GetBalance(Uncle1Coinbase) + r := new(big.Int) + r.Add(gotWinnerBalance, gotUncle1Balance) + if r.Cmp(big.NewInt(0)) != 0 { + t.Errorf("unexpected: %v", r) + } + + AccumulateRewards(config, stateDB, winner, uncles) + gotWinnerBalance = stateDB.GetBalance(winner.Coinbase) + gotUncle1Balance = stateDB.GetBalance(Uncle1Coinbase) + + // Use config if possible. Currently on testnet only. + eraLen := new(big.Int) + feat, _, configured := config.HasFeature("reward") + if !configured { + eraLen = defaultEraLength + } else { + elen, ok := feat.GetBigInt("era") + if !ok { + t.Error("unexpected reward length not configured") + } else { + eraLen = elen + } + } + era := GetBlockEra(c.block, eraLen) + + // Check balances. + if configured { + if gotWinnerBalance.Cmp(c.rewards[WinnerCoinbase]) != 0 { + t.Errorf("Config: %v | Era %v: winner balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, c.rewards[WinnerCoinbase], gotWinnerBalance, new(big.Int).Sub(gotWinnerBalance, c.rewards[WinnerCoinbase])) + } + if gotUncle1Balance.Cmp(c.rewards[Uncle1Coinbase]) != 0 { + t.Errorf("Config: %v | Era %v: uncle1 balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, c.rewards[Uncle1Coinbase], gotUncle1Balance, new(big.Int).Sub(gotUncle1Balance, c.rewards[Uncle1Coinbase])) + } + } else { + if gotWinnerBalance.Cmp(new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, big.NewInt(1)))) != 0 { + t.Errorf("Config: %v | Era %v: winner balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, new(big.Int).Add(Era1WinnerReward, new(big.Int).Mul(Era1WinnerUncleReward, big.NewInt(1))), gotWinnerBalance, new(big.Int).Sub(gotWinnerBalance, c.rewards[WinnerCoinbase])) + } + if gotUncle1Balance.Cmp(Era1UncleReward) != 0 { + t.Errorf("Config: %v | Era %v: uncle1 balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, Era1UncleReward, gotUncle1Balance, new(big.Int).Sub(gotUncle1Balance, c.rewards[Uncle1Coinbase])) + } + } + + db.Close() + } + } +} + +// Non-accruing over block cases simulates instance, +// ie. a miner wins once at different blocks. +// +// Tests winner includes 0 ommer headers. +func TestAccumulateRewards4_0Uncles(t *testing.T) { + + type rewards map[common.Address]*big.Int + + configs := []*ChainConfig{DefaultConfig, TestConfig} + cases := []struct { + block *big.Int + rewards rewards + }{ + { + block: big.NewInt(2), + rewards: rewards{ + WinnerCoinbase: Era1WinnerReward, + }, + }, + { + block: big.NewInt(13), + rewards: rewards{ + WinnerCoinbase: Era1WinnerReward, + }, + }, + { + block: big.NewInt(1914999), + rewards: rewards{ + WinnerCoinbase: Era1WinnerReward, + }, + }, + { + block: big.NewInt(1915000), + rewards: rewards{ + WinnerCoinbase: Era1WinnerReward, + }, + }, + { + block: big.NewInt(1915001), + rewards: rewards{ + WinnerCoinbase: Era1WinnerReward, + }, + }, + { + block: big.NewInt(4999999), + rewards: rewards{ + WinnerCoinbase: Era1WinnerReward, + }, + }, + { + block: big.NewInt(5000001), + rewards: rewards{ + WinnerCoinbase: Era2WinnerReward, + }, + }, + { + block: big.NewInt(5000010), + rewards: rewards{ + WinnerCoinbase: Era2WinnerReward, + }, + }, + { + block: big.NewInt(10000000), + rewards: rewards{ + WinnerCoinbase: Era2WinnerReward, + }, + }, + { + block: big.NewInt(10000001), + rewards: rewards{ + WinnerCoinbase: Era3WinnerReward, + }, + }, + { + block: big.NewInt(15000000), + rewards: rewards{ + WinnerCoinbase: Era3WinnerReward, + }, + }, + { + block: big.NewInt(15000001), + rewards: rewards{ + WinnerCoinbase: Era4WinnerReward, + }, + }, + { + block: big.NewInt(20000000), + rewards: rewards{ + WinnerCoinbase: Era4WinnerReward, + }, + }, + } + + for i, config := range configs { + for _, c := range cases { + + db, _ := ethdb.NewMemDatabase() + stateDB, err := state.New(common.Hash{}, db) + if err != nil { + t.Fatalf("could not open statedb: %v", err) + } + + var winner *types.Header = &types.Header{ + Number: c.block, + Coinbase: WinnerCoinbase, + } + var uncles []*types.Header = []*types.Header{} + + gotWinnerBalance := stateDB.GetBalance(winner.Coinbase) + if gotWinnerBalance.Cmp(big.NewInt(0)) != 0 { + t.Errorf("unexpected: %v", gotWinnerBalance) + } + + AccumulateRewards(config, stateDB, winner, uncles) + gotWinnerBalance = stateDB.GetBalance(winner.Coinbase) + + // Use config if possible. Currently on testnet only. + eraLen := new(big.Int) + feat, _, configured := config.HasFeature("reward") + if !configured { + eraLen = defaultEraLength + } else { + elen, ok := feat.GetBigInt("era") + if !ok { + t.Error("unexpected reward length not configured") + } else { + eraLen = elen + } + } + era := GetBlockEra(c.block, eraLen) + + // Check balances. + if configured { + if gotWinnerBalance.Cmp(c.rewards[WinnerCoinbase]) != 0 { + t.Errorf("Config: %v | Era %v: winner balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, c.rewards[WinnerCoinbase], gotWinnerBalance, new(big.Int).Sub(gotWinnerBalance, c.rewards[WinnerCoinbase])) + } + } else { + if gotWinnerBalance.Cmp(Era1WinnerReward) != 0 { + t.Errorf("Config: %v | Era %v: winner balance @ %v, want: %v, got: %v, \n-> diff: %v", i, era, c.block, Era1WinnerReward, gotWinnerBalance, new(big.Int).Sub(gotWinnerBalance, c.rewards[WinnerCoinbase])) + } + } + + db.Close() + } + } +} diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 961e2b117..14b7b7fdb 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -1781,7 +1781,13 @@ func testFastCriticalRestarts(t *testing.T, protocol int) { } // Wait to make sure all data is set after sync - time.Sleep(400 * time.Millisecond) + n := time.Now() + for tester.downloader.synchronising > 0 { + if time.Since(n) > time.Second { + break + } + } + t.Logf("required %v for downloader to synchronise", time.Since(n)) // Retry limit exhausted, downloader will switch to full sync, should succeed if err := tester.sync("peer", nil, FastSync); err != nil { diff --git a/miner/worker.go b/miner/worker.go index 88ed2d163..1d0033cb2 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -540,7 +540,7 @@ func (self *worker) commitNewWork() { if atomic.LoadInt32(&self.mining) == 1 { // commit state root after all state transitions. - core.AccumulateRewards(work.state, header, uncles) + core.AccumulateRewards(work.config, work.state, header, uncles) header.Root = work.state.IntermediateRoot() }