diff options
-rw-r--r-- | .circleci/config.yml | 12 | ||||
-rw-r--r-- | Gopkg.lock | 85 | ||||
-rw-r--r-- | Gopkg.toml | 4 | ||||
-rw-r--r-- | core/agreement-mgr.go | 47 | ||||
-rw-r--r-- | core/agreement.go | 13 | ||||
-rw-r--r-- | core/utils/vote-filter.go | 6 | ||||
-rw-r--r-- | core/utils/vote-filter_test.go | 12 | ||||
-rw-r--r-- | integration_test/consensus_test.go | 5 |
8 files changed, 117 insertions, 67 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 08ff726..c8d1c53 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ commands: command: | if [ "${CIRCLE_BRANCH}" == "master" ]; then make test - elif [ "${CIRCLE_BRANCH}" == "single-chain" ]; then + elif [ "${CIRCLE_BRANCH}" == "dexon-classic" ]; then make test else make test-short @@ -98,6 +98,14 @@ jobs: steps: - init_workspace - run_test + - run: + name: Saving Logs + when: on_fail + command: | + mkdir -p /tmp/logs + cd integration_test && tar -czf /tmp/logs/integration_test.tar.gz log.* + - store_artifacts: + path: /tmp/logs build: executor: go1_11 @@ -130,7 +138,7 @@ workflows: branches: only: - master - - single-chain + - dexon-classic test_and_build: jobs: @@ -3,11 +3,11 @@ [[projects]] branch = "master" - digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7" + digest = "1:093bf93a65962e8191e3e8cd8fc6c363f83d43caca9739c906531ba7210a9904" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "2a560b2036bee5e3679ec2133eb6520b2f195213" + revision = "9bfb2ca0346b57e246cc96fa31074df521175240" [[projects]] digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" @@ -19,15 +19,15 @@ [[projects]] branch = "dev" - digest = "1:c331891ab345659ad4e3e6af2c0b2ba262dd2c83867b2928f4c89455a429cb79" + digest = "1:989ca7617ed87c5fa460e9f63414d797fd0d2dd07cdda0b6d6d3e7450e002b1f" name = "github.com/dexon-foundation/bls" packages = ["ffi/go/bls"] pruneopts = "UT" - revision = "1208495d2fd3e5a69edaf7f429607e7ef431a5dd" + revision = "b0a83abe7389df7f4c45adc3b52d957f3996322d" [[projects]] branch = "dev" - digest = "1:de44df5d2d82983648c5f861dd7b7f54133e14b1c4dc412c58347e3872ab2181" + digest = "1:8fc72b5a0372267ae68d203aef3e1108eb7274050c7ded516ea1af71a8a09b77" name = "github.com/dexon-foundation/dexon" packages = [ "common", @@ -35,12 +35,11 @@ "common/math", "crypto", "crypto/secp256k1", - "crypto/sha3", "log", "rlp", ] pruneopts = "UT" - revision = "abe4e80a8b6cf9dee93652ea36cdc56065a39a0f" + revision = "f1ed7d074fe1d574a84c308c6ad878dc2d153654" [[projects]] digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" @@ -51,12 +50,23 @@ version = "v1.8.0" [[projects]] - branch = "master" - digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009" + digest = "1:e4f5819333ac698d294fe04dbf640f84719658d5c7ce195b10060cc37292ce79" name = "github.com/golang/snappy" packages = ["."] pruneopts = "UT" - revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" + revision = "2a8bb927dd31d8daada140a5d09578521ce5c36a" + version = "v0.0.1" + +[[projects]] + digest = "1:d15ee511aa0f56baacc1eb4c6b922fa1c03b38413b6be18166b996d82a0156ea" + name = "github.com/hashicorp/golang-lru" + packages = [ + ".", + "simplelru", + ] + pruneopts = "UT" + revision = "7087cb70de9f7a8bc0a10c375cb0d2280a8edf9c" + version = "v0.5.1" [[projects]] digest = "1:b56c589214f01a5601e0821387db484617392d0042f26234bf2da853a2f498a1" @@ -86,7 +96,7 @@ version = "v1.0.0" [[projects]] - digest = "1:5110e3d4f130772fd39e6ce8208ad1955b242ccfcc8ad9d158857250579c82f4" + digest = "1:8ff03ccc603abb0d7cce94d34b613f5f6251a9e1931eba1a3f9888a9029b055c" name = "github.com/stretchr/testify" packages = [ "assert", @@ -94,12 +104,12 @@ "suite", ] pruneopts = "UT" - revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" - version = "v1.2.2" + revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" + version = "v1.3.0" [[projects]] branch = "master" - digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5" + digest = "1:3f0c28875fb78456bc1815a9c13cb5962a4f0c462b37ec72e90c2f2cc407b54f" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -116,44 +126,39 @@ "leveldb/util", ] pruneopts = "UT" - revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd" + revision = "c3a204f8e96543bb0cc090385c001078f184fc46" + +[[projects]] + branch = "master" + digest = "1:d193d0292355f8fada46cf77967d64df52ce6e0a757f81a3a1b2626ac77d649d" + name = "golang.org/x/crypto" + packages = ["sha3"] + pruneopts = "UT" + revision = "38d8ce5564a5b71b2e3a00553993f1b9a7ae852f" + +[[projects]] + branch = "master" + digest = "1:13917c3d8b9b6c14a941d66af59444b7ce53a859a8b86e24891a713186f42c65" + name = "golang.org/x/sys" + packages = [ + "cpu", + "unix", + ] + pruneopts = "UT" + revision = "0ad05ae3009d6413d3363aa09b350b2ec37daf09" [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ - "github.com/btcsuite/btcd/btcec", - "github.com/davecgh/go-spew/spew", "github.com/dexon-foundation/bls/ffi/go/bls", - "github.com/dexon-foundation/dexon/common", - "github.com/dexon-foundation/dexon/common/hexutil", - "github.com/dexon-foundation/dexon/common/math", "github.com/dexon-foundation/dexon/crypto", - "github.com/dexon-foundation/dexon/crypto/secp256k1", - "github.com/dexon-foundation/dexon/crypto/sha3", "github.com/dexon-foundation/dexon/log", "github.com/dexon-foundation/dexon/rlp", - "github.com/go-stack/stack", - "github.com/golang/snappy", - "github.com/naoina/go-stringutil", + "github.com/hashicorp/golang-lru", "github.com/naoina/toml", - "github.com/naoina/toml/ast", - "github.com/pmezard/go-difflib/difflib", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/require", "github.com/stretchr/testify/suite", "github.com/syndtr/goleveldb/leveldb", - "github.com/syndtr/goleveldb/leveldb/cache", - "github.com/syndtr/goleveldb/leveldb/comparer", - "github.com/syndtr/goleveldb/leveldb/errors", - "github.com/syndtr/goleveldb/leveldb/filter", - "github.com/syndtr/goleveldb/leveldb/iterator", - "github.com/syndtr/goleveldb/leveldb/journal", - "github.com/syndtr/goleveldb/leveldb/memdb", - "github.com/syndtr/goleveldb/leveldb/opt", - "github.com/syndtr/goleveldb/leveldb/storage", - "github.com/syndtr/goleveldb/leveldb/table", - "github.com/syndtr/goleveldb/leveldb/util", ] solver-name = "gps-cdcl" solver-version = 1 @@ -45,6 +45,10 @@ branch = "master" name = "github.com/syndtr/goleveldb" +[[constraint]] + name = "github.com/hashicorp/golang-lru" + version = "0.5.1" + [prune] go-tests = true unused-packages = true diff --git a/core/agreement-mgr.go b/core/agreement-mgr.go index ac28955..4597fe9 100644 --- a/core/agreement-mgr.go +++ b/core/agreement-mgr.go @@ -24,6 +24,8 @@ import ( "sync" "time" + lru "github.com/hashicorp/golang-lru" + "github.com/dexon-foundation/dexon-consensus/common" "github.com/dexon-foundation/dexon-consensus/core/types" typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg" @@ -41,6 +43,7 @@ var ( ) const maxResultCache = 100 +const settingLimit = 3 // genValidLeader generate a validLeader function for agreement modules. func genValidLeader( @@ -130,12 +133,15 @@ type agreementMgr struct { recv *consensusBAReceiver processedBAResult map[types.Position]struct{} voteFilter *utils.VoteFilter + settingCache *lru.Cache + curRoundSetting *baRoundSetting waitGroup sync.WaitGroup isRunning bool lock sync.RWMutex } func newAgreementMgr(con *Consensus) (mgr *agreementMgr, err error) { + settingCache, _ := lru.New(settingLimit) mgr = &agreementMgr{ con: con, ID: con.ID, @@ -149,6 +155,7 @@ func newAgreementMgr(con *Consensus) (mgr *agreementMgr, err error) { ctx: con.ctx, processedBAResult: make(map[types.Position]struct{}, maxResultCache), voteFilter: utils.NewVoteFilter(), + settingCache: settingCache, } mgr.recv = &consensusBAReceiver{ consensus: con, @@ -165,21 +172,18 @@ func (mgr *agreementMgr) prepare() { newLeaderSelector(genValidLeader(mgr), mgr.logger), mgr.signer, mgr.logger) - nodes, err := mgr.cache.GetNodeSet(round) - if err != nil { + setting := mgr.generateSetting(round) + if setting == nil { + mgr.logger.Warn("Unable to prepare init setting", "round", round) return } - agr.notarySet = nodes.GetSubSet( - int(mgr.config(round).notarySetSize), - types.NewNotarySetTarget(mgr.config(round).crs)) + mgr.curRoundSetting = setting + agr.notarySet = mgr.curRoundSetting.dkgSet // Hacky way to make agreement module self contained. mgr.recv.agreementModule = agr mgr.baModule = agr if round >= DKGDelayRound { - setting := mgr.generateSetting(round) - if setting == nil { - mgr.logger.Warn("Unable to prepare init setting", "round", round) - } else if _, exist := setting.dkgSet[mgr.ID]; exist { + if _, exist := setting.dkgSet[mgr.ID]; exist { mgr.logger.Debug("Preparing signer and npks.", "round", round) npk, signer, err := mgr.con.cfgModule.getDKGInfo(round, false) if err != nil { @@ -268,9 +272,25 @@ func (mgr *agreementMgr) notifyRoundEvents(evts []utils.RoundEventParam) error { } func (mgr *agreementMgr) processVote(v *types.Vote) (err error) { + if !mgr.recv.isNotary { + return nil + } if mgr.voteFilter.Filter(v) { return nil } + if v.Position.Round == mgr.curRoundSetting.round { + if _, exist := mgr.curRoundSetting.dkgSet[v.ProposerID]; !exist { + return ErrNotInNotarySet + } + } else if v.Position.Round == mgr.curRoundSetting.round+1 { + setting := mgr.generateSetting(v.Position.Round) + if setting == nil { + return ErrConfigurationNotReady + } + if _, exist := setting.dkgSet[v.ProposerID]; !exist { + return ErrNotInNotarySet + } + } if err = mgr.baModule.processVote(v); err == nil { mgr.baModule.updateFilter(mgr.voteFilter) mgr.voteFilter.AddVote(v) @@ -339,6 +359,7 @@ func (mgr *agreementMgr) processAgreementResult( result.Position.Round) return ErrConfigurationNotReady } + mgr.curRoundSetting = setting leader, err := mgr.calcLeader(setting.dkgSet, setting.crs, result.Position) if err != nil { return err @@ -374,6 +395,9 @@ func (mgr *agreementMgr) stop() { } func (mgr *agreementMgr) generateSetting(round uint64) *baRoundSetting { + if setting, exist := mgr.settingCache.Get(round); exist { + return setting.(*baRoundSetting) + } curConfig := mgr.config(round) if curConfig == nil { return nil @@ -399,13 +423,15 @@ func (mgr *agreementMgr) generateSetting(round uint64) *baRoundSetting { return nil } } - return &baRoundSetting{ + setting := &baRoundSetting{ crs: curConfig.crs, dkgSet: dkgSet, round: round, threshold: utils.GetBAThreshold(&types.Config{ NotarySetSize: curConfig.notarySetSize}), } + mgr.settingCache.Add(round, setting) + return setting } func (mgr *agreementMgr) runBA(initRound uint64) { @@ -465,6 +491,7 @@ Loop: } mgr.recv.isNotary = checkRound() mgr.voteFilter = utils.NewVoteFilter() + mgr.voteFilter.Position.Round = currentRound mgr.recv.emptyBlockHashMap = &sync.Map{} if currentRound >= DKGDelayRound && mgr.recv.isNotary { var err error diff --git a/core/agreement.go b/core/agreement.go index a6fb074..cb46771 100644 --- a/core/agreement.go +++ b/core/agreement.go @@ -361,9 +361,6 @@ func (a *agreement) sanityCheck(vote *types.Vote) error { if vote.Type >= types.MaxVoteType { return ErrInvalidVote } - if _, exist := a.notarySet[vote.ProposerID]; !exist { - return ErrNotInNotarySet - } ok, err := utils.VerifyVoteSignature(vote) if err != nil { return err @@ -371,6 +368,10 @@ func (a *agreement) sanityCheck(vote *types.Vote) error { if !ok { return ErrIncorrectVoteSignature } + if vote.Position.Round != a.agreementID().Round { + // TODO(jimmy): maybe we can verify partial signature at agreement-mgr. + return nil + } if !a.data.recv.VerifyPartialSignature(vote) { return ErrIncorrectVotePartialSignature } @@ -412,7 +413,7 @@ func (a *agreement) updateFilter(filter *utils.VoteFilter) { filter.Confirm = a.hasOutput filter.LockIter = a.data.lockIter filter.Period = a.data.period - filter.Height = a.agreementID().Height + filter.Position.Height = a.agreementID().Height } // processVote is the entry point for processing Vote. @@ -426,8 +427,8 @@ func (a *agreement) processVote(vote *types.Vote) error { // Agreement module has stopped. if isStop(aID) { - // Hacky way to not drop first votes for genesis height. - if vote.Position.Height == types.GenesisHeight { + // Hacky way to not drop first votes when round just begins. + if vote.Position.Round == aID.Round { a.pendingVote = append(a.pendingVote, pendingVote{ vote: vote, receivedTime: time.Now().UTC(), diff --git a/core/utils/vote-filter.go b/core/utils/vote-filter.go index 2fc18bb..446d88a 100644 --- a/core/utils/vote-filter.go +++ b/core/utils/vote-filter.go @@ -25,7 +25,7 @@ import ( // To maximize performance, this structure is not thread-safe and will never be. type VoteFilter struct { Voted map[types.VoteHeader]struct{} - Height uint64 + Position types.Position LockIter uint64 Period uint64 Confirm bool @@ -43,9 +43,9 @@ func (vf *VoteFilter) Filter(vote *types.Vote) bool { if vote.Type == types.VoteInit { return true } - if vote.Position.Height < vf.Height { + if vote.Position.Older(vf.Position) { return true - } else if vote.Position.Height > vf.Height { + } else if vote.Position.Newer(vf.Position) { // It's impossible to check the vote of other height. return false } diff --git a/core/utils/vote-filter_test.go b/core/utils/vote-filter_test.go index 443efff..4e7a7b9 100644 --- a/core/utils/vote-filter_test.go +++ b/core/utils/vote-filter_test.go @@ -32,22 +32,22 @@ type VoteFilterTestSuite struct { func (s *VoteFilterTestSuite) TestFilterVotePass() { filter := NewVoteFilter() - filter.Height = uint64(6) + filter.Position.Height = uint64(6) filter.Period = uint64(3) filter.LockIter = uint64(3) // Pass with higher Height. vote := types.NewVote(types.VotePreCom, common.NewRandomHash(), uint64(1)) - vote.Position.Height = filter.Height + 1 + vote.Position.Height = filter.Position.Height + 1 s.Require().False(filter.Filter(vote)) // Pass with VotePreCom. vote = types.NewVote(types.VotePreCom, common.NewRandomHash(), filter.LockIter) - vote.Position.Height = filter.Height + vote.Position.Height = filter.Position.Height s.Require().False(filter.Filter(vote)) // Pass with VoteCom. vote = types.NewVote(types.VoteCom, common.NewRandomHash(), filter.Period) - vote.Position.Height = filter.Height + vote.Position.Height = filter.Position.Height s.Require().False(filter.Filter(vote)) vote.Period-- s.Require().False(filter.Filter(vote)) @@ -81,9 +81,9 @@ func (s *VoteFilterTestSuite) TestFilterConfirm() { } func (s *VoteFilterTestSuite) TestFilterLowerHeight() { filter := NewVoteFilter() - filter.Height = uint64(10) + filter.Position.Height = uint64(10) vote := types.NewVote(types.VoteCom, common.NewRandomHash(), uint64(1)) - vote.Position.Height = filter.Height - 1 + vote.Position.Height = filter.Position.Height - 1 s.True(filter.Filter(vote)) } diff --git a/integration_test/consensus_test.go b/integration_test/consensus_test.go index 05d7c44..bf2970a 100644 --- a/integration_test/consensus_test.go +++ b/integration_test/consensus_test.go @@ -252,6 +252,11 @@ func (s *ConsensusTestSuite) syncBlocksWithSomeNode( } func (s *ConsensusTestSuite) TestSimple() { + if testing.Short() { + // All other tests will cover this basic case. To speed up CI process, + // ignore this test in short mode. + return + } // The simplest test case: // - Node set is equals to DKG set and notary set for each chain in each // round. |