aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorMission Liao <mission.liao@dexon.org>2019-02-20 12:53:18 +0800
committerGitHub <noreply@github.com>2019-02-20 12:53:18 +0800
commit8ef4fc213703620fbfa13890dee042d40eea8545 (patch)
treeba9a07d2423314396e5677b7294122caa505ae9a /core
parent2cf18fd299ea0fc270b213343314cab652cac271 (diff)
downloaddexon-consensus-8ef4fc213703620fbfa13890dee042d40eea8545.tar
dexon-consensus-8ef4fc213703620fbfa13890dee042d40eea8545.tar.gz
dexon-consensus-8ef4fc213703620fbfa13890dee042d40eea8545.tar.bz2
dexon-consensus-8ef4fc213703620fbfa13890dee042d40eea8545.tar.lz
dexon-consensus-8ef4fc213703620fbfa13890dee042d40eea8545.tar.xz
dexon-consensus-8ef4fc213703620fbfa13890dee042d40eea8545.tar.zst
dexon-consensus-8ef4fc213703620fbfa13890dee042d40eea8545.zip
core: switch round by block height (#450)
Diffstat (limited to 'core')
-rw-r--r--core/agreement-mgr.go158
-rw-r--r--core/agreement.go4
-rw-r--r--core/blockchain.go56
-rw-r--r--core/blockchain_test.go57
-rw-r--r--core/consensus.go224
-rw-r--r--core/consensus_test.go17
-rw-r--r--core/round-based-config.go27
-rw-r--r--core/syncer/consensus.go42
-rw-r--r--core/test/app.go24
-rw-r--r--core/test/governance.go2
-rw-r--r--core/test/governance_test.go16
-rw-r--r--core/test/network.go2
-rw-r--r--core/test/network_test.go14
-rw-r--r--core/test/state-change-request.go16
-rw-r--r--core/test/state-change-request_test.go6
-rw-r--r--core/test/state.go47
-rw-r--r--core/test/state_test.go17
-rw-r--r--core/ticker.go56
-rw-r--r--core/types/block.go1
-rw-r--r--core/types/block_test.go5
-rw-r--r--core/types/config.go27
-rw-r--r--core/types/config_test.go4
-rw-r--r--core/types/position.go23
-rw-r--r--core/types/position_test.go59
-rw-r--r--core/utils.go9
-rw-r--r--core/utils/crypto.go4
-rw-r--r--core/utils/nodeset-cache.go22
-rw-r--r--core/utils/nodeset-cache_test.go10
-rw-r--r--core/utils/signer_test.go12
-rw-r--r--core/utils_test.go5
30 files changed, 389 insertions, 577 deletions
diff --git a/core/agreement-mgr.go b/core/agreement-mgr.go
index 5f5b9ae..88cc432 100644
--- a/core/agreement-mgr.go
+++ b/core/agreement-mgr.go
@@ -65,13 +65,29 @@ func genValidLeader(
}
type agreementMgrConfig struct {
- beginTime time.Time
- roundInterval time.Duration
+ roundBasedConfig
+
notarySetSize uint32
lambdaBA time.Duration
crs common.Hash
}
+func (c *agreementMgrConfig) from(
+ round uint64, config *types.Config, crs common.Hash) {
+ c.notarySetSize = config.NotarySetSize
+ c.lambdaBA = config.LambdaBA
+ c.crs = crs
+ c.setupRoundBasedFields(round, config)
+}
+
+func newAgreementMgrConfig(prev agreementMgrConfig, config *types.Config,
+ crs common.Hash) (c agreementMgrConfig) {
+ c = agreementMgrConfig{}
+ c.from(prev.roundID+1, config, crs)
+ c.setRoundBeginHeight(prev.roundEndHeight)
+ return
+}
+
type baRoundSetting struct {
notarySet map[types.NodeID]struct{}
agr *agreement
@@ -92,9 +108,8 @@ type agreementMgr struct {
signer *utils.Signer
bcModule *blockChain
ctx context.Context
- lastEndTime time.Time
initRound uint64
- configs []*agreementMgrConfig
+ configs []agreementMgrConfig
baModule *agreement
processedBAResult map[types.Position]struct{}
voteFilter *utils.VoteFilter
@@ -104,8 +119,8 @@ type agreementMgr struct {
}
func newAgreementMgr(con *Consensus, initRound uint64,
- initRoundBeginTime time.Time) *agreementMgr {
- return &agreementMgr{
+ initConfig agreementMgrConfig) (mgr *agreementMgr, err error) {
+ mgr = &agreementMgr{
con: con,
ID: con.ID,
app: con.app,
@@ -117,9 +132,33 @@ func newAgreementMgr(con *Consensus, initRound uint64,
bcModule: con.bcModule,
ctx: con.ctx,
initRound: initRound,
- lastEndTime: initRoundBeginTime,
processedBAResult: make(map[types.Position]struct{}, maxResultCache),
+ configs: []agreementMgrConfig{initConfig},
+ voteFilter: utils.NewVoteFilter(),
}
+ recv := &consensusBAReceiver{
+ consensus: con,
+ restartNotary: make(chan types.Position, 1),
+ roundValue: &atomic.Value{},
+ }
+ recv.roundValue.Store(uint64(0))
+ agr := newAgreement(
+ mgr.ID,
+ recv,
+ newLeaderSelector(genValidLeader(mgr), mgr.logger),
+ mgr.signer,
+ mgr.logger)
+ // Hacky way to initialize first notarySet.
+ nodes, err := mgr.cache.GetNodeSet(initRound)
+ if err != nil {
+ return
+ }
+ agr.notarySet = nodes.GetSubSet(
+ int(initConfig.notarySetSize), types.NewNotarySetTarget(initConfig.crs))
+ // Hacky way to make agreement module self contained.
+ recv.agreementModule = agr
+ mgr.baModule = agr
+ return
}
func (mgr *agreementMgr) run() {
@@ -136,7 +175,7 @@ func (mgr *agreementMgr) run() {
}()
}
-func (mgr *agreementMgr) getConfig(round uint64) *agreementMgrConfig {
+func (mgr *agreementMgr) config(round uint64) *agreementMgrConfig {
mgr.lock.RLock()
defer mgr.lock.RUnlock()
if round < mgr.initRound {
@@ -146,7 +185,7 @@ func (mgr *agreementMgr) getConfig(round uint64) *agreementMgrConfig {
if roundIndex >= uint64(len(mgr.configs)) {
return nil
}
- return mgr.configs[roundIndex]
+ return &mgr.configs[roundIndex]
}
func (mgr *agreementMgr) appendConfig(
@@ -156,52 +195,12 @@ func (mgr *agreementMgr) appendConfig(
if round != uint64(len(mgr.configs))+mgr.initRound {
return ErrRoundNotIncreasing
}
- newConfig := &agreementMgrConfig{
- beginTime: mgr.lastEndTime,
- roundInterval: config.RoundInterval,
- notarySetSize: config.NotarySetSize,
- lambdaBA: config.LambdaBA,
- crs: crs,
- }
- mgr.configs = append(mgr.configs, newConfig)
- mgr.lastEndTime = mgr.lastEndTime.Add(config.RoundInterval)
- // Prepare modules.
- if mgr.baModule != nil {
- return nil
- }
- recv := &consensusBAReceiver{
- consensus: mgr.con,
- restartNotary: make(chan types.Position, 1),
- roundValue: &atomic.Value{},
- }
- recv.roundValue.Store(uint64(0))
- agrModule := newAgreement(
- mgr.con.ID,
- recv,
- newLeaderSelector(genValidLeader(mgr), mgr.logger),
- mgr.signer,
- mgr.logger)
- // Hacky way to initialize first notarySet.
- nodes, err := mgr.cache.GetNodeSet(round)
- if err != nil {
- return err
- }
- agrModule.notarySet = nodes.GetSubSet(
- int(config.NotarySetSize), types.NewNotarySetTarget(crs))
- // Hacky way to make agreement module self contained.
- recv.agreementModule = agrModule
- mgr.baModule = agrModule
- mgr.voteFilter = utils.NewVoteFilter()
+ mgr.configs = append(mgr.configs, newAgreementMgrConfig(
+ mgr.configs[len(mgr.configs)-1], config, crs))
return nil
}
func (mgr *agreementMgr) processVote(v *types.Vote) (err error) {
- if v.Position.ChainID > 0 {
- mgr.logger.Error("Process vote for unknown chain to BA",
- "position", v.Position,
- "initRound", mgr.initRound)
- return utils.ErrInvalidChainID
- }
if mgr.voteFilter.Filter(v) {
return nil
}
@@ -212,13 +211,6 @@ func (mgr *agreementMgr) processVote(v *types.Vote) (err error) {
}
func (mgr *agreementMgr) processBlock(b *types.Block) error {
- if b.Position.ChainID > 0 {
- mgr.logger.Error("Process block for unknown chain to BA",
- "position", b.Position,
- "baRound", len(mgr.configs),
- "initRound", mgr.initRound)
- return utils.ErrInvalidChainID
- }
return mgr.baModule.processBlock(b)
}
@@ -247,13 +239,6 @@ func (mgr *agreementMgr) untouchAgreementResult(
func (mgr *agreementMgr) processAgreementResult(
result *types.AgreementResult) error {
- if result.Position.ChainID > 0 {
- mgr.logger.Error("Process unknown result for unknown chain to BA",
- "position", result.Position,
- "baRound", len(mgr.configs),
- "initRound", mgr.initRound)
- return utils.ErrInvalidChainID
- }
aID := mgr.baModule.agreementID()
if isStop(aID) {
return nil
@@ -310,13 +295,12 @@ func (mgr *agreementMgr) runBA(initRound uint64) {
var (
currentRound uint64
nextRound = initRound
+ curConfig = mgr.config(initRound)
setting = baRoundSetting{
agr: mgr.baModule,
recv: mgr.baModule.data.recv.(*consensusBAReceiver),
}
- roundBeginTime time.Time
- roundEndTime time.Time
- tickDuration time.Duration
+ tickDuration time.Duration
)
// Check if this routine needs to awake in this round and prepare essential
@@ -327,24 +311,20 @@ func (mgr *agreementMgr) runBA(initRound uint64) {
nextRound++
}()
// Wait until the configuartion for next round is ready.
- var config *agreementMgrConfig
for {
- if config = mgr.getConfig(nextRound); config != nil {
+ if curConfig = mgr.config(nextRound); curConfig != nil {
break
} else {
mgr.logger.Debug("round is not ready", "round", nextRound)
time.Sleep(1 * time.Second)
}
}
- // Set next checkpoint.
- roundBeginTime = config.beginTime
- roundEndTime = config.beginTime.Add(config.roundInterval)
// Check if this node in notary set of this chain in this round.
- notarySet, err := mgr.cache.GetNotarySet(nextRound, 0)
+ notarySet, err := mgr.cache.GetNotarySet(nextRound)
if err != nil {
panic(err)
}
- setting.crs = config.crs
+ setting.crs = curConfig.crs
setting.notarySet = notarySet
_, isNotary = setting.notarySet[mgr.ID]
if isNotary {
@@ -357,12 +337,12 @@ func (mgr *agreementMgr) runBA(initRound uint64) {
"round", nextRound)
}
// Setup ticker
- if tickDuration != config.lambdaBA {
+ if tickDuration != curConfig.lambdaBA {
if setting.ticker != nil {
setting.ticker.Stop()
}
setting.ticker = newTicker(mgr.gov, nextRound, TickerBA)
- tickDuration = config.lambdaBA
+ tickDuration = curConfig.lambdaBA
}
return
}
@@ -373,29 +353,13 @@ Loop:
break Loop
default:
}
- now := time.Now().UTC()
setting.recv.isNotary = checkRound()
- // Sleep until round begin. Here a biased round begin time would be
- // used instead of the one in config. The reason it to disperse the load
- // of fullnodes to verify confirmed blocks from each chain.
- if now.Before(pickBiasedTime(roundBeginTime, 4*tickDuration)) {
- select {
- case <-mgr.ctx.Done():
- break Loop
- case <-time.After(roundBeginTime.Sub(now)):
- }
- // Clean the tick channel after awake: the tick would be queued in
- // channel, thus the first few ticks would not tick on expected
- // interval.
- <-setting.ticker.Tick()
- <-setting.ticker.Tick()
- }
// Run BA for this round.
setting.recv.roundValue.Store(currentRound)
- setting.recv.changeNotaryTime = roundEndTime
+ setting.recv.changeNotaryHeight = curConfig.roundEndHeight
setting.recv.restartNotary <- types.Position{
- Round: setting.recv.round(),
- ChainID: math.MaxUint32,
+ Round: setting.recv.round(),
+ Height: math.MaxUint64,
}
mgr.voteFilter = utils.NewVoteFilter()
if err := mgr.baRoutineForOneRound(&setting); err != nil {
@@ -450,7 +414,7 @@ func (mgr *agreementMgr) baRoutineForOneRound(
if isStop(oldPos) && nextHeight == 0 {
break
}
- if isStop(restartPos) && nextHeight == 0 {
+ if isStop(restartPos) {
break
}
if nextHeight > restartPos.Height {
diff --git a/core/agreement.go b/core/agreement.go
index 579cea8..43fddd0 100644
--- a/core/agreement.go
+++ b/core/agreement.go
@@ -254,12 +254,12 @@ func (a *agreement) restart(
func (a *agreement) stop() {
a.restart(make(map[types.NodeID]struct{}), types.Position{
- ChainID: math.MaxUint32,
+ Height: math.MaxUint64,
}, types.NodeID{}, common.Hash{})
}
func isStop(aID types.Position) bool {
- return aID.ChainID == math.MaxUint32
+ return aID.Height == math.MaxUint64
}
// clocks returns how many time this state is required.
diff --git a/core/blockchain.go b/core/blockchain.go
index d1aa644..001892e 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -110,7 +110,7 @@ func newBlockChainConfig(prev blockChainConfig, config *types.Config) (
c blockChainConfig) {
c = blockChainConfig{}
c.fromConfig(prev.roundID+1, config)
- c.setRoundBeginTime(prev.roundEndTime)
+ c.setRoundBeginHeight(prev.roundEndHeight)
return
}
@@ -131,9 +131,10 @@ type blockChain struct {
configs []blockChainConfig
pendingBlocks pendingBlockRecords
confirmedBlocks types.BlocksByPosition
+ dMoment time.Time
}
-func newBlockChain(nID types.NodeID, initBlock *types.Block,
+func newBlockChain(nID types.NodeID, dMoment time.Time, initBlock *types.Block,
initConfig blockChainConfig, app Application, vGetter tsigVerifierGetter,
signer *utils.Signer, logger common.Logger) *blockChain {
if initBlock != nil {
@@ -156,6 +157,7 @@ func newBlockChain(nID types.NodeID, initBlock *types.Block,
app: app,
logger: logger,
configs: []blockChainConfig{initConfig},
+ dMoment: dMoment,
pendingRandomnesses: make(
map[types.Position]*types.BlockRandomnessResult),
}
@@ -203,9 +205,6 @@ func (bc *blockChain) extractBlocks() (ret []*types.Block) {
}
func (bc *blockChain) sanityCheck(b *types.Block) error {
- if b.Position.ChainID != 0 {
- panic(fmt.Errorf("attempt to process block from non-zero chainID"))
- }
if b.IsEmpty() {
panic(fmt.Errorf("pass empty block to sanity check: %s", b))
}
@@ -228,7 +227,7 @@ func (bc *blockChain) sanityCheck(b *types.Block) error {
}
return ErrInvalidBlockHeight
}
- tipConfig := bc.getTipConfig()
+ tipConfig := bc.tipConfig()
if tipConfig.isLastBlock(bc.lastConfirmed) {
if b.Position.Round != bc.lastConfirmed.Position.Round+1 {
return ErrRoundNotSwitch
@@ -250,9 +249,6 @@ func (bc *blockChain) sanityCheck(b *types.Block) error {
// addEmptyBlock is called when an empty block is confirmed by BA.
func (bc *blockChain) addEmptyBlock(position types.Position) (
*types.Block, error) {
- if position.ChainID != 0 {
- panic(fmt.Errorf("attempt to process block from non-zero chainID"))
- }
bc.lock.Lock()
defer bc.lock.Unlock()
add := func() *types.Block {
@@ -286,9 +282,6 @@ func (bc *blockChain) addEmptyBlock(position types.Position) (
// addBlock should be called when the block is confirmed by BA, we won't perform
// sanity check against this block, it's ok to add block with skipping height.
func (bc *blockChain) addBlock(b *types.Block) error {
- if b.Position.ChainID != 0 {
- panic(fmt.Errorf("attempt to process block from non-zero chainID"))
- }
bc.lock.Lock()
defer bc.lock.Unlock()
confirmed := false
@@ -314,9 +307,6 @@ func (bc *blockChain) addBlock(b *types.Block) error {
}
func (bc *blockChain) addRandomness(r *types.BlockRandomnessResult) error {
- if r.Position.ChainID != 0 {
- panic(fmt.Errorf("attempt to process block from non-zero chainID"))
- }
if func() bool {
bc.lock.RLock()
defer bc.lock.RUnlock()
@@ -361,8 +351,8 @@ func (bc *blockChain) tipRound() uint64 {
if bc.lastConfirmed == nil {
return 0
}
- offset := uint64(0)
- if bc.lastConfirmed.Timestamp.After(bc.getTipConfig().roundEndTime) {
+ offset, tipConfig := uint64(0), bc.tipConfig()
+ if tipConfig.isLastBlock(bc.lastConfirmed) {
offset++
}
return bc.lastConfirmed.Position.Round + offset
@@ -392,7 +382,7 @@ func (bc *blockChain) nextBlock() (uint64, time.Time) {
// lastConfirmed block in the scenario of "nextBlock" method.
tip, config := bc.lastConfirmed, bc.configs[0]
if tip == nil {
- return 0, config.roundBeginTime
+ return 0, bc.dMoment
}
return tip.Position.Height + 1, tip.Timestamp.Add(config.minBlockInterval)
}
@@ -528,26 +518,31 @@ func (bc *blockChain) prepareBlock(position types.Position,
b = &types.Block{Position: position, Timestamp: proposeTime}
tip := bc.lastConfirmed
// Make sure we can propose a block at expected position for callers.
- expectedPosition := types.Position{}
if tip == nil {
// The case for genesis block.
- if !position.Equal(expectedPosition) {
+ if !position.Equal(types.Position{}) {
b, err = nil, ErrNotGenesisBlock
+ return
} else if empty {
- b.Timestamp = bc.configs[0].roundBeginTime
+ b.Timestamp = bc.dMoment
}
} else {
- expectedPosition.Height = tip.Position.Height + 1
- tipConfig := bc.getTipConfig()
- if tipConfig.isLastBlock(tip) {
- expectedPosition.Round = tip.Position.Round + 1
- } else {
- expectedPosition.Round = tip.Position.Round
- }
- if !expectedPosition.Equal(position) {
+ tipConfig := bc.tipConfig()
+ if tip.Position.Height+1 != position.Height {
b, err = nil, ErrNotFollowTipPosition
return
}
+ if tipConfig.isLastBlock(tip) {
+ if tip.Position.Round+1 != position.Round {
+ b, err = nil, ErrRoundNotSwitch
+ return
+ }
+ } else {
+ if tip.Position.Round != position.Round {
+ b, err = nil, ErrInvalidRoundID
+ return
+ }
+ }
b.ParentHash = tip.Hash
if !empty {
bc.logger.Debug("Calling Application.PreparePayload",
@@ -564,7 +559,6 @@ func (bc *blockChain) prepareBlock(position types.Position,
if !b.Timestamp.After(tip.Timestamp) {
b.Timestamp = tip.Timestamp.Add(tipConfig.minBlockInterval)
}
-
} else {
b.Witness.Height = tip.Witness.Height
b.Witness.Data = make([]byte, len(tip.Witness.Data))
@@ -585,7 +579,7 @@ func (bc *blockChain) prepareBlock(position types.Position,
return
}
-func (bc *blockChain) getTipConfig() blockChainConfig {
+func (bc *blockChain) tipConfig() blockChainConfig {
if bc.lastConfirmed == nil {
panic(fmt.Errorf("attempting to access config without tip"))
}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 908b04f..382ccd7 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -51,7 +51,6 @@ type BlockChainTestSuite struct {
signer *utils.Signer
dMoment time.Time
blockInterval time.Duration
- roundInterval time.Duration
}
func (s *BlockChainTestSuite) SetupSuite() {
@@ -61,7 +60,6 @@ func (s *BlockChainTestSuite) SetupSuite() {
s.signer = utils.NewSigner(prvKeys[0])
s.dMoment = time.Now().UTC()
s.blockInterval = 1 * time.Millisecond
- s.roundInterval = 10 * time.Second
}
func (s *BlockChainTestSuite) newBlocks(c uint64, initBlock *types.Block) (
@@ -129,7 +127,8 @@ func (s *BlockChainTestSuite) newRandomnessFromBlock(
}
}
-func (s *BlockChainTestSuite) newBlockChain(initB *types.Block) *blockChain {
+func (s *BlockChainTestSuite) newBlockChain(initB *types.Block,
+ roundInterval uint64) *blockChain {
initRound := uint64(0)
if initB != nil {
initRound = initB.Position.Round
@@ -137,11 +136,16 @@ func (s *BlockChainTestSuite) newBlockChain(initB *types.Block) *blockChain {
initConfig := blockChainConfig{}
initConfig.fromConfig(initRound, &types.Config{
MinBlockInterval: s.blockInterval,
- RoundInterval: s.roundInterval,
+ RoundInterval: roundInterval,
})
- initConfig.setRoundBeginTime(s.dMoment)
- return newBlockChain(s.nID, initB, initConfig, test.NewApp(0, nil),
- &testTSigVerifierGetter{}, s.signer, &common.NullLogger{})
+ if initB != nil {
+ initConfig.setRoundBeginHeight(initB.Position.Height)
+ } else {
+ initConfig.setRoundBeginHeight(0)
+ }
+ return newBlockChain(s.nID, s.dMoment, initB, initConfig,
+ test.NewApp(0, nil), &testTSigVerifierGetter{}, s.signer,
+ &common.NullLogger{})
}
func (s *BlockChainTestSuite) newRoundOneInitBlock() *types.Block {
@@ -157,7 +161,7 @@ func (s *BlockChainTestSuite) newRoundOneInitBlock() *types.Block {
func (s *BlockChainTestSuite) baseConcurrentAceessTest(initBlock *types.Block,
blocks []*types.Block, rands []*types.BlockRandomnessResult) {
var (
- bc = s.newBlockChain(initBlock)
+ bc = s.newBlockChain(initBlock, uint64(len(blocks)+1))
start = make(chan struct{})
newNotif = make(chan struct{}, 1)
delivered []*types.Block
@@ -214,7 +218,7 @@ func (s *BlockChainTestSuite) baseConcurrentAceessTest(initBlock *types.Block,
func (s *BlockChainTestSuite) TestBasicUsage() {
initBlock := s.newRoundOneInitBlock()
- bc := s.newBlockChain(initBlock)
+ bc := s.newBlockChain(initBlock, 10)
// test scenario: block, empty block, randomness can be added in any order
// of position.
blocks := s.newBlocks(4, initBlock)
@@ -273,11 +277,7 @@ func (s *BlockChainTestSuite) TestConcurrentAccess() {
}
func (s *BlockChainTestSuite) TestSanityCheck() {
- bc := s.newBlockChain(nil)
- // Non-zero chainID is not allowed.
- s.Require().Panics(func() {
- bc.sanityCheck(&types.Block{Position: types.Position{ChainID: 1}})
- })
+ bc := s.newBlockChain(nil, 4)
// Empty block is not allowed.
s.Require().Panics(func() {
bc.sanityCheck(&types.Block{})
@@ -331,7 +331,7 @@ func (s *BlockChainTestSuite) TestSanityCheck() {
}
func (s *BlockChainTestSuite) TestAppendConfig() {
- bc := s.newBlockChain(nil)
+ bc := s.newBlockChain(nil, 10)
s.Require().Equal(ErrRoundNotIncreasing.Error(),
bc.appendConfig(0, &types.Config{}).Error())
s.Require().Equal(ErrRoundNotIncreasing.Error(),
@@ -340,7 +340,7 @@ func (s *BlockChainTestSuite) TestAppendConfig() {
}
func (s *BlockChainTestSuite) TestConfirmed() {
- bc := s.newBlockChain(nil)
+ bc := s.newBlockChain(nil, 10)
blocks := s.newBlocks(3, nil)
// Add a confirmed block.
s.Require().NoError(bc.addBlock(blocks[0]))
@@ -351,12 +351,17 @@ func (s *BlockChainTestSuite) TestConfirmed() {
s.Require().True(bc.confirmed(2))
}
-func (s *BlockChainTestSuite) TestNextBlock() {
- bc := s.newBlockChain(nil)
+func (s *BlockChainTestSuite) TestNextBlockAndTipRound() {
+ var roundInterval uint64 = 3
+ bc := s.newBlockChain(nil, roundInterval)
+ s.Require().NoError(bc.appendConfig(1, &types.Config{
+ MinBlockInterval: s.blockInterval,
+ RoundInterval: roundInterval,
+ }))
blocks := s.newBlocks(3, nil)
nextH, nextT := bc.nextBlock()
s.Require().Equal(nextH, uint64(0))
- s.Require().Equal(nextT, bc.configs[0].roundBeginTime)
+ s.Require().Equal(nextT, s.dMoment)
// Add one block.
s.Require().NoError(bc.addBlock(blocks[0]))
nextH, nextT = bc.nextBlock()
@@ -368,11 +373,17 @@ func (s *BlockChainTestSuite) TestNextBlock() {
nextH2, nextT2 := bc.nextBlock()
s.Require().Equal(nextH, nextH2)
s.Require().Equal(nextT, nextT2)
+ // Add a block, which is the last block of this round.
+ b3 := s.newBlock(blocks[2], 1, 1*time.Second)
+ s.Require().NoError(bc.addBlock(blocks[1]))
+ s.Require().NoError(bc.sanityCheck(b3))
+ s.Require().NoError(bc.addBlock(b3))
+ s.Require().Equal(bc.tipRound(), uint64(1))
}
func (s *BlockChainTestSuite) TestPendingBlocksWithoutRandomness() {
initBlock := s.newRoundOneInitBlock()
- bc := s.newBlockChain(initBlock)
+ bc := s.newBlockChain(initBlock, 10)
blocks := s.newBlocks(4, initBlock)
s.Require().NoError(bc.addBlock(blocks[0]))
s.Require().NoError(bc.addBlock(blocks[1]))
@@ -386,7 +397,7 @@ func (s *BlockChainTestSuite) TestPendingBlocksWithoutRandomness() {
func (s *BlockChainTestSuite) TestLastXBlock() {
initBlock := s.newRoundOneInitBlock()
- bc := s.newBlockChain(initBlock)
+ bc := s.newBlockChain(initBlock, 10)
s.Require().Nil(bc.lastPendingBlock())
s.Require().True(bc.lastDeliveredBlock() == initBlock)
blocks := s.newBlocks(2, initBlock)
@@ -422,7 +433,7 @@ func (s *BlockChainTestSuite) TestPendingBlockRecords() {
}
func (s *BlockChainTestSuite) TestFindPendingBlock() {
- bc := s.newBlockChain(nil)
+ bc := s.newBlockChain(nil, 10)
blocks := s.newBlocks(7, nil)
s.Require().NoError(bc.addBlock(blocks[6]))
s.Require().NoError(bc.addBlock(blocks[5]))
@@ -440,7 +451,7 @@ func (s *BlockChainTestSuite) TestFindPendingBlock() {
}
func (s *BlockChainTestSuite) TestAddEmptyBlockDirectly() {
- bc := s.newBlockChain(nil)
+ bc := s.newBlockChain(nil, 10)
blocks := s.newBlocks(1, nil)
s.Require().NoError(bc.addBlock(blocks[0]))
// Add an empty block after a normal block.
diff --git a/core/consensus.go b/core/consensus.go
index f4c0a37..b3fd312 100644
--- a/core/consensus.go
+++ b/core/consensus.go
@@ -58,12 +58,12 @@ var (
// consensusBAReceiver implements agreementReceiver.
type consensusBAReceiver struct {
// TODO(mission): consensus would be replaced by blockChain and network.
- consensus *Consensus
- agreementModule *agreement
- changeNotaryTime time.Time
- roundValue *atomic.Value
- isNotary bool
- restartNotary chan types.Position
+ consensus *Consensus
+ agreementModule *agreement
+ changeNotaryHeight uint64
+ roundValue *atomic.Value
+ isNotary bool
+ restartNotary chan types.Position
}
func (recv *consensusBAReceiver) round() uint64 {
@@ -241,10 +241,17 @@ CleanChannelLoop:
}
}
newPos := block.Position
- if block.Timestamp.After(recv.changeNotaryTime) {
+ if block.Position.Height+1 == recv.changeNotaryHeight {
newPos.Round++
recv.roundValue.Store(newPos.Round)
}
+ currentRound := recv.round()
+ if block.Position.Height > recv.changeNotaryHeight &&
+ block.Position.Round <= currentRound {
+ panic(fmt.Errorf(
+ "round not switch when confirmig: %s, %d, should switch at %d",
+ block, currentRound, recv.changeNotaryHeight))
+ }
recv.restartNotary <- newPos
}
@@ -383,7 +390,6 @@ type Consensus struct {
bcModule *blockChain
dMoment time.Time
nodeSetCache *utils.NodeSetCache
- round uint64
roundForNewConfig uint64
lock sync.RWMutex
ctx context.Context
@@ -412,7 +418,7 @@ func NewConsensus(
prv crypto.PrivateKey,
logger common.Logger) *Consensus {
return newConsensusForRound(
- nil, dMoment, app, gov, db, network, prv, logger, true)
+ nil, 0, dMoment, app, gov, db, network, prv, logger, true)
}
// NewConsensusForSimulation creates an instance of Consensus for simulation,
@@ -426,7 +432,7 @@ func NewConsensusForSimulation(
prv crypto.PrivateKey,
logger common.Logger) *Consensus {
return newConsensusForRound(
- nil, dMoment, app, gov, db, network, prv, logger, false)
+ nil, 0, dMoment, app, gov, db, network, prv, logger, false)
}
// NewConsensusFromSyncer constructs an Consensus instance from information
@@ -440,7 +446,8 @@ func NewConsensusForSimulation(
// their positions, in ascending order.
func NewConsensusFromSyncer(
initBlock *types.Block,
- initRoundBeginTime time.Time,
+ initRoundBeginHeight uint64,
+ dMoment time.Time,
app Application,
gov Governance,
db db.Database,
@@ -451,8 +458,8 @@ func NewConsensusFromSyncer(
cachedMessages []interface{},
logger common.Logger) (*Consensus, error) {
// Setup Consensus instance.
- con := newConsensusForRound(initBlock, initRoundBeginTime, app, gov, db,
- networkModule, prv, logger, true)
+ con := newConsensusForRound(initBlock, initRoundBeginHeight, dMoment, app,
+ gov, db, networkModule, prv, logger, true)
// Launch a dummy receiver before we start receiving from network module.
con.dummyMsgBuffer = cachedMessages
con.dummyCancel, con.dummyFinished = utils.LaunchDummyReceiver(
@@ -486,9 +493,11 @@ func NewConsensusFromSyncer(
}
// newConsensusForRound creates a Consensus instance.
+// TODO(mission): remove dMoment, it's no longer one part of consensus.
func newConsensusForRound(
initBlock *types.Block,
- initRoundBeginTime time.Time,
+ initRoundBeginHeight uint64,
+ dMoment time.Time,
app Application,
gov Governance,
db db.Database,
@@ -511,6 +520,7 @@ func newConsensusForRound(
initRound = initBlock.Position.Round
}
initConfig := utils.GetConfigWithPanic(gov, initRound, logger)
+ initCRS := utils.GetCRSWithPanic(gov, initRound, logger)
// Init configuration chain.
ID := types.NewNodeID(prv.PublicKey())
recv := &consensusDKGReceiver{
@@ -529,8 +539,8 @@ func newConsensusForRound(
}
bcConfig := blockChainConfig{}
bcConfig.fromConfig(initRound, initConfig)
- bcConfig.setRoundBeginTime(initRoundBeginTime)
- bcModule := newBlockChain(ID, initBlock, bcConfig, appModule,
+ bcConfig.setRoundBeginHeight(initRoundBeginHeight)
+ bcModule := newBlockChain(ID, dMoment, initBlock, bcConfig, appModule,
NewTSigVerifierCache(gov, 7), signer, logger)
// Construct Consensus instance.
con := &Consensus{
@@ -544,7 +554,7 @@ func newConsensusForRound(
dkgReady: sync.NewCond(&sync.Mutex{}),
cfgModule: cfgModule,
bcModule: bcModule,
- dMoment: initRoundBeginTime,
+ dMoment: dMoment,
nodeSetCache: nodeSetCache,
signer: signer,
event: common.NewEvent(),
@@ -555,8 +565,15 @@ func newConsensusForRound(
processBlockChan: make(chan *types.Block, 1024),
}
con.ctx, con.ctxCancel = context.WithCancel(context.Background())
- con.baMgr = newAgreementMgr(con, initRound, initRoundBeginTime)
- if err := con.prepare(initBlock); err != nil {
+ baConfig := agreementMgrConfig{}
+ baConfig.from(initRound, initConfig, initCRS)
+ baConfig.setRoundBeginHeight(initRoundBeginHeight)
+ var err error
+ con.baMgr, err = newAgreementMgr(con, initRound, baConfig)
+ if err != nil {
+ panic(err)
+ }
+ if err = con.prepare(initRoundBeginHeight, initBlock); err != nil {
panic(err)
}
return con
@@ -566,26 +583,17 @@ func newConsensusForRound(
// 'initBlock' could be either:
// - nil
// - the last finalized block
-func (con *Consensus) prepare(initBlock *types.Block) (err error) {
+func (con *Consensus) prepare(
+ initRoundBeginHeight uint64, initBlock *types.Block) (err error) {
// The block past from full node should be delivered already or known by
// full node. We don't have to notify it.
initRound := uint64(0)
if initBlock != nil {
initRound = initBlock.Position.Round
}
+ // Setup blockChain module.
con.roundForNewConfig = initRound + 1
initConfig := utils.GetConfigWithPanic(con.gov, initRound, con.logger)
- // Setup context.
- con.logger.Debug("Calling Governance.CRS", "round", initRound)
- initCRS := con.gov.CRS(initRound)
- if (initCRS == common.Hash{}) {
- err = ErrCRSNotReady
- return
- }
- if err = con.baMgr.appendConfig(initRound, initConfig, initCRS); err != nil {
- return
- }
- // Setup blockChain module.
initPlusOneCfg := utils.GetConfigWithPanic(con.gov, initRound+1, con.logger)
if err = con.bcModule.appendConfig(initRound+1, initPlusOneCfg); err != nil {
return
@@ -604,15 +612,16 @@ func (con *Consensus) prepare(initBlock *types.Block) (err error) {
// DKG would success. Three is a magic number.
time.Sleep(initConfig.MinBlockInterval * 3)
con.cfgModule.registerDKG(initRound, getDKGThreshold(initConfig))
- con.event.RegisterTime(con.dMoment.Add(initConfig.RoundInterval/4),
- func(time.Time) {
+ con.event.RegisterHeight(
+ initConfig.RoundInterval/4,
+ func(uint64) {
con.runDKG(initRound, initConfig)
})
}()
}
}
// Register events.
- con.initialRound(con.dMoment, initRound, initConfig)
+ con.initialRound(initRoundBeginHeight, initRound, initConfig)
return
}
@@ -672,18 +681,11 @@ func (con *Consensus) runDKG(round uint64, config *types.Config) {
}
con.dkgRunning = 1
go func() {
- startTime := time.Now().UTC()
defer func() {
con.dkgReady.L.Lock()
defer con.dkgReady.L.Unlock()
con.dkgReady.Broadcast()
con.dkgRunning = 2
- DKGTime := time.Now().Sub(startTime)
- if DKGTime.Nanoseconds() >=
- config.RoundInterval.Nanoseconds()/2 {
- con.logger.Warn("Your computer cannot finish DKG on time!",
- "nodeID", con.ID.String())
- }
}()
if err := con.cfgModule.runDKG(round); err != nil {
con.logger.Error("Failed to runDKG", "error", err)
@@ -739,7 +741,7 @@ func (con *Consensus) runCRS(round uint64) {
}
func (con *Consensus) initialRound(
- startTime time.Time, round uint64, config *types.Config) {
+ startHeight uint64, round uint64, config *types.Config) {
select {
case <-con.ctx.Done():
return
@@ -752,8 +754,9 @@ func (con *Consensus) initialRound(
}
// Initiate CRS routine.
if _, exist := curDkgSet[con.ID]; exist {
- con.event.RegisterTime(startTime.Add(config.RoundInterval/2),
- func(time.Time) {
+ con.event.RegisterHeight(
+ startHeight+config.RoundInterval/2,
+ func(uint64) {
go func() {
con.runCRS(round)
}()
@@ -774,76 +777,71 @@ func (con *Consensus) initialRound(
}
}
// Initiate BA modules.
- con.event.RegisterTime(
- startTime.Add(config.RoundInterval/2+config.LambdaDKG),
- func(time.Time) {
- go func(nextRound uint64) {
- if !checkWithCancel(
- con.ctx, 500*time.Millisecond, checkCRS(nextRound)) {
- con.logger.Debug("unable to prepare CRS for baMgr",
- "round", nextRound)
- return
- }
- // Notify BA for new round.
- nextConfig := utils.GetConfigWithPanic(
- con.gov, nextRound, con.logger)
- nextCRS := utils.GetCRSWithPanic(
- con.gov, nextRound, con.logger)
- con.logger.Info("appendConfig for baMgr", "round", nextRound)
- if err := con.baMgr.appendConfig(
- nextRound, nextConfig, nextCRS); err != nil {
- panic(err)
- }
- }(round + 1)
- })
+ con.event.RegisterHeight(startHeight+config.RoundInterval/2, func(uint64) {
+ go func(nextRound uint64) {
+ if !checkWithCancel(
+ con.ctx, 500*time.Millisecond, checkCRS(nextRound)) {
+ con.logger.Debug("unable to prepare CRS for baMgr",
+ "round", nextRound)
+ return
+ }
+ // Notify BA for new round.
+ nextConfig := utils.GetConfigWithPanic(
+ con.gov, nextRound, con.logger)
+ nextCRS := utils.GetCRSWithPanic(
+ con.gov, nextRound, con.logger)
+ con.logger.Info("appendConfig for baMgr", "round", nextRound)
+ if err := con.baMgr.appendConfig(
+ nextRound, nextConfig, nextCRS); err != nil {
+ panic(err)
+ }
+ }(round + 1)
+ })
// Initiate DKG for this round.
- con.event.RegisterTime(startTime.Add(config.RoundInterval/2+config.LambdaDKG),
- func(time.Time) {
- go func(nextRound uint64) {
- // Normally, gov.CRS would return non-nil. Use this for in case of
- // unexpected network fluctuation and ensure the robustness.
- if !checkWithCancel(
- con.ctx, 500*time.Millisecond, checkCRS(nextRound)) {
- con.logger.Debug("unable to prepare CRS for DKG set",
- "round", nextRound)
- return
- }
- nextDkgSet, err := con.nodeSetCache.GetDKGSet(nextRound)
- if err != nil {
- con.logger.Error("Error getting DKG set",
- "round", nextRound,
- "error", err)
- return
- }
- if _, exist := nextDkgSet[con.ID]; !exist {
- return
- }
- con.logger.Info("Selected as DKG set", "round", nextRound)
- con.cfgModule.registerDKG(nextRound, getDKGThreshold(config))
- con.event.RegisterTime(
- startTime.Add(config.RoundInterval*2/3),
- func(time.Time) {
- func() {
- con.dkgReady.L.Lock()
- defer con.dkgReady.L.Unlock()
- con.dkgRunning = 0
- }()
- nextConfig := utils.GetConfigWithPanic(
- con.gov, nextRound, con.logger)
- con.runDKG(nextRound, nextConfig)
- })
- }(round + 1)
- })
+ con.event.RegisterHeight(startHeight+config.RoundInterval/2, func(uint64) {
+ go func(nextRound uint64) {
+ // Normally, gov.CRS would return non-nil. Use this for in case of
+ // unexpected network fluctuation and ensure the robustness.
+ if !checkWithCancel(
+ con.ctx, 500*time.Millisecond, checkCRS(nextRound)) {
+ con.logger.Debug("unable to prepare CRS for DKG set",
+ "round", nextRound)
+ return
+ }
+ nextDkgSet, err := con.nodeSetCache.GetDKGSet(nextRound)
+ if err != nil {
+ con.logger.Error("Error getting DKG set",
+ "round", nextRound,
+ "error", err)
+ return
+ }
+ if _, exist := nextDkgSet[con.ID]; !exist {
+ return
+ }
+ con.logger.Info("Selected as DKG set", "round", nextRound)
+ con.cfgModule.registerDKG(nextRound, getDKGThreshold(config))
+ con.event.RegisterHeight(startHeight+config.RoundInterval*2/3,
+ func(uint64) {
+ func() {
+ con.dkgReady.L.Lock()
+ defer con.dkgReady.L.Unlock()
+ con.dkgRunning = 0
+ }()
+ nextConfig := utils.GetConfigWithPanic(
+ con.gov, nextRound, con.logger)
+ con.runDKG(nextRound, nextConfig)
+ })
+ }(round + 1)
+ })
// Prepare blockChain module for next round and next "initialRound" routine.
- con.event.RegisterTime(startTime.Add(config.RoundInterval),
- func(time.Time) {
- // Change round.
- // Get configuration for next round.
- nextRound := round + 1
- nextConfig := utils.GetConfigWithPanic(con.gov, nextRound, con.logger)
- con.initialRound(
- startTime.Add(config.RoundInterval), nextRound, nextConfig)
- })
+ con.event.RegisterHeight(startHeight+config.RoundInterval, func(uint64) {
+ // Change round.
+ // Get configuration for next round.
+ nextRound := round + 1
+ nextConfig := utils.GetConfigWithPanic(con.gov, nextRound, con.logger)
+ con.initialRound(
+ startHeight+config.RoundInterval, nextRound, nextConfig)
+ })
}
// Stop the Consensus core.
@@ -1194,7 +1192,7 @@ func (con *Consensus) deliverFinalizedBlocksWithoutLock() (err error) {
"pending", con.bcModule.lastPendingBlock())
for _, b := range deliveredBlocks {
con.deliverBlock(b)
- go con.event.NotifyTime(b.Finalization.Timestamp)
+ go con.event.NotifyHeight(b.Finalization.Height)
}
return
}
diff --git a/core/consensus_test.go b/core/consensus_test.go
index fe28320..ca619df 100644
--- a/core/consensus_test.go
+++ b/core/consensus_test.go
@@ -203,16 +203,6 @@ func (s *ConsensusTestSuite) prepareConsensus(
return app, con
}
-func (s *ConsensusTestSuite) prepareAgreementMgrWithoutRunning(
- con *Consensus, numChains uint32) {
- // This is a workaround to setup agreementMgr.
- con.baMgr.appendConfig(0, &types.Config{
- NumChains: numChains,
- RoundInterval: time.Hour,
- LambdaBA: 50 * time.Millisecond,
- }, common.NewRandomHash())
-}
-
func (s *ConsensusTestSuite) TestDKGCRS() {
n := 21
lambda := 200 * time.Millisecond
@@ -229,7 +219,7 @@ func (s *ConsensusTestSuite) TestDKGCRS() {
gov, err := test.NewGovernance(test.NewState(
pubKeys, lambda, &common.NullLogger{}, true), ConfigRoundShift)
s.Require().NoError(err)
- gov.State().RequestChange(test.StateChangeRoundInterval, 200*lambda)
+ gov.State().RequestChange(test.StateChangeRoundInterval, uint64(200))
cons := map[types.NodeID]*Consensus{}
dMoment := time.Now().UTC()
for _, key := range prvKeys {
@@ -281,9 +271,8 @@ func (s *ConsensusTestSuite) TestSyncBA() {
signers = append(signers, utils.NewSigner(prvKey))
}
pos := types.Position{
- Round: 0,
- ChainID: 0,
- Height: 20,
+ Round: 0,
+ Height: 20,
}
baResult := &types.AgreementResult{
BlockHash: hash,
diff --git a/core/round-based-config.go b/core/round-based-config.go
index 67779a6..f2cfc82 100644
--- a/core/round-based-config.go
+++ b/core/round-based-config.go
@@ -18,20 +18,16 @@
package core
import (
- "time"
+ "fmt"
"github.com/dexon-foundation/dexon-consensus/core/types"
)
type roundBasedConfig struct {
- roundID uint64
-
- // roundBeginTime is the beginning of round, as local time.
- roundBeginTime time.Time
- roundInterval time.Duration
-
- // roundEndTime is a cache for begin + interval.
- roundEndTime time.Time
+ roundID uint64
+ roundBeginHeight uint64
+ roundEndHeight uint64
+ roundInterval uint64
}
func (config *roundBasedConfig) setupRoundBasedFields(
@@ -40,13 +36,16 @@ func (config *roundBasedConfig) setupRoundBasedFields(
config.roundInterval = cfg.RoundInterval
}
-func (config *roundBasedConfig) setRoundBeginTime(begin time.Time) {
- config.roundBeginTime = begin
- config.roundEndTime = begin.Add(config.roundInterval)
+func (config *roundBasedConfig) setRoundBeginHeight(begin uint64) {
+ config.roundBeginHeight = begin
+ config.roundEndHeight = begin + config.roundInterval
}
// isLastBlock checks if a block is the last block of this round.
func (config *roundBasedConfig) isLastBlock(b *types.Block) bool {
- return b.Position.Round == config.roundID &&
- b.Timestamp.After(config.roundEndTime)
+ if b.Position.Round != config.roundID {
+ panic(fmt.Errorf("attempt to compare by different round: %s, %d",
+ b, config.roundID))
+ }
+ return b.Position.Height+1 == config.roundEndHeight
}
diff --git a/core/syncer/consensus.go b/core/syncer/consensus.go
index 618d90e..538913f 100644
--- a/core/syncer/consensus.go
+++ b/core/syncer/consensus.go
@@ -67,7 +67,7 @@ type Consensus struct {
blocks types.BlocksByPosition
agreementModule *agreement
configs []*types.Config
- roundBeginTimes []time.Time
+ roundBeginHeights []uint64
agreementRoundCut uint64
// lock for accessing all fields.
@@ -109,7 +109,7 @@ func NewConsensus(
configs: []*types.Config{
utils.GetConfigWithPanic(gov, 0, logger),
},
- roundBeginTimes: []time.Time{dMoment},
+ roundBeginHeights: []uint64{0},
receiveChan: make(chan *types.Block, 1000),
pullChan: make(chan common.Hash, 1000),
randomnessResults: make(map[common.Hash]*types.BlockRandomnessResult),
@@ -280,7 +280,8 @@ func (con *Consensus) GetSyncedConsensus() (*core.Consensus, error) {
var err error
con.syncedConsensus, err = core.NewConsensusFromSyncer(
con.syncedLastBlock,
- con.roundBeginTimes[con.syncedLastBlock.Position.Round],
+ con.roundBeginHeights[con.syncedLastBlock.Position.Round],
+ con.dMoment,
con.app,
con.gov,
con.db,
@@ -368,9 +369,9 @@ func (con *Consensus) setupConfigsUntilRound(round uint64) {
for r := uint64(len(con.configs)); r <= round; r++ {
cfg := utils.GetConfigWithPanic(con.gov, r, con.logger)
con.configs = append(con.configs, cfg)
- con.roundBeginTimes = append(
- con.roundBeginTimes,
- con.roundBeginTimes[r-1].Add(con.configs[r-1].RoundInterval))
+ con.roundBeginHeights = append(
+ con.roundBeginHeights,
+ con.roundBeginHeights[r-1]+con.configs[r-1].RoundInterval)
}
}
@@ -453,41 +454,22 @@ func (con *Consensus) startNetwork() {
con.moduleWaitGroup.Add(1)
go func() {
defer con.moduleWaitGroup.Done()
- Loop:
+ loop:
for {
select {
case val := <-con.network.ReceiveChan():
- var pos types.Position
switch v := val.(type) {
case *types.Block:
- pos = v.Position
case *types.AgreementResult:
- pos = v.Position
case *types.BlockRandomnessResult:
con.cacheRandomnessResult(v)
- continue Loop
+ continue loop
default:
- continue Loop
- }
- if func() bool {
- con.lock.RLock()
- defer con.lock.RUnlock()
- if pos.ChainID > 0 {
- // This error might be easily encountered when the
- // "latest" parameter of SyncBlocks is turned on too
- // early.
- con.logger.Error(
- "Unknown chainID message received (syncer)",
- "position", pos,
- )
- return false
- }
- return true
- }() {
- con.agreementModule.inputChan <- val
+ continue loop
}
+ con.agreementModule.inputChan <- val
case <-con.ctx.Done():
- return
+ break loop
}
}
}()
diff --git a/core/test/app.go b/core/test/app.go
index 1ce5b84..516974c 100644
--- a/core/test/app.go
+++ b/core/test/app.go
@@ -77,7 +77,7 @@ type AppDeliveredRecord struct {
// App implements Application interface for testing purpose.
type App struct {
Confirmed map[common.Hash]*types.Block
- LastConfirmedHeights map[uint32]uint64
+ LastConfirmedHeight uint64
confirmedLock sync.RWMutex
Delivered map[common.Hash]*AppDeliveredRecord
DeliverSequence common.Hashes
@@ -92,12 +92,11 @@ type App struct {
// NewApp constructs a TestApp instance.
func NewApp(initRound uint64, gov *Governance) (app *App) {
app = &App{
- Confirmed: make(map[common.Hash]*types.Block),
- LastConfirmedHeights: make(map[uint32]uint64),
- Delivered: make(map[common.Hash]*AppDeliveredRecord),
- DeliverSequence: common.Hashes{},
- gov: gov,
- roundToNotify: initRound,
+ Confirmed: make(map[common.Hash]*types.Block),
+ Delivered: make(map[common.Hash]*AppDeliveredRecord),
+ DeliverSequence: common.Hashes{},
+ gov: gov,
+ roundToNotify: initRound,
}
if gov != nil {
app.state = gov.State()
@@ -171,8 +170,7 @@ func (app *App) VerifyBlock(block *types.Block) types.BlockVerifyStatus {
// verify the next block in a given chain.
app.confirmedLock.RLock()
defer app.confirmedLock.RUnlock()
- if app.LastConfirmedHeights[block.Position.ChainID]+1 !=
- block.Position.Height {
+ if app.LastConfirmedHeight+1 != block.Position.Height {
return types.VerifyRetryLater
}
}
@@ -185,13 +183,11 @@ func (app *App) BlockConfirmed(b types.Block) {
defer app.confirmedLock.Unlock()
app.Confirmed[b.Hash] = &b
if b.Position.Height != 0 {
- if h, exists := app.LastConfirmedHeights[b.Position.ChainID]; exists {
- if h+1 != b.Position.Height {
- panic(ErrConfirmedHeightNotIncreasing)
- }
+ if app.LastConfirmedHeight+1 != b.Position.Height {
+ panic(ErrConfirmedHeightNotIncreasing)
}
}
- app.LastConfirmedHeights[b.Position.ChainID] = b.Position.Height
+ app.LastConfirmedHeight = b.Position.Height
}
// BlockDelivered implements Application interface.
diff --git a/core/test/governance.go b/core/test/governance.go
index 81ced54..d540280 100644
--- a/core/test/governance.go
+++ b/core/test/governance.go
@@ -382,7 +382,7 @@ func (g *Governance) Equal(other *Governance, checkState bool) bool {
// NOTE: this function should be called before running.
func (g *Governance) RegisterConfigChange(
round uint64, t StateChangeType, v interface{}) (err error) {
- if t < StateChangeNumChains || t > StateChangeDKGSetSize {
+ if t < StateAddCRS || t > StateChangeDKGSetSize {
return fmt.Errorf("state changes to register is not supported: %v", t)
}
if round < 2 {
diff --git a/core/test/governance_test.go b/core/test/governance_test.go
index cef2aea..e0a2365 100644
--- a/core/test/governance_test.go
+++ b/core/test/governance_test.go
@@ -75,16 +75,16 @@ func (s *GovernanceTestSuite) TestRegisterChange() {
genesisNodes, 100*time.Millisecond, &common.NullLogger{}, true), 2)
req.NoError(err)
// Unable to register change for genesis round.
- req.Error(g.RegisterConfigChange(0, StateChangeNumChains, uint32(32)))
+ req.Error(g.RegisterConfigChange(0, StateChangeDKGSetSize, uint32(32)))
// Make some round prepared.
g.CatchUpWithRound(4)
- req.Equal(g.Configuration(4).NumChains, uint32(20))
+ req.Equal(g.Configuration(4).DKGSetSize, uint32(20))
// Unable to register change for prepared round.
- req.Error(g.RegisterConfigChange(4, StateChangeNumChains, uint32(32)))
+ req.Error(g.RegisterConfigChange(4, StateChangeDKGSetSize, uint32(32)))
// It's ok to make some change when condition is met.
- req.NoError(g.RegisterConfigChange(5, StateChangeNumChains, uint32(32)))
- req.NoError(g.RegisterConfigChange(6, StateChangeNumChains, uint32(32)))
- req.NoError(g.RegisterConfigChange(7, StateChangeNumChains, uint32(40)))
+ req.NoError(g.RegisterConfigChange(5, StateChangeDKGSetSize, uint32(32)))
+ req.NoError(g.RegisterConfigChange(6, StateChangeDKGSetSize, uint32(32)))
+ req.NoError(g.RegisterConfigChange(7, StateChangeDKGSetSize, uint32(40)))
// In local mode, state for round 6 would be ready after notified with
// round 2.
g.NotifyRound(2)
@@ -94,8 +94,8 @@ func (s *GovernanceTestSuite) TestRegisterChange() {
g.NotifyRound(4)
// Notify governance to take a snapshot for round 7's configuration.
g.NotifyRound(5)
- req.Equal(g.Configuration(6).NumChains, uint32(32))
- req.Equal(g.Configuration(7).NumChains, uint32(40))
+ req.Equal(g.Configuration(6).DKGSetSize, uint32(32))
+ req.Equal(g.Configuration(7).DKGSetSize, uint32(40))
}
func TestGovernance(t *testing.T) {
diff --git a/core/test/network.go b/core/test/network.go
index f5d1c6e..0bbb12e 100644
--- a/core/test/network.go
+++ b/core/test/network.go
@@ -735,7 +735,7 @@ func (n *Network) getNotarySet(round uint64) map[types.NodeID]struct{} {
set, exists := n.notarySetCaches[round]
if !exists {
var err error
- set, err = n.cache.GetNotarySet(round, 0)
+ set, err = n.cache.GetNotarySet(round)
if err != nil {
panic(err)
}
diff --git a/core/test/network_test.go b/core/test/network_test.go
index fd5f97f..22c31a8 100644
--- a/core/test/network_test.go
+++ b/core/test/network_test.go
@@ -90,9 +90,8 @@ func (s *NetworkTestSuite) TestPullRequestMarshaling() {
Requester: GenerateRandomNodeIDs(1)[0],
Type: "vote",
Identity: types.Position{
- Round: 1,
- ChainID: 2,
- Height: 3,
+ Round: 1,
+ Height: 3,
}}
b, err = json.Marshal(req)
s.Require().NoError(err)
@@ -102,8 +101,6 @@ func (s *NetworkTestSuite) TestPullRequestMarshaling() {
s.Require().Equal(req.Type, req2.Type)
s.Require().Equal(req.Identity.(types.Position).Round,
req.Identity.(types.Position).Round)
- s.Require().Equal(req.Identity.(types.Position).ChainID,
- req.Identity.(types.Position).ChainID)
s.Require().Equal(req.Identity.(types.Position).Height,
req.Identity.(types.Position).Height)
}
@@ -197,9 +194,8 @@ func (s *NetworkTestSuite) TestPullVotes() {
v := types.NewVote(
types.VoteInit, common.NewRandomHash(), randObj.Uint64())
v.Position = types.Position{
- ChainID: randObj.Uint32(),
- Height: randObj.Uint64(),
- Round: uint64(randObj.Intn(int(maxRound + 1))),
+ Height: randObj.Uint64(),
+ Round: uint64(randObj.Intn(int(maxRound + 1))),
}
req.NoError(master.trans.Send(n.ID, v))
votes[v.VoteHeader] = v
@@ -262,7 +258,7 @@ func (s *NetworkTestSuite) TestBroadcastToSet() {
dkgSet, err := cache.GetDKGSet(0)
req.NoError(err)
req.Len(dkgSet, 1)
- notarySet, err := cache.GetNotarySet(0, 0)
+ notarySet, err := cache.GetNotarySet(0)
req.NoError(err)
req.Len(notarySet, 1)
var (
diff --git a/core/test/state-change-request.go b/core/test/state-change-request.go
index 21e623b..5b19859 100644
--- a/core/test/state-change-request.go
+++ b/core/test/state-change-request.go
@@ -19,7 +19,6 @@ package test
import (
"fmt"
- "math"
"time"
"github.com/dexon-foundation/dexon-consensus/common"
@@ -42,13 +41,10 @@ const (
StateAddDKGMPKReady
StateAddDKGFinal
// Configuration related.
- StateChangeNumChains
StateChangeLambdaBA
StateChangeLambdaDKG
StateChangeRoundInterval
StateChangeMinBlockInterval
- StateChangeK
- StateChangePhiRatio
StateChangeNotarySetSize
StateChangeDKGSetSize
// Node set related.
@@ -69,8 +65,6 @@ func (t StateChangeType) String() string {
return "AddDKGMPKReady"
case StateAddDKGFinal:
return "AddDKGFinal"
- case StateChangeNumChains:
- return "ChangeNumChains"
case StateChangeLambdaBA:
return "ChangeLambdaBA"
case StateChangeLambdaDKG:
@@ -79,10 +73,6 @@ func (t StateChangeType) String() string {
return "ChangeRoundInterval"
case StateChangeMinBlockInterval:
return "ChangeMinBlockInterval"
- case StateChangeK:
- return "ChangeK"
- case StateChangePhiRatio:
- return "ChangePhiRatio"
case StateChangeNotarySetSize:
return "ChangeNotarySetSize"
case StateChangeDKGSetSize:
@@ -194,8 +184,6 @@ func (req *StateChangeRequest) String() (ret string) {
ret += fmt.Sprintf("%s", req.Payload.(*typesDKG.MPKReady))
case StateAddDKGFinal:
ret += fmt.Sprintf("%s", req.Payload.(*typesDKG.Finalize))
- case StateChangeNumChains:
- ret += fmt.Sprintf("%v", req.Payload.(uint32))
case StateChangeLambdaBA:
ret += fmt.Sprintf("%v", time.Duration(req.Payload.(uint64)))
case StateChangeLambdaDKG:
@@ -204,10 +192,6 @@ func (req *StateChangeRequest) String() (ret string) {
ret += fmt.Sprintf("%v", time.Duration(req.Payload.(uint64)))
case StateChangeMinBlockInterval:
ret += fmt.Sprintf("%v", time.Duration(req.Payload.(uint64)))
- case StateChangeK:
- ret += fmt.Sprintf("%v", req.Payload.(uint64))
- case StateChangePhiRatio:
- ret += fmt.Sprintf("%v", math.Float32frombits(req.Payload.(uint32)))
case StateChangeNotarySetSize:
ret += fmt.Sprintf("%v", req.Payload.(uint32))
case StateChangeDKGSetSize:
diff --git a/core/test/state-change-request_test.go b/core/test/state-change-request_test.go
index 658a9fb..eeba4c4 100644
--- a/core/test/state-change-request_test.go
+++ b/core/test/state-change-request_test.go
@@ -30,8 +30,8 @@ type StateChangeRequestTestSuite struct {
func (s *StateChangeRequestTestSuite) TestEqual() {
// Basically, only the cloned one would be equal.
- st00 := NewStateChangeRequest(StateChangeNumChains, uint32(4))
- st01 := NewStateChangeRequest(StateChangeNumChains, uint32(4))
+ st00 := NewStateChangeRequest(StateChangeNotarySetSize, uint32(4))
+ st01 := NewStateChangeRequest(StateChangeNotarySetSize, uint32(4))
s.Error(ErrStatePendingChangesNotEqual, st00.Equal(st01))
// Even with identical payload, they would be different.
mKey := typesDKG.NewMasterPublicKey()
@@ -42,7 +42,7 @@ func (s *StateChangeRequestTestSuite) TestEqual() {
func (s *StateChangeRequestTestSuite) TestClone() {
// The cloned one should be no error when compared with 'Equal' method.
- st00 := NewStateChangeRequest(StateChangeNumChains, uint32(7))
+ st00 := NewStateChangeRequest(StateChangeDKGSetSize, uint32(7))
s.NoError(st00.Equal(st00.Clone()))
st10 := NewStateChangeRequest(
StateAddDKGMasterPublicKey, typesDKG.NewMasterPublicKey())
diff --git a/core/test/state.go b/core/test/state.go
index a5a285b..27ff87a 100644
--- a/core/test/state.go
+++ b/core/test/state.go
@@ -20,7 +20,6 @@ package test
import (
"bytes"
"errors"
- "math"
"sort"
"sync"
"time"
@@ -86,14 +85,11 @@ type crsAdditionRequest struct {
// State emulates what the global state in governace contract on a fullnode.
type State struct {
// Configuration related.
- numChains uint32
lambdaBA time.Duration
lambdaDKG time.Duration
- k int
- phiRatio float32
notarySetSize uint32
dkgSetSize uint32
- roundInterval time.Duration
+ roundInterval uint64
minBlockInterval time.Duration
// Nodes
nodes map[types.NodeID]crypto.PublicKey
@@ -129,15 +125,12 @@ func NewState(
return &State{
local: local,
logger: logger,
- numChains: uint32(len(nodes)),
lambdaBA: lambda,
lambdaDKG: lambda * 10,
- roundInterval: lambda * 10000,
+ roundInterval: 1000,
minBlockInterval: 4 * lambda,
crs: []common.Hash{genesisCRS},
nodes: nodes,
- phiRatio: 0.667,
- k: 0,
notarySetSize: uint32(len(nodes)),
dkgSetSize: uint32(len(nodes)),
ownRequests: make(map[common.Hash]*StateChangeRequest),
@@ -173,11 +166,8 @@ func (s *State) Snapshot() (*types.Config, []crypto.PublicKey) {
nodes = append(nodes, key)
}
cfg := &types.Config{
- NumChains: s.numChains,
LambdaBA: s.lambdaBA,
LambdaDKG: s.lambdaDKG,
- K: s.k,
- PhiRatio: s.phiRatio,
NotarySetSize: s.notarySetSize,
DKGSetSize: s.dkgSetSize,
RoundInterval: s.roundInterval,
@@ -210,10 +200,6 @@ func (s *State) unpackPayload(
case StateAddDKGFinal:
v = &typesDKG.Finalize{}
err = rlp.DecodeBytes(raw.Payload, v)
- case StateChangeNumChains:
- var tmp uint32
- err = rlp.DecodeBytes(raw.Payload, &tmp)
- v = tmp
case StateChangeLambdaBA:
var tmp uint64
err = rlp.DecodeBytes(raw.Payload, &tmp)
@@ -230,14 +216,6 @@ func (s *State) unpackPayload(
var tmp uint64
err = rlp.DecodeBytes(raw.Payload, &tmp)
v = tmp
- case StateChangeK:
- var tmp uint64
- err = rlp.DecodeBytes(raw.Payload, &tmp)
- v = tmp
- case StateChangePhiRatio:
- var tmp uint32
- err = rlp.DecodeBytes(raw.Payload, &tmp)
- v = tmp
case StateChangeNotarySetSize:
var tmp uint32
err = rlp.DecodeBytes(raw.Payload, &tmp)
@@ -284,11 +262,8 @@ func (s *State) unpackRequests(
// Equal checks equality between State instance.
func (s *State) Equal(other *State) error {
// Check configuration part.
- configEqual := s.numChains == other.numChains &&
- s.lambdaBA == other.lambdaBA &&
+ configEqual := s.lambdaBA == other.lambdaBA &&
s.lambdaDKG == other.lambdaDKG &&
- s.k == other.k &&
- s.phiRatio == other.phiRatio &&
s.notarySetSize == other.notarySetSize &&
s.dkgSetSize == other.dkgSetSize &&
s.roundInterval == other.roundInterval &&
@@ -447,11 +422,8 @@ func (s *State) Equal(other *State) error {
func (s *State) Clone() (copied *State) {
// Clone configuration parts.
copied = &State{
- numChains: s.numChains,
lambdaBA: s.lambdaBA,
lambdaDKG: s.lambdaDKG,
- k: s.k,
- phiRatio: s.phiRatio,
notarySetSize: s.notarySetSize,
dkgSetSize: s.dkgSetSize,
roundInterval: s.roundInterval,
@@ -730,20 +702,14 @@ func (s *State) applyRequest(req *StateChangeRequest) error {
s.dkgFinals[final.Round] = make(map[types.NodeID]*typesDKG.Finalize)
}
s.dkgFinals[final.Round][final.ProposerID] = final
- case StateChangeNumChains:
- s.numChains = req.Payload.(uint32)
case StateChangeLambdaBA:
s.lambdaBA = time.Duration(req.Payload.(uint64))
case StateChangeLambdaDKG:
s.lambdaDKG = time.Duration(req.Payload.(uint64))
case StateChangeRoundInterval:
- s.roundInterval = time.Duration(req.Payload.(uint64))
+ s.roundInterval = req.Payload.(uint64)
case StateChangeMinBlockInterval:
s.minBlockInterval = time.Duration(req.Payload.(uint64))
- case StateChangeK:
- s.k = int(req.Payload.(uint64))
- case StateChangePhiRatio:
- s.phiRatio = math.Float32frombits(req.Payload.(uint32))
case StateChangeNotarySetSize:
s.notarySetSize = req.Payload.(uint32)
case StateChangeDKGSetSize:
@@ -773,13 +739,8 @@ func (s *State) RequestChange(
payload = payload.(crypto.PublicKey).Bytes()
case StateChangeLambdaBA,
StateChangeLambdaDKG,
- StateChangeRoundInterval,
StateChangeMinBlockInterval:
payload = uint64(payload.(time.Duration))
- case StateChangeK:
- payload = uint64(payload.(int))
- case StateChangePhiRatio:
- payload = math.Float32bits(payload.(float32))
// These cases for for type assertion, make sure callers pass expected types.
case StateAddCRS:
payload = payload.(*crsAdditionRequest)
diff --git a/core/test/state_test.go b/core/test/state_test.go
index 79a7a85..d92a8c1 100644
--- a/core/test/state_test.go
+++ b/core/test/state_test.go
@@ -135,26 +135,20 @@ func (s *StateTestSuite) makeDKGChanges(
}
func (s *StateTestSuite) makeConfigChanges(st *State) {
- st.RequestChange(StateChangeNumChains, uint32(7))
st.RequestChange(StateChangeLambdaBA, time.Nanosecond)
st.RequestChange(StateChangeLambdaDKG, time.Millisecond)
- st.RequestChange(StateChangeRoundInterval, time.Hour)
+ st.RequestChange(StateChangeRoundInterval, uint64(1001))
st.RequestChange(StateChangeMinBlockInterval, time.Second)
- st.RequestChange(StateChangeK, 1)
- st.RequestChange(StateChangePhiRatio, float32(0.5))
st.RequestChange(StateChangeNotarySetSize, uint32(5))
st.RequestChange(StateChangeDKGSetSize, uint32(6))
}
func (s *StateTestSuite) checkConfigChanges(config *types.Config) {
req := s.Require()
- req.Equal(config.NumChains, uint32(7))
req.Equal(config.LambdaBA, time.Nanosecond)
req.Equal(config.LambdaDKG, time.Millisecond)
- req.Equal(config.RoundInterval, time.Hour)
+ req.Equal(config.RoundInterval, uint64(1001))
req.Equal(config.MinBlockInterval, time.Second)
- req.Equal(config.K, 1)
- req.Equal(config.PhiRatio, float32(0.5))
req.Equal(config.NotarySetSize, uint32(5))
req.Equal(config.DKGSetSize, uint32(6))
}
@@ -210,7 +204,7 @@ func (s *StateTestSuite) TestEqual() {
// Switch to remote mode.
st.SwitchToRemoteMode()
// Make some change.
- req.NoError(st.RequestChange(StateChangeK, int(5)))
+ req.NoError(st.RequestChange(StateChangeNotarySetSize, uint32(100)))
st6 := st.Clone()
req.NoError(st.Equal(st6))
// Remove the pending change, should not be equal.
@@ -254,14 +248,11 @@ func (s *StateTestSuite) TestLocalMode() {
config1, nodes1 := st.Snapshot()
req.True(s.compareNodes(genesisNodes, nodes1))
// Check settings of config1 affected by genesisNodes and lambda.
- req.Equal(config1.NumChains, uint32(len(genesisNodes)))
req.Equal(config1.LambdaBA, lambda)
req.Equal(config1.LambdaDKG, lambda*10)
- req.Equal(config1.RoundInterval, lambda*10000)
+ req.Equal(config1.RoundInterval, uint64(1000))
req.Equal(config1.NotarySetSize, uint32(len(genesisNodes)))
req.Equal(config1.DKGSetSize, uint32(len(genesisNodes)))
- req.Equal(config1.K, 0)
- req.Equal(config1.PhiRatio, float32(0.667))
// Request some changes, every fields for config should be affected.
s.makeConfigChanges(st)
// Add new node.
diff --git a/core/ticker.go b/core/ticker.go
index ffd5ab4..636fb8c 100644
--- a/core/ticker.go
+++ b/core/ticker.go
@@ -18,6 +18,9 @@
package core
import (
+ "context"
+ "fmt"
+ "sync"
"time"
"github.com/dexon-foundation/dexon-consensus/core/utils"
@@ -36,32 +39,65 @@ const (
// defaultTicker is a wrapper to implement ticker interface based on
// time.Ticker.
type defaultTicker struct {
- ticker *time.Ticker
- duration time.Duration
+ ticker *time.Ticker
+ tickerChan chan time.Time
+ duration time.Duration
+ ctx context.Context
+ ctxCancel context.CancelFunc
+ waitGroup sync.WaitGroup
}
// newDefaultTicker constructs an defaultTicker instance by giving an interval.
func newDefaultTicker(lambda time.Duration) *defaultTicker {
- return &defaultTicker{
- ticker: time.NewTicker(lambda),
- duration: lambda,
- }
+ ticker := &defaultTicker{duration: lambda}
+ ticker.init()
+ return ticker
}
// Tick implements Tick method of ticker interface.
func (t *defaultTicker) Tick() <-chan time.Time {
- return t.ticker.C
+ return t.tickerChan
}
// Stop implements Stop method of ticker interface.
func (t *defaultTicker) Stop() {
t.ticker.Stop()
+ t.ctxCancel()
+ t.waitGroup.Wait()
+ t.ctx = nil
+ t.ctxCancel = nil
+ close(t.tickerChan)
+ t.tickerChan = nil
}
// Restart implements Stop method of ticker interface.
func (t *defaultTicker) Restart() {
- t.ticker.Stop()
+ t.Stop()
+ t.init()
+}
+
+func (t *defaultTicker) init() {
t.ticker = time.NewTicker(t.duration)
+ t.tickerChan = make(chan time.Time)
+ t.ctx, t.ctxCancel = context.WithCancel(context.Background())
+ t.waitGroup.Add(1)
+ go t.monitor()
+}
+
+func (t *defaultTicker) monitor() {
+ defer t.waitGroup.Done()
+loop:
+ for {
+ select {
+ case <-t.ctx.Done():
+ break loop
+ case v := <-t.ticker.C:
+ select {
+ case t.tickerChan <- v:
+ default:
+ }
+ }
+ }
}
// newTicker is a helper to setup a ticker by giving an Governance. If
@@ -82,8 +118,8 @@ func newTicker(gov Governance, round uint64, tickerType TickerType) (t Ticker) {
duration = utils.GetConfigWithPanic(gov, round, nil).LambdaBA
case TickerDKG:
duration = utils.GetConfigWithPanic(gov, round, nil).LambdaDKG
- case TickerCRS:
- duration = utils.GetConfigWithPanic(gov, round, nil).RoundInterval / 2
+ default:
+ panic(fmt.Errorf("unknown ticker type: %d", tickerType))
}
t = newDefaultTicker(duration)
}
diff --git a/core/types/block.go b/core/types/block.go
index a2b697c..8c3e510 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -208,7 +208,6 @@ func (b *Block) Clone() (bcopy *Block) {
bcopy.ParentHash = b.ParentHash
bcopy.Hash = b.Hash
bcopy.Position.Round = b.Position.Round
- bcopy.Position.ChainID = b.Position.ChainID
bcopy.Position.Height = b.Position.Height
bcopy.Signature = b.Signature.Clone()
bcopy.CRSSignature = b.CRSSignature.Clone()
diff --git a/core/types/block_test.go b/core/types/block_test.go
index d47096f..1dd83a9 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -67,9 +67,8 @@ func (s *BlockTestSuite) createRandomBlock() *Block {
ParentHash: common.NewRandomHash(),
Hash: common.NewRandomHash(),
Position: Position{
- Round: rand.Uint64(),
- ChainID: rand.Uint32(),
- Height: rand.Uint64(),
+ Round: rand.Uint64(),
+ Height: rand.Uint64(),
},
Acks: common.NewSortedHashes(common.Hashes{
common.NewRandomHash(),
diff --git a/core/types/config.go b/core/types/config.go
index c9d31f8..56f0175 100644
--- a/core/types/config.go
+++ b/core/types/config.go
@@ -19,40 +19,29 @@ package types
import (
"encoding/binary"
- "math"
"time"
)
// Config stands for Current Configuration Parameters.
type Config struct {
- // Network related.
- NumChains uint32
-
// Lambda related.
LambdaBA time.Duration
LambdaDKG time.Duration
- // Total ordering related.
- K int
- PhiRatio float32
-
// Set related.
NotarySetSize uint32
DKGSetSize uint32
// Time related.
- RoundInterval time.Duration
+ RoundInterval uint64
MinBlockInterval time.Duration
}
// Clone return a copied configuration.
func (c *Config) Clone() *Config {
return &Config{
- NumChains: c.NumChains,
LambdaBA: c.LambdaBA,
LambdaDKG: c.LambdaDKG,
- K: c.K,
- PhiRatio: c.PhiRatio,
NotarySetSize: c.NotarySetSize,
DKGSetSize: c.DKGSetSize,
RoundInterval: c.RoundInterval,
@@ -62,9 +51,6 @@ func (c *Config) Clone() *Config {
// Bytes returns []byte representation of Config.
func (c *Config) Bytes() []byte {
- binaryNumChains := make([]byte, 4)
- binary.LittleEndian.PutUint32(binaryNumChains, c.NumChains)
-
binaryLambdaBA := make([]byte, 8)
binary.LittleEndian.PutUint64(
binaryLambdaBA, uint64(c.LambdaBA.Nanoseconds()))
@@ -72,29 +58,20 @@ func (c *Config) Bytes() []byte {
binary.LittleEndian.PutUint64(
binaryLambdaDKG, uint64(c.LambdaDKG.Nanoseconds()))
- binaryK := make([]byte, 4)
- binary.LittleEndian.PutUint32(binaryK, uint32(c.K))
- binaryPhiRatio := make([]byte, 4)
- binary.LittleEndian.PutUint32(binaryPhiRatio, math.Float32bits(c.PhiRatio))
-
binaryNotarySetSize := make([]byte, 4)
binary.LittleEndian.PutUint32(binaryNotarySetSize, c.NotarySetSize)
binaryDKGSetSize := make([]byte, 4)
binary.LittleEndian.PutUint32(binaryDKGSetSize, c.DKGSetSize)
binaryRoundInterval := make([]byte, 8)
- binary.LittleEndian.PutUint64(binaryRoundInterval,
- uint64(c.RoundInterval.Nanoseconds()))
+ binary.LittleEndian.PutUint64(binaryRoundInterval, c.RoundInterval)
binaryMinBlockInterval := make([]byte, 8)
binary.LittleEndian.PutUint64(binaryMinBlockInterval,
uint64(c.MinBlockInterval.Nanoseconds()))
enc := make([]byte, 0, 40)
- enc = append(enc, binaryNumChains...)
enc = append(enc, binaryLambdaBA...)
enc = append(enc, binaryLambdaDKG...)
- enc = append(enc, binaryK...)
- enc = append(enc, binaryPhiRatio...)
enc = append(enc, binaryNotarySetSize...)
enc = append(enc, binaryDKGSetSize...)
enc = append(enc, binaryRoundInterval...)
diff --git a/core/types/config_test.go b/core/types/config_test.go
index bf6e422..6029479 100644
--- a/core/types/config_test.go
+++ b/core/types/config_test.go
@@ -30,13 +30,11 @@ type ConfigTestSuite struct {
func (s *ConfigTestSuite) TestClone() {
c := &Config{
- NumChains: 2,
LambdaBA: 1 * time.Millisecond,
LambdaDKG: 2 * time.Hour,
- K: 4,
NotarySetSize: 5,
DKGSetSize: 6,
- RoundInterval: 3 * time.Second,
+ RoundInterval: 1000,
MinBlockInterval: 7 * time.Nanosecond,
}
s.Require().Equal(c, c.Clone())
diff --git a/core/types/position.go b/core/types/position.go
index 902a55f..81d23c2 100644
--- a/core/types/position.go
+++ b/core/types/position.go
@@ -23,33 +23,22 @@ import (
// Position describes the position in the block lattice of an entity.
type Position struct {
- ChainID uint32 `json:"chain_id"`
- Round uint64 `json:"round"`
- Height uint64 `json:"height"`
+ Round uint64 `json:"round"`
+ Height uint64 `json:"height"`
}
func (pos Position) String() string {
- return fmt.Sprintf("Position{Round:%d Chain:%d Height:%d}",
- pos.Round, pos.ChainID, pos.Height)
+ return fmt.Sprintf("Position{Round:%d Height:%d}", pos.Round, pos.Height)
}
-// Equal checks if two positions are equal, it panics when their chainIDs
-// are different.
+// Equal checks if two positions are equal.
func (pos Position) Equal(other Position) bool {
- if pos.ChainID != other.ChainID {
- panic(fmt.Errorf("unexpected chainID %d, should be %d",
- other.ChainID, pos.ChainID))
- }
return pos.Round == other.Round && pos.Height == other.Height
}
// Newer checks if one block is newer than another one on the same chain.
// If two blocks on different chain compared by this function, it would panic.
func (pos Position) Newer(other Position) bool {
- if pos.ChainID != other.ChainID {
- panic(fmt.Errorf("unexpected chainID %d, should be %d",
- other.ChainID, pos.ChainID))
- }
return pos.Round > other.Round ||
(pos.Round == other.Round && pos.Height > other.Height)
}
@@ -57,10 +46,6 @@ func (pos Position) Newer(other Position) bool {
// Older checks if one block is older than another one on the same chain.
// If two blocks on different chain compared by this function, it would panic.
func (pos Position) Older(other Position) bool {
- if pos.ChainID != other.ChainID {
- panic(fmt.Errorf("unexpected chainID %d, should be %d",
- other.ChainID, pos.ChainID))
- }
return pos.Round < other.Round ||
(pos.Round == other.Round && pos.Height < other.Height)
}
diff --git a/core/types/position_test.go b/core/types/position_test.go
index 213c15f..d2f4165 100644
--- a/core/types/position_test.go
+++ b/core/types/position_test.go
@@ -30,63 +30,47 @@ type PositionTestSuite struct {
func (s *PositionTestSuite) TestNewer() {
pos := Position{
- Round: 1,
- ChainID: 1,
- Height: 1,
+ Round: 1,
+ Height: 1,
}
- s.Panics(func() {
- pos.Newer(Position{ChainID: 2})
- })
s.False(pos.Newer(Position{
- Round: 2,
- ChainID: 1,
- Height: 0,
+ Round: 2,
+ Height: 0,
}))
s.False(pos.Newer(Position{
- Round: 1,
- ChainID: 1,
- Height: 2,
+ Round: 1,
+ Height: 2,
}))
s.True(pos.Newer(Position{
- Round: 0,
- ChainID: 1,
- Height: 100,
+ Round: 0,
+ Height: 100,
}))
s.True(pos.Newer(Position{
- Round: 1,
- ChainID: 1,
- Height: 0,
+ Round: 1,
+ Height: 0,
}))
}
func (s *PositionTestSuite) TestOlder() {
pos := Position{
- Round: 1,
- ChainID: 1,
- Height: 1,
+ Round: 1,
+ Height: 1,
}
- s.Panics(func() {
- pos.Older(Position{ChainID: 2})
- })
s.False(pos.Older(Position{
- Round: 0,
- ChainID: 1,
- Height: 0,
+ Round: 0,
+ Height: 0,
}))
s.False(pos.Older(Position{
- Round: 1,
- ChainID: 1,
- Height: 0,
+ Round: 1,
+ Height: 0,
}))
s.True(pos.Older(Position{
- Round: 2,
- ChainID: 1,
- Height: 0,
+ Round: 2,
+ Height: 0,
}))
s.True(pos.Older(Position{
- Round: 1,
- ChainID: 1,
- Height: 100,
+ Round: 1,
+ Height: 100,
}))
}
@@ -116,9 +100,6 @@ func (s *PositionTestSuite) TestSearchInAsendingOrder() {
func (s *PositionTestSuite) TestEqual() {
pos := Position{}
- s.Panics(func() {
- pos.Equal(Position{ChainID: 1})
- })
s.True(pos.Equal(Position{}))
s.False(pos.Equal(Position{Round: 1}))
s.False(pos.Equal(Position{Height: 1}))
diff --git a/core/utils.go b/core/utils.go
index 14780e7..46aa77a 100644
--- a/core/utils.go
+++ b/core/utils.go
@@ -21,7 +21,6 @@ import (
"context"
"errors"
"fmt"
- "math/rand"
"os"
"sort"
"time"
@@ -130,11 +129,6 @@ func removeFromSortedUint32Slice(xs []uint32, x uint32) []uint32 {
return append(xs[:indexToRemove], xs[indexToRemove+1:]...)
}
-// pickBiasedTime returns a biased time based on a given range.
-func pickBiasedTime(base time.Time, biasedRange time.Duration) time.Time {
- return base.Add(time.Duration(rand.Intn(int(biasedRange))))
-}
-
// HashConfigurationBlock returns the hash value of configuration block.
func HashConfigurationBlock(
notarySet map[types.NodeID]struct{},
@@ -165,8 +159,7 @@ func HashConfigurationBlock(
// instance.
func VerifyAgreementResult(
res *types.AgreementResult, cache *utils.NodeSetCache) error {
- notarySet, err := cache.GetNotarySet(
- res.Position.Round, res.Position.ChainID)
+ notarySet, err := cache.GetNotarySet(res.Position.Round)
if err != nil {
return err
}
diff --git a/core/utils/crypto.go b/core/utils/crypto.go
index 43bbde1..fe67a95 100644
--- a/core/utils/crypto.go
+++ b/core/utils/crypto.go
@@ -140,9 +140,6 @@ func VerifyCRSSignature(block *types.Block, crs common.Hash) (
}
func hashPosition(position types.Position) common.Hash {
- binaryChainID := make([]byte, 4)
- binary.LittleEndian.PutUint32(binaryChainID, position.ChainID)
-
binaryRound := make([]byte, 8)
binary.LittleEndian.PutUint64(binaryRound, position.Round)
@@ -150,7 +147,6 @@ func hashPosition(position types.Position) common.Hash {
binary.LittleEndian.PutUint64(binaryHeight, position.Height)
return crypto.Keccak256Hash(
- binaryChainID,
binaryRound,
binaryHeight,
)
diff --git a/core/utils/nodeset-cache.go b/core/utils/nodeset-cache.go
index 8354128..3ff5196 100644
--- a/core/utils/nodeset-cache.go
+++ b/core/utils/nodeset-cache.go
@@ -19,7 +19,6 @@ package utils
import (
"errors"
- "fmt"
"sync"
"github.com/dexon-foundation/dexon-consensus/common"
@@ -34,8 +33,6 @@ var (
ErrCRSNotReady = errors.New("crs is not ready")
// ErrConfigurationNotReady means we go nil configuration.
ErrConfigurationNotReady = errors.New("configuration is not ready")
- // ErrInvalidChainID means the chain ID is unexpected.
- ErrInvalidChainID = errors.New("invalid chain id")
)
type sets struct {
@@ -125,12 +122,8 @@ func (cache *NodeSetCache) GetNodeSet(round uint64) (*types.NodeSet, error) {
}
// GetNotarySet returns of notary set of this round.
-// TODO(mission): remove chainID parameter.
func (cache *NodeSetCache) GetNotarySet(
- round uint64, chainID uint32) (map[types.NodeID]struct{}, error) {
- if chainID != 0 {
- panic(fmt.Errorf("non-zero chainID found: %d", chainID))
- }
+ round uint64) (map[types.NodeID]struct{}, error) {
IDs, err := cache.getOrUpdate(round)
if err != nil {
return nil, err
@@ -196,12 +189,9 @@ func (cache *NodeSetCache) getOrUpdate(round uint64) (nIDs *sets, err error) {
//
// This cache would maintain 10 rounds before the updated round and purge
// rounds not in this range.
-func (cache *NodeSetCache) update(
- round uint64) (nIDs *sets, err error) {
-
+func (cache *NodeSetCache) update(round uint64) (nIDs *sets, err error) {
cache.lock.Lock()
defer cache.lock.Unlock()
-
// Get information for the requested round.
keySet := cache.nsIntf.NodeSet(round)
if keySet == nil {
@@ -232,14 +222,13 @@ func (cache *NodeSetCache) update(
err = ErrConfigurationNotReady
return
}
- nodesPerChain := cfg.RoundInterval / cfg.MinBlockInterval
nIDs = &sets{
crs: crs,
nodeSet: nodeSet,
notarySet: make(map[types.NodeID]struct{}),
dkgSet: nodeSet.GetSubSet(
int(cfg.DKGSetSize), types.NewDKGSetTarget(crs)),
- leaderNode: make(map[uint64]types.NodeID, nodesPerChain),
+ leaderNode: make(map[uint64]types.NodeID, cfg.RoundInterval),
}
nIDs.notarySet = nodeSet.GetSubSet(
int(cfg.NotarySetSize), types.NewNotarySetTarget(crs))
@@ -261,12 +250,9 @@ func (cache *NodeSetCache) update(
return
}
-func (cache *NodeSetCache) get(
- round uint64) (nIDs *sets, exists bool) {
-
+func (cache *NodeSetCache) get(round uint64) (nIDs *sets, exists bool) {
cache.lock.RLock()
defer cache.lock.RUnlock()
-
nIDs, exists = cache.rounds[round]
return
}
diff --git a/core/utils/nodeset-cache_test.go b/core/utils/nodeset-cache_test.go
index eb6008d..52036b5 100644
--- a/core/utils/nodeset-cache_test.go
+++ b/core/utils/nodeset-cache_test.go
@@ -38,9 +38,8 @@ func (g *nsIntf) Configuration(round uint64) (cfg *types.Config) {
return &types.Config{
NotarySetSize: 7,
DKGSetSize: 7,
- NumChains: 4,
+ RoundInterval: 60,
LambdaBA: 250 * time.Millisecond,
- RoundInterval: 60 * time.Second,
MinBlockInterval: 1 * time.Second,
}
}
@@ -89,16 +88,15 @@ func (s *NodeSetCacheTestSuite) TestBasicUsage() {
nodeSet0, err := cache.GetNodeSet(0)
req.NoError(err)
chk(cache, 0, nodeSet0.IDs)
- notarySet, err := cache.GetNotarySet(0, 0)
+ notarySet, err := cache.GetNotarySet(0)
req.NoError(err)
chk(cache, 0, notarySet)
dkgSet, err := cache.GetDKGSet(0)
req.NoError(err)
chk(cache, 0, dkgSet)
leaderNode, err := cache.GetLeaderNode(types.Position{
- Round: uint64(0),
- ChainID: uint32(0),
- Height: uint64(10),
+ Round: uint64(0),
+ Height: uint64(10),
})
req.NoError(err)
chk(cache, 0, map[types.NodeID]struct{}{
diff --git a/core/utils/signer_test.go b/core/utils/signer_test.go
index 8e34b98..3905352 100644
--- a/core/utils/signer_test.go
+++ b/core/utils/signer_test.go
@@ -42,8 +42,8 @@ func (s *SignerTestSuite) TestBlock() {
b := &types.Block{
ParentHash: common.NewRandomHash(),
Position: types.Position{
- ChainID: 2,
- Height: 3,
+ Round: 2,
+ Height: 3,
},
Timestamp: time.Now().UTC(),
}
@@ -55,8 +55,8 @@ func (s *SignerTestSuite) TestVote() {
k := s.setupSigner()
v := types.NewVote(types.VoteCom, common.NewRandomHash(), 123)
v.Position = types.Position{
- ChainID: 4,
- Height: 6,
+ Round: 4,
+ Height: 6,
}
v.ProposerID = types.NodeID{Hash: common.NewRandomHash()}
s.NoError(k.SignVote(v))
@@ -70,8 +70,8 @@ func (s *SignerTestSuite) TestCRS() {
b := &types.Block{
ParentHash: common.NewRandomHash(),
Position: types.Position{
- ChainID: 8,
- Height: 9,
+ Round: 8,
+ Height: 9,
},
Timestamp: time.Now().UTC(),
}
diff --git a/core/utils_test.go b/core/utils_test.go
index fa5d260..c53a38f 100644
--- a/core/utils_test.go
+++ b/core/utils_test.go
@@ -58,9 +58,8 @@ func (s *UtilsTestSuite) TestVerifyAgreementResult() {
signers = append(signers, utils.NewSigner(prvKey))
}
pos := types.Position{
- Round: 0,
- ChainID: 0,
- Height: 20,
+ Round: 0,
+ Height: 20,
}
baResult := &types.AgreementResult{
BlockHash: hash,