diff options
Diffstat (limited to 'core/test')
-rw-r--r-- | core/test/app.go | 211 | ||||
-rw-r--r-- | core/test/app_test.go | 238 | ||||
-rw-r--r-- | core/test/block-revealer.go | 33 | ||||
-rw-r--r-- | core/test/block-revealer_test.go | 35 | ||||
-rw-r--r-- | core/test/governance.go | 9 | ||||
-rw-r--r-- | core/test/network.go | 17 | ||||
-rw-r--r-- | core/test/network_test.go | 13 |
7 files changed, 268 insertions, 288 deletions
diff --git a/core/test/app.go b/core/test/app.go index 20fe80f..769683e 100644 --- a/core/test/app.go +++ b/core/test/app.go @@ -18,6 +18,7 @@ package test import ( + "bytes" "fmt" "sync" "time" @@ -34,20 +35,18 @@ var ( // ErrMismatchBlockHashSequence means the delivering sequence between two App // instances are different. ErrMismatchBlockHashSequence = fmt.Errorf("mismatch block hash sequence") - // ErrMismatchConsensusTime means the consensus timestamp between two blocks - // with the same hash from two App instances are different. - ErrMismatchConsensusTime = fmt.Errorf("mismatch consensus time") + // ErrMismatchRandomness means the randomness between two blocks with the + // same hash from two App instances are different. + ErrMismatchRandomness = fmt.Errorf("mismatch randomness") // ErrApplicationIntegrityFailed means the internal datum in a App instance // is not integrated. ErrApplicationIntegrityFailed = fmt.Errorf("application integrity failed") - // ErrConsensusTimestampOutOfOrder means the later delivered block has - // consensus timestamp older than previous block. - ErrConsensusTimestampOutOfOrder = fmt.Errorf( - "consensus timestamp out of order") - // ErrConsensusHeightOutOfOrder means the later delivered block has - // consensus height not equal to height of previous block plus one. - ErrConsensusHeightOutOfOrder = fmt.Errorf( - "consensus height out of order") + // ErrTimestampOutOfOrder means the later delivered block has timestamp + // older than previous block. + ErrTimestampOutOfOrder = fmt.Errorf("timestamp out of order") + // ErrHeightOutOfOrder means the later delivered block has height not equal + // to height of previous block plus one. + ErrHeightOutOfOrder = fmt.Errorf("height out of order") // ErrDeliveredBlockNotConfirmed means some block delivered (confirmed) but // not confirmed. ErrDeliveredBlockNotConfirmed = fmt.Errorf("delivered block not confirmed") @@ -65,31 +64,36 @@ var ( // ErrParentBlockNotDelivered raised when the parent block is not seen by // this app. ErrParentBlockNotDelivered = fmt.Errorf("parent block not delivered") + // ErrMismatchDeliverPosition raised when the block hash and position are + // mismatched when calling BlockDelivered. + ErrMismatchDeliverPosition = fmt.Errorf("mismatch deliver position") + // ErrEmptyRandomness raised when the block contains empty randomness. + ErrEmptyRandomness = fmt.Errorf("empty randomness") + // ErrInvalidHeight refers to invalid value for block height. + ErrInvalidHeight = fmt.Errorf("invalid height") ) // AppDeliveredRecord caches information when this application received // a block delivered notification. type AppDeliveredRecord struct { - Result types.FinalizationResult - When time.Time - Pos types.Position + Rand []byte + When time.Time + Pos types.Position } // App implements Application interface for testing purpose. type App struct { - Confirmed map[common.Hash]*types.Block - LastConfirmedHeight uint64 - confirmedLock sync.RWMutex - Delivered map[common.Hash]*AppDeliveredRecord - DeliverSequence common.Hashes - deliveredLock sync.RWMutex - state *State - gov *Governance - rEvt *utils.RoundEvent - hEvt *common.Event - lastPendingHeightLock sync.RWMutex - LastPendingHeight uint64 - roundToNotify uint64 + Confirmed map[common.Hash]*types.Block + LastConfirmedHeight uint64 + confirmedLock sync.RWMutex + Delivered map[common.Hash]*AppDeliveredRecord + DeliverSequence common.Hashes + deliveredLock sync.RWMutex + state *State + gov *Governance + rEvt *utils.RoundEvent + hEvt *common.Event + roundToNotify uint64 } // NewApp constructs a TestApp instance. @@ -129,30 +133,20 @@ func (app *App) PreparePayload(position types.Position) ([]byte, error) { // PrepareWitness implements Application interface. func (app *App) PrepareWitness(height uint64) (types.Witness, error) { - pendingHeight := app.getLastPendingWitnessHeight() - if pendingHeight < height { - return types.Witness{}, ErrLowerPendingHeight - } - if pendingHeight == 0 { + // Although we only perform reading operations here, to make sure what we + // prepared unique under concurrent access to this method, writer lock is + // used. + app.deliveredLock.Lock() + defer app.deliveredLock.Unlock() + hash, lastRec := app.LastDeliveredRecordNoLock() + if lastRec == nil { return types.Witness{}, nil } - hash := func() common.Hash { - app.deliveredLock.RLock() - defer app.deliveredLock.RUnlock() - // Our witness height starts from 1. - h := app.DeliverSequence[pendingHeight-1] - // Double confirm if the delivered record matches the pending height. - if app.Delivered[h].Result.Height != pendingHeight { - app.confirmedLock.RLock() - defer app.confirmedLock.RUnlock() - panic(fmt.Errorf("unmatched finalization record: %s, %v, %v", - app.Confirmed[h], pendingHeight, - app.Delivered[h].Result.Height)) - } - return h - }() + if lastRec.Pos.Height < height { + return types.Witness{}, ErrLowerPendingHeight + } return types.Witness{ - Height: pendingHeight, + Height: lastRec.Pos.Height, Data: hash.Bytes(), }, nil } @@ -160,31 +154,30 @@ func (app *App) PrepareWitness(height uint64) (types.Witness, error) { // VerifyBlock implements Application interface. func (app *App) VerifyBlock(block *types.Block) types.BlockVerifyStatus { // Make sure we can handle the witness carried by this block. - pendingHeight := app.getLastPendingWitnessHeight() - if pendingHeight < block.Witness.Height { + app.deliveredLock.RLock() + defer app.deliveredLock.RUnlock() + _, rec := app.LastDeliveredRecordNoLock() + if rec != nil && rec.Pos.Height < block.Witness.Height { return types.VerifyRetryLater } // Confirm if the consensus height matches corresponding block hash. var h common.Hash copy(h[:], block.Witness.Data) - app.deliveredLock.RLock() - defer app.deliveredLock.RUnlock() - // This is the difference between test.App and fullnode, fullnode has the - // genesis block at height=0, we don't. Thus our reasonable witness starts - // from 1. - if block.Witness.Height > 0 { - if block.Witness.Height != app.Delivered[h].Result.Height { + app.confirmedLock.RLock() + defer app.confirmedLock.RUnlock() + if block.Witness.Height >= types.GenesisHeight { + // Make sure the hash and height are matched. + confirmed, exist := app.Confirmed[h] + if !exist || block.Witness.Height != confirmed.Position.Height { return types.VerifyInvalidBlock } } - if block.Position.Height != 0 { + if block.Position.Height != types.GenesisHeight { // This check is copied from fullnode, below is quoted from coresponding // comment: // // Check if target block is the next height to be verified, we can only // verify the next block in a given chain. - app.confirmedLock.RLock() - defer app.confirmedLock.RUnlock() if app.LastConfirmedHeight+1 != block.Position.Height { return types.VerifyRetryLater } @@ -197,10 +190,8 @@ func (app *App) BlockConfirmed(b types.Block) { app.confirmedLock.Lock() defer app.confirmedLock.Unlock() app.Confirmed[b.Hash] = &b - if b.Position.Height != 0 { - if app.LastConfirmedHeight+1 != b.Position.Height { - panic(ErrConfirmedHeightNotIncreasing) - } + if app.LastConfirmedHeight+1 != b.Position.Height { + panic(ErrConfirmedHeightNotIncreasing) } app.LastConfirmedHeight = b.Position.Height } @@ -211,34 +202,32 @@ func (app *App) ClearUndeliveredBlocks() { defer app.deliveredLock.RUnlock() app.confirmedLock.Lock() defer app.confirmedLock.Unlock() - app.LastConfirmedHeight = uint64(len(app.DeliverSequence) - 1) + app.LastConfirmedHeight = uint64(len(app.DeliverSequence)) } // BlockDelivered implements Application interface. func (app *App) BlockDelivered(blockHash common.Hash, pos types.Position, - result types.FinalizationResult) { + rand []byte) { func() { app.deliveredLock.Lock() defer app.deliveredLock.Unlock() app.Delivered[blockHash] = &AppDeliveredRecord{ - Result: result, - When: time.Now().UTC(), - Pos: pos, + Rand: common.CopyBytes(rand), + When: time.Now().UTC(), + Pos: pos, } - app.DeliverSequence = append(app.DeliverSequence, blockHash) - // Make sure parent block also delivered. - if !result.ParentHash.Equal(common.Hash{}) { - d, exists := app.Delivered[result.ParentHash] + if len(app.DeliverSequence) > 0 { + // Make sure parent block also delivered. + lastHash := app.DeliverSequence[len(app.DeliverSequence)-1] + d, exists := app.Delivered[lastHash] if !exists { panic(ErrParentBlockNotDelivered) } - if d.Result.Height+1 != result.Height { - panic(ErrConsensusHeightOutOfOrder) + if d.Pos.Height+1 != pos.Height { + panic(ErrHeightOutOfOrder) } } - app.lastPendingHeightLock.Lock() - defer app.lastPendingHeightLock.Unlock() - app.LastPendingHeight = result.Height + app.DeliverSequence = append(app.DeliverSequence, blockHash) }() // Apply packed state change requests in payload. func() { @@ -247,7 +236,13 @@ func (app *App) BlockDelivered(blockHash common.Hash, pos types.Position, } app.confirmedLock.RLock() defer app.confirmedLock.RUnlock() - b := app.Confirmed[blockHash] + b, exists := app.Confirmed[blockHash] + if !exists { + panic(ErrDeliveredBlockNotConfirmed) + } + if !b.Position.Equal(pos) { + panic(ErrMismatchDeliverPosition) + } if err := app.state.Apply(b.Payload); err != nil { if err != ErrDuplicatedChange { panic(err) @@ -260,7 +255,7 @@ func (app *App) BlockDelivered(blockHash common.Hash, pos types.Position, } } }() - app.hEvt.NotifyHeight(result.Height) + app.hEvt.NotifyHeight(pos.Height) } // GetLatestDeliveredPosition would return the latest position of delivered @@ -298,9 +293,9 @@ func (app *App) Compare(other *App) (err error) { err = ErrMismatchBlockHashSequence return } - if app.Delivered[h].Result.Timestamp != - other.Delivered[h].Result.Timestamp { - err = ErrMismatchConsensusTime + if bytes.Compare(app.Delivered[h].Rand, + other.Delivered[h].Rand) != 0 { + err = ErrMismatchRandomness return } } @@ -311,7 +306,6 @@ func (app *App) Compare(other *App) (err error) { // Verify checks the integrity of date received by this App instance. func (app *App) Verify() error { - // TODO(mission): verify blocks' position when delivered. app.confirmedLock.RLock() defer app.confirmedLock.RUnlock() app.deliveredLock.RLock() @@ -326,24 +320,37 @@ func (app *App) Verify() error { expectHeight := uint64(1) prevTime := time.Time{} for _, h := range app.DeliverSequence { - _, exists := app.Confirmed[h] - if !exists { + _, exist := app.Confirmed[h] + if !exist { return ErrDeliveredBlockNotConfirmed } - rec, exists := app.Delivered[h] - if !exists { + _, exist = app.Delivered[h] + if !exist { + return ErrApplicationIntegrityFailed + } + b, exist := app.Confirmed[h] + if !exist { return ErrApplicationIntegrityFailed } // Make sure the consensus time is incremental. - ok := prevTime.Before(rec.Result.Timestamp) || - prevTime.Equal(rec.Result.Timestamp) - if !ok { - return ErrConsensusTimestampOutOfOrder + if prevTime.After(b.Timestamp) { + return ErrTimestampOutOfOrder } - prevTime = rec.Result.Timestamp + prevTime = b.Timestamp // Make sure the consensus height is incremental. - if expectHeight != rec.Result.Height { - return ErrConsensusHeightOutOfOrder + rec, exist := app.Delivered[h] + if !exist { + return ErrApplicationIntegrityFailed + } + if len(rec.Rand) == 0 { + return ErrEmptyRandomness + } + // Make sure height is valid. + if b.Position.Height < types.GenesisHeight { + return ErrInvalidHeight + } + if expectHeight != rec.Pos.Height { + return ErrHeightOutOfOrder } expectHeight++ } @@ -362,14 +369,16 @@ func (app *App) WithLock(function func(*App)) { defer app.confirmedLock.RUnlock() app.deliveredLock.RLock() defer app.deliveredLock.RUnlock() - app.lastPendingHeightLock.RLock() - defer app.lastPendingHeightLock.RUnlock() function(app) } -func (app *App) getLastPendingWitnessHeight() uint64 { - app.lastPendingHeightLock.RLock() - defer app.lastPendingHeightLock.RUnlock() - return app.LastPendingHeight +// LastDeliveredRecordNoLock returns the latest AppDeliveredRecord under lock. +func (app *App) LastDeliveredRecordNoLock() (common.Hash, *AppDeliveredRecord) { + var hash common.Hash + if len(app.DeliverSequence) == 0 { + return hash, nil + } + hash = app.DeliverSequence[len(app.DeliverSequence)-1] + return hash, app.Delivered[hash] } diff --git a/core/test/app_test.go b/core/test/app_test.go index 0a68f5e..574604c 100644 --- a/core/test/app_test.go +++ b/core/test/app_test.go @@ -104,112 +104,97 @@ func (s *AppTestSuite) proposeFinalize( } } -func (s *AppTestSuite) deliverBlockWithTimeFromSequenceLength( - app *App, hash common.Hash) { - - s.deliverBlock(app, hash, time.Time{}.Add( - time.Duration(len(app.DeliverSequence))*time.Second), - uint64(len(app.DeliverSequence)+1)) -} - -func (s *AppTestSuite) deliverBlock( - app *App, hash common.Hash, timestamp time.Time, height uint64) { - - app.BlockDelivered(hash, types.Position{}, types.FinalizationResult{ - Timestamp: timestamp, - Height: height, - }) -} - func (s *AppTestSuite) TestCompare() { var ( - now = time.Now().UTC() - b0 = types.Block{Hash: common.Hash{}} - b1 = types.Block{ - Hash: common.NewRandomHash(), - Position: types.Position{Height: 1}, + b0 = types.Block{ + Hash: common.Hash{}, + Position: types.Position{Height: types.GenesisHeight}, + Randomness: []byte("b0")} + b1 = types.Block{ + Hash: common.NewRandomHash(), + Position: types.Position{Height: types.GenesisHeight + 1}, + Randomness: []byte("b1"), } ) // Prepare an OK App instance. app1 := NewApp(0, nil, nil) app1.BlockConfirmed(b0) app1.BlockConfirmed(b1) - app1.BlockDelivered(b0.Hash, b0.Position, types.FinalizationResult{ - Height: 1, - Timestamp: now, - }) - app1.BlockDelivered(b1.Hash, b1.Position, types.FinalizationResult{ - Height: 2, - Timestamp: now.Add(1 * time.Second), - }) + app1.BlockDelivered(b0.Hash, b0.Position, b0.Randomness) + app1.BlockDelivered(b1.Hash, b1.Position, b1.Randomness) app2 := NewApp(0, nil, nil) - s.Require().Equal(ErrEmptyDeliverSequence.Error(), + s.Require().EqualError(ErrEmptyDeliverSequence, app1.Compare(app2).Error()) app2.BlockConfirmed(b0) - app2.BlockDelivered(b0.Hash, b0.Position, types.FinalizationResult{ - Height: 1, - Timestamp: now, - }) + app2.BlockDelivered(b0.Hash, b0.Position, b0.Randomness) b1Bad := types.Block{ - Hash: common.NewRandomHash(), - Position: types.Position{Height: 1}, + Hash: common.NewRandomHash(), + Position: types.Position{Height: types.GenesisHeight + 1}, + Randomness: []byte("b1Bad"), } app2.BlockConfirmed(b1Bad) - app2.BlockDelivered(b1Bad.Hash, b1Bad.Position, types.FinalizationResult{ - Height: 1, - Timestamp: now, - }) - s.Require().Equal(ErrMismatchBlockHashSequence.Error(), + app2.BlockDelivered(b1Bad.Hash, b1Bad.Position, b1Bad.Randomness) + s.Require().EqualError(ErrMismatchBlockHashSequence, app1.Compare(app2).Error()) app2 = NewApp(0, nil, nil) app2.BlockConfirmed(b0) - app2.BlockDelivered(b0.Hash, b0.Position, types.FinalizationResult{ - Height: 1, - Timestamp: now.Add(1 * time.Second), - }) - s.Require().Equal(ErrMismatchConsensusTime.Error(), - app1.Compare(app2).Error()) + app2.BlockDelivered(b0.Hash, b0.Position, []byte("b0-another")) + s.Require().EqualError(ErrMismatchRandomness, app1.Compare(app2).Error()) } func (s *AppTestSuite) TestVerify() { var ( now = time.Now().UTC() - b0 = types.Block{Hash: common.Hash{}} - b1 = types.Block{ - Hash: common.NewRandomHash(), - Position: types.Position{Height: 1}, + b0 = types.Block{ + Hash: common.Hash{}, + Position: types.Position{Height: types.GenesisHeight}, + Randomness: []byte("b0"), + Timestamp: now, + } + b1 = types.Block{ + Hash: common.NewRandomHash(), + Position: types.Position{Height: types.GenesisHeight + 1}, + Randomness: []byte("b1"), + Timestamp: now.Add(1 * time.Second), } ) + // ErrDeliveredBlockNotConfirmed app := NewApp(0, nil, nil) s.Require().Equal(ErrEmptyDeliverSequence.Error(), app.Verify().Error()) - app.BlockDelivered(b0.Hash, b0.Position, types.FinalizationResult{}) - app.BlockDelivered(b1.Hash, b1.Position, types.FinalizationResult{Height: 1}) - s.Require().Equal( - ErrDeliveredBlockNotConfirmed.Error(), app.Verify().Error()) + app.BlockDelivered(b0.Hash, b0.Position, b0.Randomness) + app.BlockDelivered(b1.Hash, b1.Position, b1.Randomness) + s.Require().EqualError(ErrDeliveredBlockNotConfirmed, app.Verify().Error()) + // ErrTimestampOutOfOrder. + app = NewApp(0, nil, nil) + now = time.Now().UTC() + b0Bad := *(b0.Clone()) + b0Bad.Timestamp = now + b1Bad := *(b1.Clone()) + b1Bad.Timestamp = now.Add(-1 * time.Second) + app.BlockConfirmed(b0Bad) + app.BlockDelivered(b0Bad.Hash, b0Bad.Position, b0Bad.Randomness) + app.BlockConfirmed(b1Bad) + app.BlockDelivered(b1Bad.Hash, b1Bad.Position, b1Bad.Randomness) + s.Require().EqualError(ErrTimestampOutOfOrder, app.Verify().Error()) + // ErrInvalidHeight. + app = NewApp(0, nil, nil) + b0Bad = *(b0.Clone()) + b0Bad.Position.Height = 0 + s.Require().Panics(func() { app.BlockConfirmed(b0Bad) }) + b0Bad.Position.Height = 2 + s.Require().Panics(func() { app.BlockConfirmed(b0Bad) }) + // ErrEmptyRandomness app = NewApp(0, nil, nil) app.BlockConfirmed(b0) - app.BlockDelivered(b0.Hash, b0.Position, types.FinalizationResult{ - Height: 1, - Timestamp: now, - }) - app.BlockConfirmed(b1) - app.BlockDelivered(b1.Hash, b1.Position, types.FinalizationResult{ - Height: 2, - Timestamp: now.Add(-1 * time.Second), - }) - s.Require().Equal(ErrConsensusTimestampOutOfOrder.Error(), - app.Verify().Error()) + app.BlockDelivered(b0.Hash, b0.Position, []byte{}) + s.Require().EqualError(ErrEmptyRandomness, app.Verify().Error()) + // OK. app = NewApp(0, nil, nil) app.BlockConfirmed(b0) app.BlockConfirmed(b1) - app.BlockDelivered(b0.Hash, b0.Position, types.FinalizationResult{ - Height: 1, - Timestamp: now, - }) - app.BlockDelivered(b1.Hash, b1.Position, types.FinalizationResult{ - Height: 1, - Timestamp: now.Add(1 * time.Second), - }) + app.BlockDelivered(b0.Hash, b0.Position, b0.Randomness) + app.BlockDelivered(b1.Hash, b1.Position, b1.Randomness) + s.Require().NoError(app.Verify()) } func (s *AppTestSuite) TestWitness() { @@ -217,36 +202,30 @@ func (s *AppTestSuite) TestWitness() { app := NewApp(0, nil, nil) deliver := func(b *types.Block) { app.BlockConfirmed(*b) - app.BlockDelivered(b.Hash, b.Position, b.Finalization) + app.BlockDelivered(b.Hash, b.Position, b.Randomness) } b00 := &types.Block{ - Hash: common.NewRandomHash(), - Finalization: types.FinalizationResult{ - Height: 1, - Timestamp: time.Now().UTC(), - }} + Hash: common.NewRandomHash(), + Position: types.Position{Height: 1}, + Timestamp: time.Now().UTC(), + Randomness: common.GenerateRandomBytes(), + } b01 := &types.Block{ - Hash: common.NewRandomHash(), - Position: types.Position{Height: 1}, - Finalization: types.FinalizationResult{ - ParentHash: b00.Hash, - Height: 2, - Timestamp: time.Now().UTC(), - }, + Hash: common.NewRandomHash(), + Position: types.Position{Height: 2}, + Timestamp: time.Now().UTC(), + Randomness: common.GenerateRandomBytes(), Witness: types.Witness{ - Height: 1, + Height: b00.Position.Height, Data: b00.Hash.Bytes(), }} b02 := &types.Block{ - Hash: common.NewRandomHash(), - Position: types.Position{Height: 2}, - Finalization: types.FinalizationResult{ - ParentHash: b01.Hash, - Height: 3, - Timestamp: time.Now().UTC(), - }, + Hash: common.NewRandomHash(), + Position: types.Position{Height: 3}, + Timestamp: time.Now().UTC(), + Randomness: common.GenerateRandomBytes(), Witness: types.Witness{ - Height: 1, + Height: b00.Position.Height, Data: b00.Hash.Bytes(), }} deliver(b00) @@ -257,24 +236,32 @@ func (s *AppTestSuite) TestWitness() { Witness: types.Witness{Height: 4}})) // Mismatched witness height and data, should return invalid. s.Require().Equal(types.VerifyInvalidBlock, app.VerifyBlock(&types.Block{ - Witness: types.Witness{Height: 1, Data: b01.Hash.Bytes()}})) + Witness: types.Witness{ + Height: 1, + Data: b01.Hash.Bytes(), + }})) // We can only verify a block followed last confirmed block. s.Require().Equal(types.VerifyRetryLater, app.VerifyBlock(&types.Block{ - Witness: types.Witness{Height: 2, Data: b01.Hash.Bytes()}, - Position: types.Position{Height: 4}})) + Witness: types.Witness{ + Height: b01.Position.Height, + Data: b01.Hash.Bytes()}, + Position: types.Position{Height: 5}})) // It's the OK case. s.Require().Equal(types.VerifyOK, app.VerifyBlock(&types.Block{ - Witness: types.Witness{Height: 2, Data: b01.Hash.Bytes()}, - Position: types.Position{Height: 3}})) + Witness: types.Witness{ + Height: b01.Position.Height, + Data: b01.Hash.Bytes()}, + Position: types.Position{Height: 4}})) // Check current last pending height. - s.Require().Equal(app.LastPendingHeight, uint64(3)) + _, lastRec := app.LastDeliveredRecordNoLock() + s.Require().Equal(lastRec.Pos.Height, uint64(3)) // We can only prepare witness for what've delivered. _, err := app.PrepareWitness(4) s.Require().Equal(err.Error(), ErrLowerPendingHeight.Error()) // It should be ok to prepare for height that already delivered. w, err := app.PrepareWitness(3) s.Require().NoError(err) - s.Require().Equal(w.Height, b02.Finalization.Height) + s.Require().Equal(w.Height, b02.Position.Height) s.Require().Equal(0, bytes.Compare(w.Data, b02.Hash[:])) } @@ -292,10 +279,10 @@ func (s *AppTestSuite) TestAttachedWithRoundEvent() { for r := uint64(2); r <= uint64(20); r++ { gov.ProposeCRS(r, getCRS(r, 0)) } - for r := uint64(0); r <= uint64(19); r++ { - gov.NotifyRound(r, r*roundLength) + for r := uint64(1); r <= uint64(19); r++ { + gov.NotifyRound(r, utils.GetRoundHeight(gov, r-1)+roundLength) } - gov.NotifyRound(20, 2200) + gov.NotifyRound(20, 2201) // Reset round#20 twice, then make it done DKG preparation. gov.ResetDKG(getCRS(20, 1)) gov.ResetDKG(getCRS(20, 2)) @@ -311,8 +298,8 @@ func (s *AppTestSuite) TestAttachedWithRoundEvent() { s.proposeMPK(gov, 22, 0, 3) s.proposeFinalize(gov, 22, 0, 3) // Prepare utils.RoundEvent, starts from round#19, reset(for round#20)#1. - rEvt, err := utils.NewRoundEvent(context.Background(), gov, s.logger, 19, - 2019, core.ConfigRoundShift) + rEvt, err := utils.NewRoundEvent(context.Background(), gov, s.logger, + types.Position{Round: 19, Height: 2019}, core.ConfigRoundShift) s.Require().NoError(err) // Register a handler to collects triggered events. evts := make(chan evtParamToCheck, 3) @@ -331,30 +318,31 @@ func (s *AppTestSuite) TestAttachedWithRoundEvent() { deliver := func(round, start, end uint64) { for i := start; i <= end; i++ { b := &types.Block{ - Hash: common.NewRandomHash(), - Position: types.Position{Round: round, Height: i}, - Finalization: types.FinalizationResult{Height: i}, + Hash: common.NewRandomHash(), + Position: types.Position{Round: round, Height: i}, + Randomness: common.GenerateRandomBytes(), } app.BlockConfirmed(*b) - app.BlockDelivered(b.Hash, b.Position, b.Finalization) + app.BlockDelivered(b.Hash, b.Position, b.Randomness) } } - // Deliver blocks from height=2020 to height=2081. + // Deliver blocks from height=2020 to height=2092. for r := uint64(0); r <= uint64(19); r++ { - deliver(r, r*roundLength, (r+1)*roundLength-1) + begin := utils.GetRoundHeight(gov, r) + deliver(r, begin, begin+roundLength-1) } - deliver(19, 2000, 2091) - s.Require().Equal(<-evts, evtParamToCheck{19, 1, 2000, gov.CRS(19)}) - s.Require().Equal(<-evts, evtParamToCheck{19, 2, 2100, gov.CRS(19)}) - s.Require().Equal(<-evts, evtParamToCheck{20, 0, 2200, gov.CRS(20)}) + deliver(19, 2001, 2092) + s.Require().Equal(<-evts, evtParamToCheck{19, 1, 2001, gov.CRS(19)}) + s.Require().Equal(<-evts, evtParamToCheck{19, 2, 2101, gov.CRS(19)}) + s.Require().Equal(<-evts, evtParamToCheck{20, 0, 2201, gov.CRS(20)}) // Deliver blocks from height=2082 to height=2281. - deliver(19, 2092, 2199) - deliver(20, 2200, 2291) - s.Require().Equal(<-evts, evtParamToCheck{21, 0, 2300, gov.CRS(21)}) + deliver(19, 2093, 2200) + deliver(20, 2201, 2292) + s.Require().Equal(<-evts, evtParamToCheck{21, 0, 2301, gov.CRS(21)}) // Deliver blocks from height=2282 to height=2381. - deliver(20, 2292, 2299) - deliver(21, 2300, 2391) - s.Require().Equal(<-evts, evtParamToCheck{22, 0, 2400, gov.CRS(22)}) + deliver(20, 2293, 2300) + deliver(21, 2301, 2392) + s.Require().Equal(<-evts, evtParamToCheck{22, 0, 2401, gov.CRS(22)}) } func TestApp(t *testing.T) { diff --git a/core/test/block-revealer.go b/core/test/block-revealer.go index 7516e6c..e104f04 100644 --- a/core/test/block-revealer.go +++ b/core/test/block-revealer.go @@ -50,53 +50,48 @@ func loadAllBlocks(iter db.BlockIterator) ( return } -// CompactionChainBlockRevealer implements BlockRevealer interface, which would +// BlockRevealerByPosition implements BlockRevealer interface, which would // load all blocks from db, reveal them in the order of compaction chain, // from the genesis block to the latest one. -type CompactionChainBlockRevealer struct { - blocks types.BlocksByFinalizationHeight +type BlockRevealerByPosition struct { + blocks types.BlocksByPosition nextRevealIndex int } -// NewCompactionChainBlockRevealer constructs a block revealer in the order of +// NewBlockRevealerByPosition constructs a block revealer in the order of // compaction chain. -func NewCompactionChainBlockRevealer(iter db.BlockIterator, - startHeight uint64) (r *CompactionChainBlockRevealer, err error) { +func NewBlockRevealerByPosition(iter db.BlockIterator, startHeight uint64) ( + r *BlockRevealerByPosition, err error) { blocksByHash, err := loadAllBlocks(iter) if err != nil { return } - if startHeight == 0 { - startHeight = 1 - } - blocks := types.BlocksByFinalizationHeight{} + blocks := types.BlocksByPosition{} for _, b := range blocksByHash { - if b.Finalization.Height < startHeight { + if b.Position.Height < startHeight { continue } blocks = append(blocks, b) } - sort.Sort(types.BlocksByFinalizationHeight(blocks)) - // Make sure the finalization height of blocks are incremental with step 1. + sort.Sort(types.BlocksByPosition(blocks)) + // Make sure the height of blocks are incremental with step 1. for idx, b := range blocks { if idx == 0 { continue } - if b.Finalization.Height != blocks[idx-1].Finalization.Height+1 { + if b.Position.Height != blocks[idx-1].Position.Height+1 { err = ErrNotValidCompactionChain return } } - r = &CompactionChainBlockRevealer{ - blocks: blocks, - } + r = &BlockRevealerByPosition{blocks: blocks} r.Reset() return } // NextBlock implements Revealer.Next method, which would reveal blocks in the // order of compaction chain. -func (r *CompactionChainBlockRevealer) NextBlock() (types.Block, error) { +func (r *BlockRevealerByPosition) NextBlock() (types.Block, error) { if r.nextRevealIndex == len(r.blocks) { return types.Block{}, db.ErrIterationFinished } @@ -106,6 +101,6 @@ func (r *CompactionChainBlockRevealer) NextBlock() (types.Block, error) { } // Reset implement Revealer.Reset method, which would reset revealing. -func (r *CompactionChainBlockRevealer) Reset() { +func (r *BlockRevealerByPosition) Reset() { r.nextRevealIndex = 0 } diff --git a/core/test/block-revealer_test.go b/core/test/block-revealer_test.go index 54432e8..dd2aeb8 100644 --- a/core/test/block-revealer_test.go +++ b/core/test/block-revealer_test.go @@ -30,34 +30,29 @@ type BlockRevealerTestSuite struct { suite.Suite } -func (s *BlockRevealerTestSuite) TestCompactionChainBlockReveal() { +func (s *BlockRevealerTestSuite) TestBlockRevealByPosition() { dbInst, err := db.NewMemBackedDB() s.Require().NoError(err) - // Put several blocks with finalization field ready. + // Put several blocks with position field ready. b1 := &types.Block{ - Hash: common.NewRandomHash(), - Finalization: types.FinalizationResult{ - Height: 1, - }} + Hash: common.NewRandomHash(), + Position: types.Position{Height: 1}, + } b2 := &types.Block{ - Hash: common.NewRandomHash(), - Finalization: types.FinalizationResult{ - ParentHash: b1.Hash, - Height: 2, - }} + Hash: common.NewRandomHash(), + Position: types.Position{Height: 2}, + } b3 := &types.Block{ - Hash: common.NewRandomHash(), - Finalization: types.FinalizationResult{ - ParentHash: b2.Hash, - Height: 3, - }} + Hash: common.NewRandomHash(), + Position: types.Position{Height: 3}, + } s.Require().NoError(dbInst.PutBlock(*b1)) s.Require().NoError(dbInst.PutBlock(*b3)) iter, err := dbInst.GetAllBlocks() s.Require().NoError(err) // The compaction chain is not complete, we can't construct a revealer // instance successfully. - r, err := NewCompactionChainBlockRevealer(iter, 0) + r, err := NewBlockRevealerByPosition(iter, 0) s.Require().Nil(r) s.Require().Equal(ErrNotValidCompactionChain.Error(), err.Error()) // Put a block to make the compaction chain complete. @@ -65,14 +60,14 @@ func (s *BlockRevealerTestSuite) TestCompactionChainBlockReveal() { // We can construct that revealer now. iter, err = dbInst.GetAllBlocks() s.Require().NoError(err) - r, err = NewCompactionChainBlockRevealer(iter, 0) + r, err = NewBlockRevealerByPosition(iter, 0) s.Require().NotNil(r) s.Require().NoError(err) // The revealing order should be ok. chk := func(h uint64) { b, err := r.NextBlock() s.Require().NoError(err) - s.Require().Equal(b.Finalization.Height, h) + s.Require().Equal(b.Position.Height, h) } chk(1) chk(2) @@ -83,7 +78,7 @@ func (s *BlockRevealerTestSuite) TestCompactionChainBlockReveal() { // Test 'startHeight' parameter. iter, err = dbInst.GetAllBlocks() s.Require().NoError(err) - r, err = NewCompactionChainBlockRevealer(iter, 2) + r, err = NewBlockRevealerByPosition(iter, 2) s.Require().NotNil(r) s.Require().NoError(err) chk(2) diff --git a/core/test/governance.go b/core/test/governance.go index 4ee20d8..14b7838 100644 --- a/core/test/governance.go +++ b/core/test/governance.go @@ -56,7 +56,7 @@ func NewGovernance(state *State, roundShift uint64) (g *Governance, err error) { pendingConfigChanges: make(map[uint64]map[StateChangeType]interface{}), stateModule: state, prohibitedTypes: make(map[StateChangeType]struct{}), - roundBeginHeights: []uint64{0}, + roundBeginHeights: []uint64{types.GenesisHeight}, } return } @@ -94,6 +94,8 @@ func (g *Governance) Configuration(round uint64) *types.Config { // GetRoundHeight returns the begin height of a round. func (g *Governance) GetRoundHeight(round uint64) uint64 { + // This is a workaround to fit fullnode's behavior, their 0 is reserved for + // a genesis block unseen to core. if round == 0 { return 0 } @@ -103,8 +105,7 @@ func (g *Governance) GetRoundHeight(round uint64) uint64 { panic(fmt.Errorf("round begin height is not ready: %d %d", round, len(g.roundBeginHeights))) } - // TODO(jimmy): remove this workaround. - return g.roundBeginHeights[round] + 1 + return g.roundBeginHeights[round] } // CRS returns the CRS for a given round. @@ -342,7 +343,7 @@ func (g *Governance) CatchUpWithRound(round uint64) { // begin height of round 0 and round 1 should be ready, they won't be // afected by DKG reset mechanism. g.roundBeginHeights = append(g.roundBeginHeights, - g.configs[0].RoundLength) + g.configs[0].RoundLength+g.roundBeginHeights[0]) } } diff --git a/core/test/network.go b/core/test/network.go index b0ce3f7..f32c27f 100644 --- a/core/test/network.go +++ b/core/test/network.go @@ -295,10 +295,7 @@ func (n *Network) BroadcastBlock(block *types.Block) { } n.addBlockToCache(block) if block.IsFinalized() { - n.addBlockFinalizationToCache( - block.Hash, - block.Finalization.Height, - block.Finalization.Randomness) + n.addBlockRandomnessToCache(block.Hash, block.Randomness) } } @@ -308,11 +305,7 @@ func (n *Network) BroadcastAgreementResult( if !n.markAgreementResultAsSent(result.BlockHash) { return } - n.addBlockFinalizationToCache( - result.BlockHash, - result.FinalizationHeight, - result.Randomness, - ) + n.addBlockRandomnessToCache(result.BlockHash, result.Randomness) notarySet := n.getNotarySet(result.Position.Round) count := maxAgreementResultBroadcast for nID := range notarySet { @@ -626,16 +619,14 @@ func (n *Network) addBlockToCache(b *types.Block) { n.blockCache[b.Hash] = b.Clone() } -func (n *Network) addBlockFinalizationToCache( - hash common.Hash, height uint64, rand []byte) { +func (n *Network) addBlockRandomnessToCache(hash common.Hash, rand []byte) { n.blockCacheLock.Lock() defer n.blockCacheLock.Unlock() block, exist := n.blockCache[hash] if !exist { return } - block.Finalization.Height = height - block.Finalization.Randomness = rand + block.Randomness = rand } func (n *Network) addVoteToCache(v *types.Vote) { diff --git a/core/test/network_test.go b/core/test/network_test.go index d0e9fb2..7a5ad16 100644 --- a/core/test/network_test.go +++ b/core/test/network_test.go @@ -252,7 +252,8 @@ func (s *NetworkTestSuite) TestBroadcastToSet() { 1, pubKeys, time.Second, &common.NullLogger{}, true), 2) req.NoError(err) req.NoError(gov.State().RequestChange(StateChangeNotarySetSize, uint32(1))) - gov.NotifyRound(round, gov.Configuration(0).RoundLength) + gov.NotifyRound(round, + utils.GetRoundHeight(gov, 0)+gov.Configuration(0).RoundLength) networks := s.setupNetworks(pubKeys) cache := utils.NewNodeSetCache(gov) // Cache required set of nodeIDs. @@ -278,16 +279,16 @@ func (s *NetworkTestSuite) TestBroadcastToSet() { req.NotNil(nerd) req.NotNil(notaryNode) nerd.AttachNodeSetCache(cache) + pos := types.Position{Round: round, Height: types.GenesisHeight} // Try broadcasting with datum from round 0, and make sure only node belongs // to that set receiving the message. - nerd.BroadcastVote(&types.Vote{VoteHeader: types.VoteHeader{ - Position: types.Position{Round: round}}}) + nerd.BroadcastVote(&types.Vote{VoteHeader: types.VoteHeader{Position: pos}}) req.IsType(&types.Vote{}, <-notaryNode.ReceiveChan()) - nerd.BroadcastDKGPrivateShare(&typesDKG.PrivateShare{Round: round}) + nerd.BroadcastDKGPrivateShare(&typesDKG.PrivateShare{Round: pos.Round}) req.IsType(&typesDKG.PrivateShare{}, <-notaryNode.ReceiveChan()) - nerd.BroadcastDKGPartialSignature(&typesDKG.PartialSignature{Round: round}) + nerd.BroadcastDKGPartialSignature(&typesDKG.PartialSignature{Round: pos.Round}) req.IsType(&typesDKG.PartialSignature{}, <-notaryNode.ReceiveChan()) - nerd.BroadcastBlock(&types.Block{Position: types.Position{Round: round}}) + nerd.BroadcastBlock(&types.Block{Position: pos}) req.IsType(&types.Block{}, <-notaryNode.ReceiveChan()) } |