diff options
-rw-r--r-- | .travis.yml | 52 | ||||
-rw-r--r-- | GNUmakefile | 24 | ||||
-rw-r--r-- | Gopkg.lock | 12 | ||||
-rw-r--r-- | core/configuration-chain_test.go | 96 | ||||
-rw-r--r-- | core/consensus_test.go | 72 | ||||
-rw-r--r-- | integration_test/consensus_test.go | 31 |
6 files changed, 230 insertions, 57 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..faf9a24 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,52 @@ +language: go +go_import_path: github.com/dexon-foundation/dexon-consensus +os: linux +dist: trusty +sudo: required +go: 1.11.x +addons: + apt: + packages: + - openssl + - libssl-dev + - libgmp-dev +script: +- set -e +- bin/install_tools.sh +- dep ensure -v +- make dep +- | + if [ "${TRAVIS_BRANCH}" == "master" ]; then + make test + else + make test-short + fi +- make +git: + depth: 3 +cache: + directories: + - vendor +before_cache: +- rm -rf vendor/github.com/dexon-foundation/bls +- rm -rf vendor/github.com/dexon-foundation/mcl +env: +- ONLY_INTEGRATION_TEST=true NO_TEST_RACE=true +- ONLY_INTEGRATION_TEST=true +- NO_INTEGRATION_TEST=true +matrix: + allow_failures: + - env: ONLY_INTEGRATION_TEST=true + fast_finish: true + include: + - name: Lint + script: + - set -e + - bin/install_tools.sh + - dep ensure -v + - make dep + - make lint + - make vet +notifications: + slack: + secure: Asyh9eDoT6/zn6bnpDQ0CabPk6YwP2N5ACmyltT9ozeJLCoOGAsWmmVyUpHJkF/yA6Zd59ilGfC4hFMy+Wk+DZvbQL27QTsgcLf+sHzbD2CunCuJzDkO8b6JSKgZ60BH5XCLSxtf7p/0lYn+MnXCg+Ly/CFN5fwacp6gybZ+UqlH+xjvW8VSZnka2YHzRIB3P5+013scPvjr9WIJ/wxviUZetFM+AOL3Uf2t7hXEGTceYEN8bIchElazJoYpzzN0nZwHJ7/IjdbSWNnXfuJUlEEqrUHeY+shLIGZvGES9zqPIOHPFJQyh3oyknYY08wsZJpU7InS/wbOs43Pte78vhoYQbxIi5Pg4bPGAWJgTePllFV99rk7ELBoDPrEp/auSxlEu1rhq9yFsef0HePrp2KuAQHz5twnnaF3kAR6IUoa/DgWNqgw71QqdWFihEeeQGn/sYUocdp2Jc1ZVIUrafICjCPJFf5pSoAyBVnx0lA4MhyEEVdwtoOR2XXn6G4+58/IF3VfH2gqS8OlE10uA71wQb3np74RBNA4mfke3mjPKwKNXJug/txPBFxsGGq9kOVwYVm4QUwkJ5hbHciGFv29Pzvo1fhJSE5/YluaHhzLHeb8vVTBTPMwzApQBL2ZCdKPZ9bppzJiR+zR+JacE/jrHnQfpMJYoykvcGQrN/s= diff --git a/GNUmakefile b/GNUmakefile index 51c6b34..b7af3f5 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -44,6 +44,21 @@ else endif endef +GO_TEST_TIMEOUT := 20m + +TEST_TARGET := go list ./... | grep -v 'vendor' +ifeq ($(NO_INTEGRATION_TEST), true) + GO_TEST_TIMEOUT := 10m + TEST_TARGET := $(TEST_TARGET) | grep -v 'integration_test' +else ifeq ($(ONLY_INTEGRATION_TEST), true) + TEST_TARGET := $(TEST_TARGET) | grep 'integration_test' +endif + +GO_TEST_FLAG := -v -timeout $(GO_TEST_TIMEOUT) +ifneq ($(NO_TEST_RACE), true) + GO_TEST_FLAG := $(GO_TEST_FLAG) -race +endif + COMPONENTS = \ dexcon-simulation \ dexcon-simulation-peer-server \ @@ -83,17 +98,18 @@ lint: vet: @go vet `go list ./... | grep -v 'vendor'` + test-short: - @for pkg in `go list ./... | grep -v 'vendor'`; do \ - if ! go test -race -short -v $$pkg; then \ + @for pkg in `$(TEST_TARGET)`; do \ + if ! go test -short $(GO_TEST_FLAG) $$pkg; then \ echo 'Some test failed, abort'; \ exit 1; \ fi; \ done test: - @for pkg in `go list ./... | grep -v 'vendor'`; do \ - if ! go test -timeout 15m -race -v $$pkg; then \ + @for pkg in `$(TEST_TARGET)`; do \ + if ! go test $(GO_TEST_FLAG) $$pkg; then \ echo 'Some test failed, abort'; \ exit 1; \ fi; \ @@ -27,7 +27,7 @@ [[projects]] branch = "dev" - digest = "1:2b010a52e574bc0f815d5fdf403a8b36384665ac0923c94a28b99f8537fbf9fe" + digest = "1:de44df5d2d82983648c5f861dd7b7f54133e14b1c4dc412c58347e3872ab2181" name = "github.com/dexon-foundation/dexon" packages = [ "common", @@ -36,12 +36,21 @@ "crypto", "crypto/secp256k1", "crypto/sha3", + "log", "rlp", ] pruneopts = "UT" revision = "abe4e80a8b6cf9dee93652ea36cdc56065a39a0f" [[projects]] + digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" + name = "github.com/go-stack/stack" + packages = ["."] + pruneopts = "UT" + revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" + version = "v1.8.0" + +[[projects]] branch = "master" digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009" name = "github.com/golang/snappy" @@ -115,6 +124,7 @@ input-imports = [ "github.com/dexon-foundation/bls/ffi/go/bls", "github.com/dexon-foundation/dexon/crypto", + "github.com/dexon-foundation/dexon/log", "github.com/dexon-foundation/dexon/rlp", "github.com/naoina/toml", "github.com/stretchr/testify/suite", diff --git a/core/configuration-chain_test.go b/core/configuration-chain_test.go index 3a7f729..2e75b7f 100644 --- a/core/configuration-chain_test.go +++ b/core/configuration-chain_test.go @@ -19,6 +19,7 @@ package core import ( "encoding/json" + "errors" "sync" "testing" "time" @@ -61,16 +62,24 @@ func newTestCCReceiver( func (r *testCCReceiver) ProposeDKGComplaint(complaint *typesDKG.Complaint) { prvKey, exist := r.s.prvKeys[complaint.ProposerID] - r.s.Require().True(exist) + if !exist { + panic(errors.New("should exist")) + } var err error complaint.Signature, err = prvKey.Sign(hashDKGComplaint(complaint)) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } for _, gov := range r.govs { // Use Marshal/Unmarshal to do deep copy. data, err := json.Marshal(complaint) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } complaintCopy := &typesDKG.Complaint{} - r.s.Require().NoError(json.Unmarshal(data, complaintCopy)) + if err := json.Unmarshal(data, complaintCopy); err != nil { + panic(err) + } gov.AddDKGComplaint(complaintCopy.Round, complaintCopy) } } @@ -78,16 +87,24 @@ func (r *testCCReceiver) ProposeDKGComplaint(complaint *typesDKG.Complaint) { func (r *testCCReceiver) ProposeDKGMasterPublicKey( mpk *typesDKG.MasterPublicKey) { prvKey, exist := r.s.prvKeys[mpk.ProposerID] - r.s.Require().True(exist) + if !exist { + panic(errors.New("should exist")) + } var err error mpk.Signature, err = prvKey.Sign(hashDKGMasterPublicKey(mpk)) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } for _, gov := range r.govs { // Use Marshal/Unmarshal to do deep copy. data, err := json.Marshal(mpk) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } mpkCopy := typesDKG.NewMasterPublicKey() - r.s.Require().NoError(json.Unmarshal(data, mpkCopy)) + if err := json.Unmarshal(data, mpkCopy); err != nil { + panic(err) + } gov.AddDKGMasterPublicKey(mpkCopy.Round, mpkCopy) } } @@ -96,14 +113,22 @@ func (r *testCCReceiver) ProposeDKGPrivateShare( prv *typesDKG.PrivateShare) { go func() { prvKey, exist := r.s.prvKeys[prv.ProposerID] - r.s.Require().True(exist) + if !exist { + panic(errors.New("should exist")) + } var err error prv.Signature, err = prvKey.Sign(hashDKGPrivateShare(prv)) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } receiver, exist := r.nodes[prv.ReceiverID] - r.s.Require().True(exist) + if !exist { + panic(errors.New("should exist")) + } err = receiver.processPrivateShare(prv) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } }() } @@ -111,34 +136,52 @@ func (r *testCCReceiver) ProposeDKGAntiNackComplaint( prv *typesDKG.PrivateShare) { go func() { prvKey, exist := r.s.prvKeys[prv.ProposerID] - r.s.Require().True(exist) + if !exist { + panic(errors.New("should exist")) + } var err error prv.Signature, err = prvKey.Sign(hashDKGPrivateShare(prv)) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } for _, cc := range r.nodes { // Use Marshal/Unmarshal to do deep copy. data, err := json.Marshal(prv) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } prvCopy := &typesDKG.PrivateShare{} - r.s.Require().NoError(json.Unmarshal(data, prvCopy)) + if err := json.Unmarshal(data, prvCopy); err != nil { + panic(err) + } err = cc.processPrivateShare(prvCopy) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } } }() } func (r *testCCReceiver) ProposeDKGFinalize(final *typesDKG.Finalize) { prvKey, exist := r.s.prvKeys[final.ProposerID] - r.s.Require().True(exist) + if !exist { + panic(errors.New("should exist")) + } var err error final.Signature, err = prvKey.Sign(hashDKGFinalize(final)) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } for _, gov := range r.govs { // Use Marshal/Unmarshal to do deep copy. data, err := json.Marshal(final) - r.s.Require().NoError(err) + if err != nil { + panic(err) + } finalCopy := &typesDKG.Finalize{} - r.s.Require().NoError(json.Unmarshal(data, finalCopy)) + if err := json.Unmarshal(data, finalCopy); err != nil { + panic(err) + } gov.AddDKGFinalize(finalCopy.Round, finalCopy) } } @@ -174,7 +217,7 @@ func (s *ConfigurationChainTestSuite) runDKG( for _, nID := range s.nIDs { gov, err := test.NewGovernance(test.NewState( - pks, 50*time.Millisecond, &common.NullLogger{}, true), ConfigRoundShift) + pks, 100*time.Millisecond, &common.NullLogger{}, true), ConfigRoundShift) s.Require().NoError(err) cache := utils.NewNodeSetCache(gov) cfgChains[nID] = newConfigurationChain( @@ -214,6 +257,9 @@ func (s *ConfigurationChainTestSuite) preparePartialSignature( psigs []*typesDKG.PartialSignature) { psigs = make([]*typesDKG.PartialSignature, 0, len(cfgChains)) for nID, cc := range cfgChains { + if _, exist := cc.gpk[round]; !exist { + continue + } if _, exist := cc.gpk[round].qualifyNodeIDs[nID]; !exist { continue } @@ -245,6 +291,9 @@ func (s *ConfigurationChainTestSuite) TestConfigurationChain() { errs := make(chan error, n) tsigChan := make(chan crypto.Signature, n) for nID, cc := range cfgChains { + if _, exist := cc.gpk[round]; !exist { + continue + } if _, exist := cc.gpk[round].qualifyNodeIDs[nID]; !exist { continue } @@ -260,6 +309,9 @@ func (s *ConfigurationChainTestSuite) TestConfigurationChain() { } } for nID, cc := range cfgChains { + if _, exist := cc.gpk[round]; !exist { + s.FailNow("Should be qualifyied") + } if _, exist := cc.gpk[round].qualifyNodeIDs[nID]; !exist { s.FailNow("Should be qualifyied") } diff --git a/core/consensus_test.go b/core/consensus_test.go index 9234512..65f4e36 100644 --- a/core/consensus_test.go +++ b/core/consensus_test.go @@ -18,17 +18,19 @@ package core import ( + "encoding/json" "sort" "testing" "time" + "github.com/stretchr/testify/suite" + "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/blockdb" "github.com/dexon-foundation/dexon-consensus/core/crypto" "github.com/dexon-foundation/dexon-consensus/core/test" "github.com/dexon-foundation/dexon-consensus/core/types" typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg" - "github.com/stretchr/testify/suite" ) // network implements core.Network. @@ -101,37 +103,33 @@ func (nc *networkConnection) broadcast(from types.NodeID, msg interface{}) { } func (nc *networkConnection) send(to types.NodeID, msg interface{}) { - con, exist := nc.cons[to] + ch, exist := nc.cons[to] if !exist { return } - go func() { - var err error - // Testify package does not support concurrent call. - // Use panic() to detact error. - switch val := msg.(type) { - case *types.Block: - err = con.preProcessBlock(val) - case *types.Vote: - err = con.ProcessVote(val) - case *types.AgreementResult: - err = con.ProcessAgreementResult(val) - case *types.BlockRandomnessResult: - err = con.ProcessBlockRandomnessResult(val) - case *typesDKG.PrivateShare: - err = con.cfgModule.processPrivateShare(val) - case *typesDKG.PartialSignature: - err = con.cfgModule.processPartialSignature(val) - } + msgCopy := msg + // Clone msg if necessary. + switch val := msg.(type) { + case *types.Block: + msgCopy = val.Clone() + case *typesDKG.PrivateShare: + // Use Marshal/Unmarshal to do deep copy. + data, err := json.Marshal(val) if err != nil { panic(err) } - }() + valCopy := &typesDKG.PrivateShare{} + if err := json.Unmarshal(data, valCopy); err != nil { + panic(err) + } + msgCopy = valCopy + } + ch <- msgCopy } type networkConnection struct { s *ConsensusTestSuite - cons map[types.NodeID]*Consensus + cons map[types.NodeID]chan interface{} } func (nc *networkConnection) newNetwork(nID types.NodeID) *network { @@ -142,7 +140,33 @@ func (nc *networkConnection) newNetwork(nID types.NodeID) *network { } func (nc *networkConnection) setCon(nID types.NodeID, con *Consensus) { - nc.cons[nID] = con + ch := make(chan interface{}, 1000) + go func() { + for { + msg := <-ch + var err error + // Testify package does not support concurrent call. + // Use panic() to detact error. + switch val := msg.(type) { + case *types.Block: + err = con.preProcessBlock(val) + case *types.Vote: + err = con.ProcessVote(val) + case *types.AgreementResult: + err = con.ProcessAgreementResult(val) + case *types.BlockRandomnessResult: + err = con.ProcessBlockRandomnessResult(val) + case *typesDKG.PrivateShare: + err = con.cfgModule.processPrivateShare(val) + case *typesDKG.PartialSignature: + err = con.cfgModule.processPartialSignature(val) + } + if err != nil { + panic(err) + } + } + }() + nc.cons[nID] = ch } type ConsensusTestSuite struct { @@ -153,7 +177,7 @@ type ConsensusTestSuite struct { func (s *ConsensusTestSuite) newNetworkConnection() *networkConnection { return &networkConnection{ s: s, - cons: make(map[types.NodeID]*Consensus), + cons: make(map[types.NodeID]chan interface{}), } } diff --git a/integration_test/consensus_test.go b/integration_test/consensus_test.go index d66ff72..16cfa8f 100644 --- a/integration_test/consensus_test.go +++ b/integration_test/consensus_test.go @@ -19,6 +19,7 @@ package integration import ( "context" + "fmt" "sync" "testing" "time" @@ -208,10 +209,10 @@ func (s *ConsensusTestSuite) TestSimple() { Loop: for { <-time.After(5 * time.Second) - s.T().Log("check latest position delivered by each node") + fmt.Println("check latest position delivered by each node") for _, n := range nodes { latestPos := n.app.GetLatestDeliveredPosition() - s.T().Log("latestPos", n.ID, &latestPos) + fmt.Println("latestPos", n.ID, &latestPos) if latestPos.Round < untilRound { continue Loop } @@ -283,10 +284,10 @@ func (s *ConsensusTestSuite) TestNumChainsChange() { Loop: for { <-time.After(5 * time.Second) - s.T().Log("check latest position delivered by each node") + fmt.Println("check latest position delivered by each node") for _, n := range nodes { latestPos := n.app.GetLatestDeliveredPosition() - s.T().Log("latestPos", n.ID, &latestPos) + fmt.Println("latestPos", n.ID, &latestPos) if latestPos.Round < untilRound { continue Loop } @@ -320,7 +321,7 @@ func (s *ConsensusTestSuite) TestSync() { core.ConfigRoundShift) req.NoError(err) req.NoError(seedGov.State().RequestChange( - test.StateChangeRoundInterval, 30*time.Second)) + test.StateChangeRoundInterval, 50*time.Second)) // A short round interval. nodes := s.setupNodes(dMoment, prvKeys, seedGov) // Choose the first node as "syncNode" that its consensus' Run() is called @@ -348,6 +349,24 @@ func (s *ConsensusTestSuite) TestSync() { } } }() + // Print status every 5 seconds so CI won't fail. + monitorCtx, monitorCancel := context.WithCancel( + context.Background()) + defer monitorCancel() + go func() { + for { + select { + case <-time.After(5 * time.Second): + for _, n := range nodes { + pos := n.app.GetLatestDeliveredPosition() + fmt.Println("latestPos", n.ID, &pos) + break + } + case <-monitorCtx.Done(): + return + } + } + }() ReachAlive: for { // If all nodes excepts syncNode have reached aliveRound, call syncNode's @@ -425,7 +444,7 @@ ReachAlive: // Stop a node, we should still be able to proceed. stoppedNode.con.Stop() stoppedNode.con = nil - s.T().Log("one node stopped") + fmt.Println("one node stopped") // Initiate a dummy routine to consume the receive channel. go func() { for { |