diff options
-rw-r--r-- | consensus/dexcon/dexcon.go | 52 | ||||
-rw-r--r-- | consensus/dexcon/dexcon_test.go | 2 | ||||
-rw-r--r-- | core/chain_makers.go | 1 | ||||
-rw-r--r-- | core/state_transition.go | 66 | ||||
-rw-r--r-- | eth/tracers/tracers_test.go | 1 |
5 files changed, 102 insertions, 20 deletions
diff --git a/consensus/dexcon/dexcon.go b/consensus/dexcon/dexcon.go index 3e520438b..0b6fa192f 100644 --- a/consensus/dexcon/dexcon.go +++ b/consensus/dexcon/dexcon.go @@ -111,8 +111,21 @@ func (d *Dexcon) Prepare(chain consensus.ChainReader, header *types.Header) erro return nil } -func (d *Dexcon) calculateBlockReward(round int64, state *state.StateDB) *big.Int { - gs := d.govStateFetcer.GetStateForConfigAtRound(uint64(round)) +func (d *Dexcon) inExtendedRound(header *types.Header, state *state.StateDB) bool { + gs := vm.GovernanceState{state} + rgs := d.govStateFetcer.GetStateForConfigAtRound(header.Round) + + roundEnd := gs.RoundHeight(new(big.Int).SetUint64(header.Round)).Uint64() + rgs.RoundLength().Uint64() + + // Round 0 starts and height 0 instead of height 1. + if header.Round == 0 { + roundEnd += 1 + } + return header.Number.Uint64() >= roundEnd +} + +func (d *Dexcon) calculateBlockReward(round uint64) *big.Int { + gs := d.govStateFetcer.GetStateForConfigAtRound(round) config := gs.Configuration() blocksPerRound := config.RoundLength @@ -177,24 +190,27 @@ func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, sta } // Distribute block reward and halving condition. - if header.Coinbase == (common.Address{}) { - header.Reward = new(big.Int) - } else { - reward := d.calculateBlockReward(int64(header.Round), state) - state.AddBalance(header.Coinbase, reward) - header.Reward = reward - gs.IncTotalSupply(reward) - - // Record last proposed height. - gs.PutLastProposedHeight(header.Coinbase, header.Number) - - // Check if halving checkpoint reached. - config := gs.Configuration() - if gs.TotalSupply().Cmp(config.NextHalvingSupply) >= 0 { - gs.MiningHalved() - } + reward := new(big.Int) + + // If this is not an empty block and we are not in extended round, calculate + // the block reward. + if header.Coinbase != (common.Address{}) && !d.inExtendedRound(header, state) { + reward = d.calculateBlockReward(header.Round) + } + + header.Reward = reward + state.AddBalance(header.Coinbase, reward) + gs.IncTotalSupply(reward) + + // Check if halving checkpoint reached. + config := gs.Configuration() + if gs.TotalSupply().Cmp(config.NextHalvingSupply) >= 0 { + gs.MiningHalved() } + // Record last proposed height. + gs.PutLastProposedHeight(header.Coinbase, header.Number) + header.Root = state.IntermediateRoot(true) return types.NewBlock(header, txs, uncles, receipts), nil } diff --git a/consensus/dexcon/dexcon_test.go b/consensus/dexcon/dexcon_test.go index 0181a80f3..bfded8db8 100644 --- a/consensus/dexcon/dexcon_test.go +++ b/consensus/dexcon/dexcon_test.go @@ -96,7 +96,7 @@ func (d *DexconTestSuite) TestBlockRewardCalculation() { // blockReard = miningVelocity * totalStaked * roundInterval / aYear / numBlocksInCurRound // 0.1875 * 1e18 * 3600 * 1000 / (86400 * 1000 * 365 * 3600) = 5945585996.96 - d.Require().Equal(big.NewInt(5945585996), consensus.calculateBlockReward(0, d.stateDB)) + d.Require().Equal(big.NewInt(5945585996), consensus.calculateBlockReward(0)) } func TestDexcon(t *testing.T) { diff --git a/core/chain_makers.go b/core/chain_makers.go index 27aff215c..f57eb37ff 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -86,6 +86,7 @@ func (b *BlockGen) SetNonce(nonce types.BlockNonce) { // added. Notably, contract code relying on the BLOCKHASH instruction // will panic during execution. func (b *BlockGen) AddTx(tx *types.Transaction) { + TestingMode = true b.AddTxWithChain(nil, tx) } diff --git a/core/state_transition.go b/core/state_transition.go index 02d58575b..f5ac9bde6 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -21,7 +21,9 @@ import ( "flag" "math" "math/big" + "sync/atomic" + dexCore "github.com/dexon-foundation/dexon-consensus/core" "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/vm" "github.com/dexon-foundation/dexon/log" @@ -29,11 +31,19 @@ import ( ) var legacyEvm = flag.Bool("legacy-evm", false, "make evm run origin logic") +var TestingMode = false var ( errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas") ) +var lastInExtendedRoundResultCache atomic.Value + +type lastInExtendedRoundResultType struct { + Height uint64 + Result bool +} + /* The State Transitioning Model @@ -180,6 +190,53 @@ func (st *StateTransition) preCheck() error { return st.buyGas() } +func (st *StateTransition) inExtendedRound() bool { + // If we are running tests with chian_makers.go, there will be no valid + // blockchain instance for st.evm.StateAtNumber to work correctly. Simply + // return false in this case. + if TestingMode { + return false + } + + if h := lastInExtendedRoundResultCache.Load(); h != nil { + res := h.(*lastInExtendedRoundResultType) + if res.Height == st.evm.BlockNumber.Uint64() { + return res.Result + } + } + + gs := vm.GovernanceState{st.state} + + round := st.evm.Round.Uint64() + if round < dexCore.ConfigRoundShift { + round = 0 + } else { + round -= dexCore.ConfigRoundShift + } + + configHeight := gs.RoundHeight(new(big.Int).SetUint64(round)) + state, err := st.evm.StateAtNumber(configHeight.Uint64()) + if err != nil { + panic(err) + } + rgs := vm.GovernanceState{state} + + roundEnd := gs.RoundHeight(st.evm.Round).Uint64() + rgs.RoundLength().Uint64() + + // Round 0 starts and height 0 instead of height 1. + if round == 0 { + roundEnd += 1 + } + + res := st.evm.BlockNumber.Uint64() >= roundEnd + + lastInExtendedRoundResultCache.Store(&lastInExtendedRoundResultType{ + Height: st.evm.BlockNumber.Uint64(), + Result: res, + }) + return res +} + // TransitionDb will transition the state by applying the current message and // returning the result including the used gas. It returns an error if failed. // An error indicates a consensus issue. @@ -230,7 +287,14 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo } else { st.dexonRefundGas() } - st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + + receiver := st.evm.Coinbase + if !*legacyEvm && st.inExtendedRound() { + gs := vm.GovernanceState{st.state} + receiver = gs.Owner() + } + + st.state.AddBalance(receiver, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) return ret, st.gasUsed(), vmerr != nil, err } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 2d6b0023b..1436cc67f 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -180,6 +180,7 @@ func TestPrestateTracerCreate2(t *testing.T) { t.Fatalf("failed to prepare transaction for tracing: %v", err) } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + core.TestingMode = true if _, _, _, err = st.TransitionDb(); err != nil { t.Fatalf("failed to execute transaction: %v", err) } |