aboutsummaryrefslogtreecommitdiffstats
path: root/consensus/dexcon
diff options
context:
space:
mode:
authorWei-Ning Huang <w@dexon.org>2019-01-13 16:21:17 +0800
committerWei-Ning Huang <w@dexon.org>2019-04-09 21:32:56 +0800
commit7c664ca090d90815db23644c209d4f5cdf0ff594 (patch)
tree79f192501314c5ae37f8cb6c45ac3ef759d01945 /consensus/dexcon
parent7bf5e2205a49a500672ff7415921aa6659c46611 (diff)
downloaddexon-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.go26
-rw-r--r--consensus/dexcon/dexcon.go67
-rw-r--r--consensus/dexcon/dexcon_test.go107
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))
+}