aboutsummaryrefslogtreecommitdiffstats
path: root/consensus
diff options
context:
space:
mode:
authorWei-Ning Huang <w@dexon.org>2019-03-20 11:55:01 +0800
committerGitHub <noreply@github.com>2019-03-20 11:55:01 +0800
commitd72b0255b760b3b0ded0c1ce59985754b8000a78 (patch)
tree30f6203962b52a41bf6548aadabba569d4809e03 /consensus
parentd22b689fdfafc3535e1008dd4e3b05d81588f807 (diff)
downloaddexon-d72b0255b760b3b0ded0c1ce59985754b8000a78.tar
dexon-d72b0255b760b3b0ded0c1ce59985754b8000a78.tar.gz
dexon-d72b0255b760b3b0ded0c1ce59985754b8000a78.tar.bz2
dexon-d72b0255b760b3b0ded0c1ce59985754b8000a78.tar.lz
dexon-d72b0255b760b3b0ded0c1ce59985754b8000a78.tar.xz
dexon-d72b0255b760b3b0ded0c1ce59985754b8000a78.tar.zst
dexon-d72b0255b760b3b0ded0c1ce59985754b8000a78.zip
consensus: dexcon: disqualify dead node (#280)
Since a qualified node might fail stopped, we need to remove them from qualified nodes to maintain network integrity. We do this by inspect the previous round to see if there are dead nodes. A dead node is a notary set node that does not propose any block in the previous round. We disqualify them by fining them so their staked value is 1 wei below minStake. This make them unqualified for being notary set in the follow on rounds.
Diffstat (limited to 'consensus')
-rw-r--r--consensus/dexcon/dexcon.go42
-rw-r--r--consensus/dexcon/dexcon_test.go10
2 files changed, 46 insertions, 6 deletions
diff --git a/consensus/dexcon/dexcon.go b/consensus/dexcon/dexcon.go
index c837e3a43..3e520438b 100644
--- a/consensus/dexcon/dexcon.go
+++ b/consensus/dexcon/dexcon.go
@@ -17,6 +17,8 @@
package dexcon
import (
+ "encoding/hex"
+ "fmt"
"math/big"
"github.com/dexon-foundation/dexon/common"
@@ -24,11 +26,13 @@ import (
"github.com/dexon-foundation/dexon/core/state"
"github.com/dexon-foundation/dexon/core/types"
"github.com/dexon-foundation/dexon/core/vm"
+ "github.com/dexon-foundation/dexon/log"
"github.com/dexon-foundation/dexon/rpc"
)
type GovernanceStateFetcher interface {
GetStateForConfigAtRound(round uint64) *vm.GovernanceState
+ NotarySetNodeKeyAddresses(round uint64) (map[common.Address]struct{}, error)
}
// Dexcon is a delegated proof-of-stake consensus engine.
@@ -137,8 +141,39 @@ func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, sta
gs := vm.GovernanceState{state}
height := gs.RoundHeight(new(big.Int).SetUint64(header.Round))
+
+ // The first block of a round is found.
if header.Round > 0 && height.Uint64() == 0 {
gs.PushRoundHeight(header.Number)
+
+ // Check for dead node and disqualify them.
+ // A dead node node is defined as: a notary set node that did not propose
+ // any block in the past round.
+ addrs, err := d.govStateFetcer.NotarySetNodeKeyAddresses(header.Round - 1)
+ if err != nil {
+ panic(err)
+ }
+
+ gcs := d.govStateFetcer.GetStateForConfigAtRound(header.Round - 1)
+
+ for addr := range addrs {
+ offset := gcs.NodesOffsetByNodeKeyAddress(addr)
+ if offset.Cmp(big.NewInt(0)) < 0 {
+ panic(fmt.Errorf("invalid notary set found, addr = %s", addr.String()))
+ }
+
+ node := gcs.Node(offset)
+ lastHeight := gs.LastProposedHeight(node.Owner)
+ prevRoundHeight := gs.RoundHeight(big.NewInt(int64(header.Round - 1)))
+
+ if lastHeight.Uint64() < prevRoundHeight.Uint64() {
+ log.Info("Disqualify node", "round", header.Round, "nodePubKey", hex.EncodeToString(node.PublicKey))
+ err = gs.Disqualify(node)
+ if err != nil {
+ log.Error("Failed to disqualify node", "err", err)
+ }
+ }
+ }
}
// Distribute block reward and halving condition.
@@ -147,16 +182,17 @@ func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, sta
} else {
reward := d.calculateBlockReward(int64(header.Round), state)
state.AddBalance(header.Coinbase, reward)
-
+ header.Reward = reward
gs.IncTotalSupply(reward)
- config := gs.Configuration()
+ // 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()
}
- header.Reward = reward
}
header.Root = state.IntermediateRoot(true)
diff --git a/consensus/dexcon/dexcon_test.go b/consensus/dexcon/dexcon_test.go
index f34823570..0181a80f3 100644
--- a/consensus/dexcon/dexcon_test.go
+++ b/consensus/dexcon/dexcon_test.go
@@ -31,14 +31,18 @@ import (
"github.com/dexon-foundation/dexon/params"
)
-type GovStateFetcher struct {
+type govStateFetcher struct {
statedb *state.StateDB
}
-func (g *GovStateFetcher) GetStateForConfigAtRound(_ uint64) *vm.GovernanceState {
+func (g *govStateFetcher) GetStateForConfigAtRound(_ uint64) *vm.GovernanceState {
return &vm.GovernanceState{g.statedb}
}
+func (g *govStateFetcher) NotarySetNodeKeyAddresses(round uint64) (map[common.Address]struct{}, error) {
+ return make(map[common.Address]struct{}), nil
+}
+
type DexconTestSuite struct {
suite.Suite
@@ -86,7 +90,7 @@ func (d *DexconTestSuite) SetupTest() {
func (d *DexconTestSuite) TestBlockRewardCalculation() {
consensus := New()
- consensus.SetGovStateFetcher(&GovStateFetcher{d.stateDB})
+ consensus.SetGovStateFetcher(&govStateFetcher{d.stateDB})
d.s.IncTotalStaked(big.NewInt(1e18))