aboutsummaryrefslogtreecommitdiffstats
path: root/core/test/app.go
diff options
context:
space:
mode:
authorMission Liao <mission.liao@dexon.org>2018-08-15 14:04:35 +0800
committerGitHub <noreply@github.com>2018-08-15 14:04:35 +0800
commit3a9b545b0f33435c277fcede2251e4b5ae800d40 (patch)
tree7848c5f02a3e2e145a038ec206e95d6230ab5d45 /core/test/app.go
parentc4bfb69724f5fb777fbf5fc272dc65a0f9d1f368 (diff)
downloadtangerine-consensus-3a9b545b0f33435c277fcede2251e4b5ae800d40.tar
tangerine-consensus-3a9b545b0f33435c277fcede2251e4b5ae800d40.tar.gz
tangerine-consensus-3a9b545b0f33435c277fcede2251e4b5ae800d40.tar.bz2
tangerine-consensus-3a9b545b0f33435c277fcede2251e4b5ae800d40.tar.lz
tangerine-consensus-3a9b545b0f33435c277fcede2251e4b5ae800d40.tar.xz
tangerine-consensus-3a9b545b0f33435c277fcede2251e4b5ae800d40.tar.zst
tangerine-consensus-3a9b545b0f33435c277fcede2251e4b5ae800d40.zip
test: refine test utility (#61)
* Add functionality to test.App * Add test utility to generate slices of types.ValidatorID
Diffstat (limited to 'core/test/app.go')
-rw-r--r--core/test/app.go158
1 files changed, 142 insertions, 16 deletions
diff --git a/core/test/app.go b/core/test/app.go
index 55ba7c5..aedcba9 100644
--- a/core/test/app.go
+++ b/core/test/app.go
@@ -18,44 +18,79 @@
package test
import (
+ "fmt"
+ "sync"
"time"
"github.com/dexon-foundation/dexon-consensus-core/common"
)
+var (
+ // ErrEmptyDeliverSequence means there is no delivery event in this App
+ // instance.
+ ErrEmptyDeliverSequence = fmt.Errorf("emptry deliver sequence")
+ // 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")
+ // 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")
+ // ErrDeliveredBlockNotAcked means some block delivered (confirmed) but
+ // not strongly acked.
+ ErrDeliveredBlockNotAcked = fmt.Errorf("delivered block not acked")
+ // ErrMismatchTotalOrderingAndDelivered mean the sequence of total ordering
+ // and delivered are different.
+ ErrMismatchTotalOrderingAndDelivered = fmt.Errorf(
+ "mismatch total ordering and delivered sequence")
+)
+
+type totalOrderDeliver struct {
+ BlockHashes common.Hashes
+ Early bool
+}
+
// App implements Application interface for testing purpose.
type App struct {
- Acked map[common.Hash]struct{}
- TotalOrdered []*struct {
- BlockHashes common.Hashes
- Early bool
- }
- Delivered map[common.Hash]time.Time
+ Acked map[common.Hash]struct{}
+ ackedLock sync.RWMutex
+ TotalOrdered []*totalOrderDeliver
+ totalOrderedLock sync.RWMutex
+ Delivered map[common.Hash]time.Time
+ DeliverSequence common.Hashes
+ deliveredLock sync.RWMutex
}
// NewApp constructs a TestApp instance.
func NewApp() *App {
return &App{
- Acked: make(map[common.Hash]struct{}),
- TotalOrdered: []*struct {
- BlockHashes common.Hashes
- Early bool
- }{},
- Delivered: make(map[common.Hash]time.Time),
+ Acked: make(map[common.Hash]struct{}),
+ TotalOrdered: []*totalOrderDeliver{},
+ Delivered: make(map[common.Hash]time.Time),
+ DeliverSequence: common.Hashes{},
}
}
// StronglyAcked implements Application interface.
func (app *App) StronglyAcked(blockHash common.Hash) {
+ app.ackedLock.Lock()
+ defer app.ackedLock.Unlock()
+
app.Acked[blockHash] = struct{}{}
}
// TotalOrderingDeliver implements Application interface.
func (app *App) TotalOrderingDeliver(blockHashes common.Hashes, early bool) {
- app.TotalOrdered = append(app.TotalOrdered, &struct {
- BlockHashes common.Hashes
- Early bool
- }{
+ app.totalOrderedLock.Lock()
+ defer app.totalOrderedLock.Unlock()
+
+ app.TotalOrdered = append(app.TotalOrdered, &totalOrderDeliver{
BlockHashes: blockHashes,
Early: early,
})
@@ -63,5 +98,96 @@ func (app *App) TotalOrderingDeliver(blockHashes common.Hashes, early bool) {
// DeliverBlock implements Application interface.
func (app *App) DeliverBlock(blockHash common.Hash, timestamp time.Time) {
+ app.deliveredLock.Lock()
+ defer app.deliveredLock.Unlock()
+
app.Delivered[blockHash] = timestamp
+ app.DeliverSequence = append(app.DeliverSequence, blockHash)
+}
+
+// Compare performs these checks against another App instance
+// and return erros if not passed:
+// - deliver sequence by comparing block hashes.
+// - consensus timestamp of each block are equal.
+func (app *App) Compare(other *App) error {
+ app.deliveredLock.RLock()
+ defer app.deliveredLock.RUnlock()
+ other.deliveredLock.RLock()
+ defer other.deliveredLock.RUnlock()
+
+ minLength := len(app.DeliverSequence)
+ if minLength > len(other.DeliverSequence) {
+ minLength = len(other.DeliverSequence)
+ }
+ if minLength == 0 {
+ return ErrEmptyDeliverSequence
+ }
+ for idx, h := range app.DeliverSequence[:minLength] {
+ hOther := other.DeliverSequence[idx]
+ if hOther != h {
+ return ErrMismatchBlockHashSequence
+ }
+ if app.Delivered[h] != other.Delivered[h] {
+ return ErrMismatchConsensusTime
+ }
+ }
+ return nil
+}
+
+// Verify checks the integrity of date received by this App instance.
+func (app *App) Verify() error {
+ app.deliveredLock.RLock()
+ defer app.deliveredLock.RUnlock()
+
+ if len(app.DeliverSequence) == 0 {
+ return ErrEmptyDeliverSequence
+ }
+ if len(app.DeliverSequence) != len(app.Delivered) {
+ return ErrApplicationIntegrityFailed
+ }
+
+ app.ackedLock.RLock()
+ defer app.ackedLock.RUnlock()
+
+ prevTime := time.Time{}
+ for _, h := range app.DeliverSequence {
+ // Make sure delivered block is strongly acked.
+ if _, acked := app.Acked[h]; !acked {
+ return ErrDeliveredBlockNotAcked
+ }
+ t, exists := app.Delivered[h]
+ if !exists {
+ return ErrApplicationIntegrityFailed
+ }
+ // Make sure the consensus time is incremental.
+ ok := prevTime.Before(t) || prevTime.Equal(t)
+ if !ok {
+ return ErrConsensusTimestampOutOfOrder
+ }
+ prevTime = t
+ }
+ // Make sure the order of delivered and total ordering are the same by
+ // comparing the concated string.
+ app.totalOrderedLock.RLock()
+ defer app.totalOrderedLock.RUnlock()
+
+ hashSequenceIdx := 0
+Loop:
+ for _, totalOrderDeliver := range app.TotalOrdered {
+ for _, h := range totalOrderDeliver.BlockHashes {
+ if hashSequenceIdx >= len(app.DeliverSequence) {
+ break Loop
+ }
+ if h != app.DeliverSequence[hashSequenceIdx] {
+ return ErrMismatchTotalOrderingAndDelivered
+ }
+ hashSequenceIdx++
+ }
+ }
+ if hashSequenceIdx != len(app.DeliverSequence) {
+ // The count of delivered blocks should be larger than those delivered
+ // by total ordering.
+ return ErrMismatchTotalOrderingAndDelivered
+ }
+ return nil
}