From 9919263fe225d6bbd0183fba578a84ecbac8aae6 Mon Sep 17 00:00:00 2001
From: Wei-Ning Huang <aitjcize@gmail.com>
Date: Wed, 14 Nov 2018 10:57:53 +0800
Subject: core: validate DKG set with correct nodeset in round-2 (#19)

* vendor: sync consensus core
* core: validate DKG set with correct nodeset in round-2
---
 core/blockchain.go                                 | 10 +++--
 core/evm.go                                        | 50 +++++++++++++---------
 core/vm/evm.go                                     | 11 ++++-
 core/vm/governance.go                              | 24 ++++++++---
 dex/governance.go                                  |  6 +--
 light/lightchain.go                                | 12 ++++--
 params/config.go                                   |  2 +-
 .../dexon-consensus/core/consensus.go              | 15 +++----
 .../dexon-consensus/core/constant.go               |  6 +--
 .../dexon-consensus/core/interfaces.go             |  4 +-
 .../dexon-consensus/core/lattice.go                |  1 -
 .../dexon-foundation/dexon-consensus/core/utils.go | 11 -----
 vendor/vendor.json                                 | 34 +++++++--------
 13 files changed, 101 insertions(+), 85 deletions(-)

diff --git a/core/blockchain.go b/core/blockchain.go
index 19b17db8f..cb235ba8c 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -2143,7 +2143,11 @@ func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscript
 	return bc.scope.Track(bc.logsFeed.Subscribe(ch))
 }
 
-// GetRoundHeightMap returns the mapping between round and height.
-func (bc *BlockChain) GetRoundHeightMap() sync.Map {
-	return bc.roundHeightMap
+// GetRoundHeight returns the height of a given round.
+func (bc *BlockChain) GetRoundHeight(round uint64) (uint64, bool) {
+	h, ok := bc.roundHeightMap.Load(round)
+	if !ok {
+		return 0, false
+	}
+	return h.(uint64), true
 }
diff --git a/core/evm.go b/core/evm.go
index bae09c514..87185abb8 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -18,11 +18,10 @@ package core
 
 import (
 	"math/big"
-	"reflect"
-	"sync"
 
 	"github.com/dexon-foundation/dexon/common"
 	"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/core/vm"
 )
@@ -36,8 +35,14 @@ type ChainContext interface {
 	// GetHeader returns the hash corresponding to their hash.
 	GetHeader(common.Hash, uint64) *types.Header
 
-	// GetRoundHeightMap returns the mapping between round and height.
-	GetRoundHeightMap() sync.Map
+	// StateAt returns the statedb given a root hash.
+	StateAt(common.Hash) (*state.StateDB, error)
+
+	// GetHeaderByNumber returns the header given a block number.
+	GetHeaderByNumber(number uint64) *types.Header
+
+	// GetRoundHeight returns the mapping between round and height.
+	GetRoundHeight(uint64) (uint64, bool)
 }
 
 // NewEVMContext creates a new context for use in the EVM.
@@ -50,24 +55,29 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author
 		beneficiary = *author
 	}
 
-	var roundHeight sync.Map
-	if !reflect.ValueOf(chain).IsNil() {
-		roundHeight = chain.GetRoundHeightMap()
+	return vm.Context{
+		CanTransfer:    CanTransfer,
+		Transfer:       Transfer,
+		GetHash:        GetHashFn(header, chain),
+		StateAtNumber:  StateAtNumberFn(chain),
+		GetRoundHeight: chain.GetRoundHeight,
+		Origin:         msg.From(),
+		Coinbase:       beneficiary,
+		BlockNumber:    new(big.Int).Set(header.Number),
+		Time:           new(big.Int).Set(header.Time),
+		Randomness:     header.Randomness,
+		Difficulty:     new(big.Int).Set(header.Difficulty),
+		GasLimit:       header.GasLimit,
+		GasPrice:       new(big.Int).Set(msg.GasPrice()),
 	}
+}
 
