diff options
author | Wei-Ning Huang <w@dexon.org> | 2019-01-13 16:21:17 +0800 |
---|---|---|
committer | Wei-Ning Huang <w@dexon.org> | 2019-04-09 21:32:56 +0800 |
commit | 7c664ca090d90815db23644c209d4f5cdf0ff594 (patch) | |
tree | 79f192501314c5ae37f8cb6c45ac3ef759d01945 /consensus/dexcon | |
parent | 7bf5e2205a49a500672ff7415921aa6659c46611 (diff) | |
download | dexon-7c664ca090d90815db23644c209d4f5cdf0ff594.tar.gz dexon-7c664ca090d90815db23644c209d4f5cdf0ff594.tar.zst dexon-7c664ca090d90815db23644c209d4f5cdf0ff594.zip |
consensus: implement DEXON cryptoeconomics v4.0 (#145)
Diffstat (limited to 'consensus/dexcon')
-rw-r--r-- | consensus/dexcon/api.go | 26 | ||||
-rw-r--r-- | consensus/dexcon/dexcon.go | 67 | ||||
-rw-r--r-- | consensus/dexcon/dexcon_test.go | 107 |
3 files changed, 159 insertions, 41 deletions
diff --git a/consensus/dexcon/api.go b/consensus/dexcon/api.go deleted file mode 100644 index 6f314b96b..000000000 --- a/consensus/dexcon/api.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package dexcon - -// API exposes ethash related methods for the RPC interface. -type API struct { - dexcon *Dexcon -} - -func (api *API) Hello() error { - return nil -} diff --git a/consensus/dexcon/dexcon.go b/consensus/dexcon/dexcon.go index f0a133ccf..4480f64cb 100644 --- a/consensus/dexcon/dexcon.go +++ b/consensus/dexcon/dexcon.go @@ -23,17 +23,17 @@ import ( "github.com/dexon-foundation/dexon/consensus" "github.com/dexon-foundation/dexon/core/state" "github.com/dexon-foundation/dexon/core/types" - "github.com/dexon-foundation/dexon/params" + "github.com/dexon-foundation/dexon/core/vm" "github.com/dexon-foundation/dexon/rpc" ) -type ConfigurationFetcher interface { - DexconConfiguration(round uint64) *params.DexconConfig +type GovernanceStateFetcher interface { + GetGovStateHelperAtRound(round uint64) *vm.GovernanceStateHelper } // Dexcon is a delegated proof-of-stake consensus engine. type Dexcon struct { - configFetcher ConfigurationFetcher + govStateFetcer GovernanceStateFetcher } // New creates a Clique proof-of-authority consensus engine with the initial @@ -42,11 +42,11 @@ func New() *Dexcon { return &Dexcon{} } -// SetConfigFetcher sets the config fetcher for Dexcon. The reason this is not +// SetGovStateFetcher sets the config fetcher for Dexcon. The reason this is not // passed in the New() method is to bypass cycle dependencies when initializing // dex backend. -func (d *Dexcon) SetConfigFetcher(fetcher ConfigurationFetcher) { - d.configFetcher = fetcher +func (d *Dexcon) SetGovStateFetcher(fetcher GovernanceStateFetcher) { + d.govStateFetcer = fetcher } // Author implements consensus.Engine, returning the Ethereum address recovered @@ -107,13 +107,55 @@ 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.GetGovStateHelperAtRound(uint64(round)) + config := gs.Configuration() + + gsCurrent := vm.GovernanceStateHelper{state} + configCurrent := gsCurrent.Configuration() + heightCurrent := gsCurrent.RoundHeight(big.NewInt(round)).Uint64() + + blocksPerRound := uint64(0) + + // The initial round, calculate an approximate number of round base on config. + if round == 0 || heightCurrent == 0 { + blocksPerRound = uint64(config.NumChains) * config.RoundInterval / config.MinBlockInterval + } else { + heightPrev := gsCurrent.RoundHeight(big.NewInt(round - 1)).Uint64() + blocksPerRound = heightCurrent - heightPrev + } + + // blockReard = miningVelocity * totalStaked * roundInterval / aYear / numBlocksInPrevRound + numerator, _ := new(big.Float).Mul( + new(big.Float).Mul( + big.NewFloat(float64(configCurrent.MiningVelocity)), + new(big.Float).SetInt(gs.TotalStaked())), + new(big.Float).SetInt(gs.RoundInterval())).Int(nil) + + reward := new(big.Int).Div(numerator, + new(big.Int).Mul( + big.NewInt(86400*1000*365), + big.NewInt(int64(blocksPerRound)))) + + return reward +} + // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given, and returns the final block. func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { - config := d.configFetcher.DexconConfiguration(header.Round) - reward := new(big.Int).Div(config.BlockReward, big.NewInt(int64(config.NumChains))) + reward := d.calculateBlockReward(int64(header.Round), state) state.AddBalance(header.Coinbase, reward) + gs := vm.GovernanceStateHelper{state} + gs.IncTotalSupply(reward) + + config := gs.Configuration() + + // Check if halving checkpoint reached. + if gs.TotalSupply().Cmp(config.NextHalvingSupply) >= 0 { + gs.MiningHalved() + } + header.Reward = reward header.Root = state.IntermediateRoot(true) return types.NewBlock(header, txs, uncles, receipts), nil @@ -145,10 +187,5 @@ func (d *Dexcon) Close() error { // APIs implements consensus.Engine, returning the user facing RPC API to allow // controlling the signer voting. func (d *Dexcon) APIs(chain consensus.ChainReader) []rpc.API { - return []rpc.API{{ - Namespace: "dexcon", - Version: "1.0", - Service: &API{dexcon: d}, - Public: false, - }} + return []rpc.API{} } diff --git a/consensus/dexcon/dexcon_test.go b/consensus/dexcon/dexcon_test.go new file mode 100644 index 000000000..7ba1be876 --- /dev/null +++ b/consensus/dexcon/dexcon_test.go @@ -0,0 +1,107 @@ +// Copyright 2019 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// <http://www.gnu.org/licenses/>. + +package dexcon + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/state" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/ethdb" + "github.com/dexon-foundation/dexon/params" +) + +type GovStateFetcher struct { + statedb *state.StateDB +} + +func (g *GovStateFetcher) GetGovStateHelperAtRound(_ uint64) *vm.GovernanceStateHelper { + return &vm.GovernanceStateHelper{g.statedb} +} + +type DexconTestSuite struct { + suite.Suite + + config *params.DexconConfig + memDB *ethdb.MemDatabase + stateDB *state.StateDB + s *vm.GovernanceStateHelper +} + +func (d *DexconTestSuite) SetupTest() { + memDB := ethdb.NewMemDatabase() + stateDB, err := state.New(common.Hash{}, state.NewDatabase(memDB)) + if err != nil { + panic(err) + } + d.memDB = memDB + d.stateDB = stateDB + d.s = &vm.GovernanceStateHelper{stateDB} + + config := params.TestnetChainConfig.Dexcon + config.LockupPeriod = 1000 + config.NextHalvingSupply = new(big.Int).Mul(big.NewInt(1e18), big.NewInt(2.5e9)) + config.LastHalvedAmount = new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1.5e9)) + config.MiningVelocity = 0.1875 + config.RoundInterval = 3600000 + config.NumChains = 6 + config.MinBlockInterval = 1000 + + d.config = config + + // Give governance contract balance so it will not be deleted because of being an empty state object. + stateDB.AddBalance(vm.GovernanceContractAddress, big.NewInt(1)) + + // Genesis CRS. + crs := crypto.Keccak256Hash([]byte(config.GenesisCRSText)) + d.s.PushCRS(crs) + + // Round 0 height. + d.s.PushRoundHeight(big.NewInt(0)) + + // Governance configuration. + d.s.UpdateConfiguration(config) + + d.stateDB.Commit(true) +} + +func (d *DexconTestSuite) TestBlockRewardCalculation() { + consensus := New() + consensus.SetGovStateFetcher(&GovStateFetcher{d.stateDB}) + + d.s.IncTotalStaked(big.NewInt(1e18)) + + // blockReard = miningVelocity * totalStaked * roundInterval / aYear / numBlocksInPrevRound + // 0.1875 * 1e18 * 3600 * 1000 / (86400 * 1000 * 365 * 6 * 3600) = 990930999.4926434 + d.Require().Equal(big.NewInt(990930999), consensus.calculateBlockReward(0, d.stateDB)) + + // Round 1 + d.s.PushRoundHeight(big.NewInt(4000 * 6)) + + // 0.1875 * 1e18 * 3600 * 1000 / (86400 * 1000 * 365 * 6 * 4000) = 891837899 + d.Require().Equal(big.NewInt(891837899), consensus.calculateBlockReward(1, d.stateDB)) +} + +func TestDexcon(t *testing.T) { + suite.Run(t, new(DexconTestSuite)) +} |