-	return vm.Context{
-		CanTransfer: CanTransfer,
-		Transfer:    Transfer,
-		GetHash:     GetHashFn(header, chain),
-		Origin:      msg.From(),
-		Coinbase:    beneficiary,
-		BlockNumber: new(big.Int).Set(header.Number),
-		Time:        new(big.Int).Set(header.Time),
-		Randomness:  header.Randomness,
-		Difficulty:  new(big.Int).Set(header.Difficulty),
-		RoundHeight: roundHeight,
-		GasLimit:    header.GasLimit,
-		GasPrice:    new(big.Int).Set(msg.GasPrice()),
+// StateAtNumberFn returns a StateAtNumberFunc which allows the retrieval of
+// statedb at a given block height.
+func StateAtNumberFn(chain ChainContext) func(n uint64) (*state.StateDB, error) {
+	return func(n uint64) (*state.StateDB, error) {
+		header := chain.GetHeaderByNumber(n)
+		return chain.StateAt(header.Root)
 	}
 }
 
diff --git a/core/vm/evm.go b/core/vm/evm.go
index a75d0f1d3..865ab0d5b 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -18,11 +18,11 @@ package vm
 
 import (
 	"math/big"
-	"sync"
 	"sync/atomic"
 	"time"
 
 	"github.com/dexon-foundation/dexon/common"
+	"github.com/dexon-foundation/dexon/core/state"
 	"github.com/dexon-foundation/dexon/crypto"
 	"github.com/dexon-foundation/dexon/params"
 )
@@ -39,6 +39,10 @@ type (
 	// GetHashFunc returns the nth block hash in the blockchain
 	// and is used by the BLOCKHASH EVM op code.
 	GetHashFunc func(uint64) common.Hash
+	// StateAtFunc returns the statedb given a root hash.
+	StateAtNumberFunc func(uint64) (*state.StateDB, error)
+	// GetRoundHeightFunc returns the round height.
+	GetRoundHeightFunc func(uint64) (uint64, bool)
 )
 
 // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
@@ -81,6 +85,10 @@ type Context struct {
 	Transfer TransferFunc
 	// GetHash returns the hash corresponding to n
 	GetHash GetHashFunc
+	// StateAtNumber returns the statedb given a root hash.
+	StateAtNumber StateAtNumberFunc
+	// GetRoundHeight returns the round height.
+	GetRoundHeight GetRoundHeightFunc
 
 	// Message information
 	Origin   common.Address // Provides information for ORIGIN
@@ -93,7 +101,6 @@ type Context struct {
 	Time        *big.Int       // Provides information for TIME
 	Randomness  []byte         // Provides information for RAND
 	Difficulty  *big.Int       // Provides information for DIFFICULTY
-	RoundHeight sync.Map       // Provides information of round height mapping.
 }
 
 // EVM is the Ethereum Virtual Machine base object and provides
diff --git a/core/vm/governance.go b/core/vm/governance.go
index 49141761f..b38733e1a 100644
--- a/core/vm/governance.go
+++ b/core/vm/governance.go
@@ -1467,11 +1467,22 @@ func (g *GovernanceContract) penalize() {
 	g.contract.UseGas(g.contract.Gas)
 }
 
-func (g *GovernanceContract) inDKGSet(nodeID coreTypes.NodeID) bool {
+func (g *GovernanceContract) inDKGSet(round *big.Int, nodeID coreTypes.NodeID) bool {
 	target := coreTypes.NewDKGSetTarget(coreCommon.Hash(g.state.CurrentCRS()))
 	ns := coreTypes.NewNodeSet()
 
-	for _, x := range g.state.Nodes() {
+	configRound := big.NewInt(0) // If round < core.ConfigRoundShift, use 0.
+	if round.Uint64() >= core.ConfigRoundShift {
+		configRound = new(big.Int).Sub(round, big.NewInt(int64(core.ConfigRoundShift)))
+	}
+
+	statedb, err := g.evm.Context.StateAtNumber(g.state.RoundHeight(configRound).Uint64())
+	if err != nil {
+		panic(err)
+	}
+
+	state := GovernanceStateHelper{statedb}
+	for _, x := range state.Nodes() {
 		mpk, err := ecdsa.NewPublicKeyFromByteSlice(x.PublicKey)
 		if err != nil {
 			panic(err)
@@ -1515,7 +1526,7 @@ func (g *GovernanceContract) addDKGComplaint(round *big.Int, comp []byte) ([]byt
 	}
 
 	// DKGComplaint must belongs to someone in DKG set.
-	if !g.inDKGSet(dkgComplaint.ProposerID) {
+	if !g.inDKGSet(round, dkgComplaint.ProposerID) {
 		g.penalize()
 		return nil, errExecutionReverted
 	}
@@ -1555,7 +1566,7 @@ func (g *GovernanceContract) addDKGMasterPublicKey(round *big.Int, mpk []byte) (
 	}
 
 	// DKGMasterPublicKey must belongs to someone in DKG set.
-	if !g.inDKGSet(dkgMasterPK.ProposerID) {
+	if !g.inDKGSet(round, dkgMasterPK.ProposerID) {
 		g.penalize()
 		return nil, errExecutionReverted
 	}
@@ -1588,7 +1599,7 @@ func (g *GovernanceContract) addDKGFinalize(round *big.Int, finalize []byte) ([]
 	}
 
 	// DKGFInalize must belongs to someone in DKG set.
-	if !g.inDKGSet(dkgFinalize.ProposerID) {
+	if !g.inDKGSet(round, dkgFinalize.ProposerID) {
 		g.penalize()
 		return nil, errExecutionReverted
 	}
@@ -1757,13 +1768,12 @@ func (g *GovernanceContract) transferOwnership(newOwner common.Address) ([]byte,
 
 func (g *GovernanceContract) snapshotRound(round, height *big.Int) ([]byte, error) {
 	// Validate if this mapping is correct.
-	rawHeight, ok := g.evm.Context.RoundHeight.Load(round)
+	realHeight, ok := g.evm.Context.GetRoundHeight(round.Uint64())
 	if !ok {
 		g.penalize()
 		return nil, errExecutionReverted
 	}
 
-	realHeight := rawHeight.(uint64)
 	if height.Cmp(new(big.Int).SetUint64(realHeight)) != 0 {
 		g.penalize()
 		return nil, errExecutionReverted
diff --git a/dex/governance.go b/dex/governance.go
index 92a8789b3..0251d2afd 100644
--- a/dex/governance.go
+++ b/dex/governance.go
@@ -24,8 +24,6 @@ import (
 	"github.com/dexon-foundation/dexon/rpc"
 )
 
-const configActivationOffset = 2
-
 type DexconGovernance struct {
 	b            *DexAPIBackend
 	chainConfig  *params.ChainConfig
@@ -68,10 +66,10 @@ func (d *DexconGovernance) getGovState() *vm.GovernanceStateHelper {
 }
 
 func (d *DexconGovernance) getGovStateAtRound(round uint64) *vm.GovernanceStateHelper {
-	if round < configActivationOffset {
+	if round < dexCore.ConfigRoundShift {
 		round = 0
 	} else {
-		round -= configActivationOffset
+		round -= dexCore.ConfigRoundShift
 	}
 	ctx := context.Background()
 	blockHeight, err := d.getRoundHeight(ctx, round)
diff --git a/light/lightchain.go b/light/lightchain.go
index a07cfedf7..7fbeb7b96 100644
--- a/light/lightchain.go
+++ b/light/lightchain.go
@@ -534,8 +534,12 @@ func (self *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEven
 	return self.scope.Track(new(event.Feed).Subscribe(ch))
 }
 
-// GetRoundHeightMap returns the mapping between round and height.
-func (self *LightChain) GetRoundHeightMap() sync.Map {
-	// TODO(w): fix this.
-	return sync.Map{}
+// StateAt returns a new mutable state based on a particular point in time.
+func (self *LightChain) StateAt(root common.Hash) (*state.StateDB, error) {
+	return nil, nil
+}
+
+// GetRoundHeight returns the height of a given round.
+func (self *LightChain) GetRoundHeight(round uint64) (uint64, bool) {
+	return 0, false
 }
diff --git a/params/config.go b/params/config.go
index b5b99b1e3..b115cc806 100644
--- a/params/config.go
+++ b/params/config.go
@@ -213,7 +213,7 @@ type dexconConfigSpecMarshaling struct {
 
 // String implements the stringer interface, returning the consensus engine details.
 func (d *DexconConfig) String() string {
-	return fmt.Sprintf("{GenesisCRSText: %v Owner: %v BlockReward: %v BlockGasLimit: %v NumChains: %v LambdaBA: %v LambdaDKG: %v K: %v PhiRatio: %v NotarySetSize: %v DKGSetSize: %v RoundInterval: %v MinBlockInterval: %v",
+	return fmt.Sprintf("{GenesisCRSText: %v Owner: %v BlockReward: %v BlockGasLimit: %v NumChains: %v LambdaBA: %v LambdaDKG: %v K: %v PhiRatio: %v NotarySetSize: %v DKGSetSize: %v RoundInterval: %v MinBlockInterval: %v}",
 		d.GenesisCRSText,
 		d.Owner,
 		d.BlockReward,
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
index d12d30a6a..3d46c5c8b 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
@@ -315,11 +315,7 @@ func NewConsensus(
 	logger common.Logger) *Consensus {
 
 	// TODO(w): load latest blockHeight from DB, and use config at that height.
-	var (
-		round uint64
-		// round 0 and 1 are decided at beginning.
-		roundToNotify = round + 2
-	)
+	var round uint64
 	logger.Debug("Calling Governance.Configuration", "round", round)
 	config := gov.Configuration(round)
 	nodeSetCache := NewNodeSetCache(gov)
@@ -366,7 +362,6 @@ func NewConsensus(
 		authModule:       authModule,
 		event:            common.NewEvent(),
 		logger:           logger,
-		roundToNotify:    roundToNotify,
 	}
 
 	validLeader := func(block *types.Block) (bool, error) {
@@ -416,9 +411,9 @@ func NewConsensus(
 
 // Run starts running DEXON Consensus.
 func (con *Consensus) Run(initBlock *types.Block) {
-	con.logger.Debug("Calling Governance.NotifyRoundHeight for genesis rounds",
-		"block", initBlock)
-	notifyGenesisRounds(initBlock, con.gov)
+	// The block past from full node should be delivered already or known by
+	// full node. We don't have to notify it.
+	con.roundToNotify = initBlock.Position.Round + 1
 	initRound := initBlock.Position.Round
 	con.logger.Debug("Calling Governance.Configuration", "round", initRound)
 	initConfig := con.gov.Configuration(initRound)
@@ -1004,7 +999,7 @@ func (con *Consensus) preProcessBlock(b *types.Block) (err error) {
 func (con *Consensus) deliverBlock(b *types.Block) {
 	con.logger.Debug("Calling Application.BlockDelivered", "block", b)
 	con.app.BlockDelivered(b.Hash, b.Position, b.Finalization.Clone())
-	if b.Position.Round+roundShift == con.roundToNotify {
+	if b.Position.Round == con.roundToNotify {
 		// Only the first block delivered of that round would
 		// trigger this noitification.
 		con.logger.Debug("Calling Governance.NotifyRoundHeight",
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/constant.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/constant.go
index 9a61c0abb..563a321f5 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/constant.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/constant.go
@@ -17,9 +17,9 @@
 
 package core
 
-// round shift refers to the difference between block's round and config round
-// derived from its state.
+// ConfigRoundShift refers to the difference between block's round and config
+// round derived from its state.
 //
 // For example, when round shift is 2, a block in round 0 should derive config
 // for round 2.
-const roundShift uint64 = 2
+const ConfigRoundShift uint64 = 2
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go
index 3a9c0752a..e07476d44 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go
@@ -111,8 +111,8 @@ type Governance interface {
 	// Return the genesis node set if round == 0.
 	NodeSet(round uint64) []crypto.PublicKey
 
-	// NotifyRoundHeight notifies governance contract to generate configuration
-	// for that round with the block on that consensus height.
+	// NotifyRoundHeight notifies governance contract the consensus height of
+	// the first block of the given round.
 	NotifyRoundHeight(targetRound, consensusHeight uint64)
 
 	//// DKG-related methods.
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go
index dcb3368fd..108f2887b 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go
@@ -282,7 +282,6 @@ func (l *Lattice) PurgeBlocks(blocks []*types.Block) error {
 func (l *Lattice) AppendConfig(round uint64, config *types.Config) (err error) {
 	l.lock.Lock()
 	defer l.lock.Unlock()
-
 	l.pool.resize(config.NumChains)
 	if err = l.data.appendConfig(round, config); err != nil {
 		return
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
index 9159be858..6b9ce634f 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
@@ -159,14 +159,3 @@ func DiffUint64(a, b uint64) uint64 {
 	}
 	return b - a
 }
-
-// notifyGenesisRounds notifies governance to generate configs based on genesis
-// state.
-func notifyGenesisRounds(initBlock *types.Block, gov Governance) {
-	if initBlock.Position.Round != 0 || !initBlock.IsGenesis() {
-		return
-	}
-	for round := uint64(0); round < roundShift; round++ {
-		gov.NotifyRoundHeight(round, 0)
-	}
-}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 83d579652..711396aec 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -105,50 +105,50 @@
 		{
 			"checksumSHA1": "ev84RyegNbt2Pr/sK26LK9LoQNI=",
 			"path": "github.com/dexon-foundation/dexon-consensus/common",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
-			"checksumSHA1": "9HDUFD7awHgKX5BhFZQAqr5e8Mo=",
+			"checksumSHA1": "CDbhowufKnHipqNsFhQymXdlAyY=",
 			"path": "github.com/dexon-foundation/dexon-consensus/core",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
 			"checksumSHA1": "vNsaBvsrXJF+W6K5DCLpgy1rUZY=",
 			"path": "github.com/dexon-foundation/dexon-consensus/core/blockdb",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
 			"checksumSHA1": "tQSbYCu5P00lUhKsx3IbBZCuSLY=",
 			"path": "github.com/dexon-foundation/dexon-consensus/core/crypto",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
 			"checksumSHA1": "p2jOAulavUU2xyj018pYPHlj8XA=",
 			"path": "github.com/dexon-foundation/dexon-consensus/core/crypto/dkg",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
 			"checksumSHA1": "6Pf6caC8LTNCI7IflFmglKYnxYo=",
 			"path": "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
 			"checksumSHA1": "Kzw8b6kQLSApzVHO2WvmxrBfixA=",
 			"path": "github.com/dexon-foundation/dexon-consensus/core/types",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
 			"checksumSHA1": "ovChyW9OfDGnk/7CDAR+A5vJymc=",
 			"path": "github.com/dexon-foundation/dexon-consensus/core/types/dkg",
-			"revision": "86838fe70789292de0851f82426e5241c0f0cc96",
-			"revisionTime": "2018-11-13T07:26:09Z"
+			"revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
+			"revisionTime": "2018-11-13T08:28:24Z"
 		},
 		{
 			"checksumSHA1": "TAkwduKZqLyimyTPPWIllZWYFuE=",
-- 
cgit v1.2.3