From bac7831e2de3ea855a34b3db7f5854e4567ca40d Mon Sep 17 00:00:00 2001 From: Pastoh Date: Tue, 28 Feb 2023 16:57:31 -0300 Subject: [PATCH] Remove istanbul.v1 code (#1999) * Remove v1 types * Add backlog tests (remove v1) * Remove v1 references from istanbul core core.go * Remove v1 code from handler & handler_test * remove v1 code from preprepare files * remove v1 code from roundchange files * Remove most of v1 code * Remove isConsensusFork * Remove all mentions to V2Block * Remove useless testing suite function --- cmd/geth/config.go | 3 - cmd/geth/main.go | 1 - cmd/utils/flags.go | 6 - consensus/istanbul/config.go | 6 - consensus/istanbul/core/backlog.go | 7 +- consensus/istanbul/core/backlog_test.go | 63 +- consensus/istanbul/core/commit_test.go | 30 +- consensus/istanbul/core/core.go | 79 +-- consensus/istanbul/core/errors.go | 2 - consensus/istanbul/core/handler.go | 5 - consensus/istanbul/core/handler_test.go | 14 +- consensus/istanbul/core/prepare_test.go | 36 +- consensus/istanbul/core/prepare_v2_test.go | 2 +- consensus/istanbul/core/preprepare.go | 147 ----- consensus/istanbul/core/preprepare_test.go | 572 ----------------- consensus/istanbul/core/preprepare_v2.go | 17 +- consensus/istanbul/core/preprepare_v2_test.go | 36 +- consensus/istanbul/core/request.go | 6 +- consensus/istanbul/core/request_test.go | 4 +- consensus/istanbul/core/roundchange.go | 203 ------ consensus/istanbul/core/roundchange_test.go | 577 ------------------ consensus/istanbul/core/roundchange_v2.go | 41 +- .../istanbul/core/roundchange_v2_test.go | 12 - consensus/istanbul/core/roundchangeset.go | 207 ------- consensus/istanbul/core/roundstate.go | 105 +--- consensus/istanbul/core/roundstate_db.go | 5 +- consensus/istanbul/core/roundstate_db_test.go | 10 +- .../core/roundstate_save_decorator.go | 10 +- consensus/istanbul/core/roundstate_test.go | 24 +- consensus/istanbul/core/testbackend_test.go | 35 -- consensus/istanbul/core/testutils_test.go | 22 +- consensus/istanbul/core/types_test.go | 6 +- consensus/istanbul/types.go | 304 +-------- consensus/istanbul/types_test.go | 128 ---- e2e_test/e2e_test.go | 12 - eth/ethconfig/config.go | 6 - eth/ethconfig/gen_config.go | 12 +- params/config.go | 5 - 38 files changed, 170 insertions(+), 2590 deletions(-) delete mode 100644 consensus/istanbul/core/preprepare.go delete mode 100644 consensus/istanbul/core/preprepare_test.go delete mode 100644 consensus/istanbul/core/roundchange.go delete mode 100644 consensus/istanbul/core/roundchange_test.go delete mode 100644 consensus/istanbul/core/roundchangeset.go diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 55c926b789..2a7e4ef473 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -161,9 +161,6 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { if ctx.GlobalIsSet(utils.OverrideEHardforkFlag.Name) { cfg.Eth.OverrideEHardfork = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideEHardforkFlag.Name)) } - if ctx.GlobalIsSet(utils.OverrideV2IstanbulForkFlag.Name) { - cfg.Eth.OverrideV2IstanbulFork = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideV2IstanbulForkFlag.Name)) - } backend, _ := utils.RegisterEthService(stack, &cfg.Eth) // Configure GraphQL if requested diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 0a63250322..f9eed133e4 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -73,7 +73,6 @@ var ( utils.USBFlag, // utils.SmartCardDaemonPathFlag, utils.OverrideEHardforkFlag, - utils.OverrideV2IstanbulForkFlag, utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, utils.TxPoolJournalFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 1fc747c940..24fce63abe 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -241,12 +241,6 @@ var ( Usage: "Manually specify the espresso fork block, overriding the bundled setting", } - // V2 Istanbul fork activation overrides - OverrideV2IstanbulForkFlag = cli.Uint64Flag{ - Name: "override.v2istanbul", - Usage: "Manually specify the v2 istanbul consensus fork block, overriding the bundled setting", - } - BloomFilterSizeFlag = cli.Uint64Flag{ Name: "bloomfilter.size", Usage: "Megabytes of memory allocated to bloom-filter for pruning", diff --git a/consensus/istanbul/config.go b/consensus/istanbul/config.go index dda609aab9..c521f3ef84 100644 --- a/consensus/istanbul/config.go +++ b/consensus/istanbul/config.go @@ -18,7 +18,6 @@ package istanbul import ( "fmt" - "math/big" "github.com/celo-org/celo-blockchain/common" "github.com/celo-org/celo-blockchain/p2p/enode" @@ -69,8 +68,6 @@ type Config struct { AnnounceAggressiveQueryEnodeGossipOnEnablement bool `toml:",omitempty"` // Specifies if this node should aggressively query enodes on announce enablement AnnounceAdditionalValidatorsToGossip int64 `toml:",omitempty"` // Specifies the number of additional non-elected validators to gossip an announce - V2Block *big.Int `toml:",omitempty"` // Activation block for the V2 istanbul consensus fork (nil = no fork, 0 = already activated) - // Load test config LoadTestCSVFile string `toml:",omitempty"` // If non-empty, specifies the file to write out csv metrics about the block production cycle to. } @@ -107,9 +104,6 @@ var DefaultConfig = &Config{ //ApplyParamsChainConfigToConfig applies the istanbul config values from params.chainConfig to the istanbul.Config config func ApplyParamsChainConfigToConfig(chainConfig *params.ChainConfig, config *Config) error { - if chainConfig.Istanbul.V2Block != nil { - config.V2Block = chainConfig.Istanbul.V2Block - } if chainConfig.Istanbul.Epoch != 0 { if chainConfig.Istanbul.Epoch < MinEpochSize { return fmt.Errorf("istanbul.Epoch must be greater than %d", MinEpochSize-1) diff --git a/consensus/istanbul/core/backlog.go b/consensus/istanbul/core/backlog.go index c9755904f9..b8b11a71fb 100644 --- a/consensus/istanbul/core/backlog.go +++ b/consensus/istanbul/core/backlog.go @@ -30,9 +30,8 @@ import ( var ( // msgPriority is defined for calculating processing priority to speedup consensus - // istanbul.MsgPreprepare > istanbul.MsgCommit > istanbul.MsgPrepare + // istanbul.MsgPreprepareV2 > istanbul.MsgCommit > istanbul.MsgPrepare msgPriority = map[uint64]int{ - istanbul.MsgPreprepare: 1, istanbul.MsgPreprepareV2: 1, istanbul.MsgCommit: 2, istanbul.MsgPrepare: 3, @@ -328,16 +327,12 @@ func toPriority(msgCode uint64, view *istanbul.View) int64 { func extractMessageView(msg *istanbul.Message) *istanbul.View { switch msg.Code { - case istanbul.MsgPreprepare: - return msg.Preprepare().View case istanbul.MsgPreprepareV2: return msg.PreprepareV2().View case istanbul.MsgPrepare: return msg.Prepare().View case istanbul.MsgCommit: return msg.Commit().Subject.View - case istanbul.MsgRoundChange: - return msg.RoundChange().View case istanbul.MsgRoundChangeV2: return &msg.RoundChangeV2().Request.View default: diff --git a/consensus/istanbul/core/backlog_test.go b/consensus/istanbul/core/backlog_test.go index 7bf1852fcd..8deec43408 100644 --- a/consensus/istanbul/core/backlog_test.go +++ b/consensus/istanbul/core/backlog_test.go @@ -43,18 +43,18 @@ func TestCheckMessage(t *testing.T) { current: newRoundState(&istanbul.View{ Sequence: big.NewInt(2), Round: big.NewInt(2), - }, valSet, valSet.GetByIndex(0), false), + }, valSet, valSet.GetByIndex(0)), } t.Run("invalid view format", func(t *testing.T) { - err := c.checkMessage(istanbul.MsgPreprepare, nil) + err := c.checkMessage(istanbul.MsgPreprepareV2, nil) if err != errInvalidMessage { t.Errorf("error mismatch: have %v, want %v", err, errInvalidMessage) } }) testStates := []State{StateAcceptRequest, StatePreprepared, StatePrepared, StateCommitted, StateWaitingForNewRound} - testCodes := []uint64{istanbul.MsgPreprepare, istanbul.MsgPrepare, istanbul.MsgCommit, istanbul.MsgRoundChange} + testCodes := []uint64{istanbul.MsgPreprepareV2, istanbul.MsgPrepare, istanbul.MsgCommit, istanbul.MsgRoundChangeV2} // accept Commits from sequence, round matching LastSubject t.Run("Rejects all other older rounds", func(t *testing.T) { @@ -116,7 +116,7 @@ func TestCheckMessage(t *testing.T) { for _, testCode := range testCodes { c.current.(*roundStateImpl).state = testState err := c.checkMessage(testCode, v) - if testCode == istanbul.MsgRoundChange { + if testCode == istanbul.MsgRoundChangeV2 { if err != nil { t.Errorf("error mismatch: have %v, want nil", err) } @@ -133,11 +133,11 @@ func TestCheckMessage(t *testing.T) { for _, testCode := range testCodes { err := c.checkMessage(testCode, v) - if testCode == istanbul.MsgRoundChange { + if testCode == istanbul.MsgRoundChangeV2 { if err != nil { t.Errorf("error mismatch: have %v, want nil", err) } - } else if testCode == istanbul.MsgPreprepare { + } else if testCode == istanbul.MsgPreprepareV2 { if err != nil { t.Errorf("error mismatch: have %v, want nil", err) } @@ -154,7 +154,7 @@ func TestCheckMessage(t *testing.T) { c.current.(*roundStateImpl).state = StatePreprepared for _, testCode := range testCodes { err := c.checkMessage(testCode, v) - if testCode == istanbul.MsgRoundChange { + if testCode == istanbul.MsgRoundChangeV2 { if err != nil { t.Errorf("error mismatch: have %v, want nil", err) } @@ -169,7 +169,7 @@ func TestCheckMessage(t *testing.T) { c.current.(*roundStateImpl).state = StatePrepared for _, testCode := range testCodes { err := c.checkMessage(testCode, v) - if testCode == istanbul.MsgRoundChange { + if testCode == istanbul.MsgRoundChangeV2 { if err != nil { t.Errorf("error mismatch: have %v, want nil", err) } @@ -184,7 +184,7 @@ func TestCheckMessage(t *testing.T) { c.current.(*roundStateImpl).state = StateCommitted for _, testCode := range testCodes { err := c.checkMessage(testCode, v) - if testCode == istanbul.MsgRoundChange { + if testCode == istanbul.MsgRoundChangeV2 { if err != nil { t.Errorf("error mismatch: have %v, want nil", err) } @@ -199,7 +199,7 @@ func TestCheckMessage(t *testing.T) { c.current.(*roundStateImpl).state = StateWaitingForNewRound for _, testCode := range testCodes { err := c.checkMessage(testCode, v) - if testCode == istanbul.MsgRoundChange || testCode == istanbul.MsgPreprepare { + if testCode == istanbul.MsgRoundChangeV2 || testCode == istanbul.MsgPreprepareV2 { if err != nil { t.Errorf("error mismatch: have %v, want nil", err) } @@ -231,8 +231,8 @@ func TestStoreBacklog(t *testing.T) { p1 := validator.New(common.BytesToAddress([]byte("12345667890")), blscrypto.SerializedPublicKey{}) p2 := validator.New(common.BytesToAddress([]byte("47324349949")), blscrypto.SerializedPublicKey{}) - mPreprepare := istanbul.NewPreprepareMessage( - &istanbul.Preprepare{View: v10, Proposal: makeBlock(10)}, + mPreprepare := istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{View: v10, Proposal: makeBlock(10)}, p1.Address(), ) backlog.store(mPreprepare) @@ -246,8 +246,8 @@ func TestStoreBacklog(t *testing.T) { &istanbul.Subject{View: v10, Digest: common.BytesToHash([]byte("1234567890"))}, p1.Address(), ) - mPreprepare2 := istanbul.NewPreprepareMessage( - &istanbul.Preprepare{View: v11, Proposal: makeBlock(11)}, + mPreprepare2 := istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{View: v11, Proposal: makeBlock(11)}, p2.Address(), ) @@ -300,8 +300,8 @@ func TestClearBacklogForSequence(t *testing.T) { // The backlog's state is sequence number 1, round 0. Store future messages with sequence number 2 p1 := validator.New(common.BytesToAddress([]byte("12345667890")), blscrypto.SerializedPublicKey{}) - mPreprepare := istanbul.NewPreprepareMessage( - &istanbul.Preprepare{ + mPreprepare := istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{ View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(2)}, Proposal: makeBlock(2), }, @@ -360,15 +360,21 @@ func TestProcessFutureBacklog(t *testing.T) { backlog.store(mFuture) // push a message from the past and check we expire it - mPast := istanbul.NewRoundChangeMessage(&istanbul.RoundChange{ - View: &istanbul.View{ - Round: big.NewInt(0), - Sequence: oldSequence, - }, - PreparedCertificate: istanbul.PreparedCertificate{ - Proposal: makeBlock(0), + addr := valSet.GetByIndex(1).Address() + roundChangeV2 := &istanbul.RoundChangeV2{ + Request: istanbul.RoundChangeRequest{ + Address: addr, + View: istanbul.View{ + Round: big.NewInt(0), + Sequence: oldSequence, + }, + PreparedCertificateV2: istanbul.PCV2FromPCV1(istanbul.PreparedCertificate{ + Proposal: makeBlock(0), + }), }, - }, valSet.GetByIndex(1).Address()) + } + // No need to sign the RoundChangeRequest for this test + mPast := istanbul.NewRoundChangeV2Message(roundChangeV2, addr) backlog.store(mPast) @@ -404,8 +410,8 @@ func TestProcessBacklog(t *testing.T) { address := common.BytesToAddress([]byte("0xce10ce10")) msgs := []*istanbul.Message{ - istanbul.NewPreprepareMessage( - &istanbul.Preprepare{View: v, Proposal: makeBlock(1)}, + istanbul.NewPreprepareV2Message( + &istanbul.PreprepareV2{View: v, Proposal: makeBlock(1)}, address, ), istanbul.NewPrepareMessage(subject, address), @@ -413,8 +419,9 @@ func TestProcessBacklog(t *testing.T) { &istanbul.CommittedSubject{Subject: subject, CommittedSeal: []byte{0x63, 0x65, 0x6C, 0x6F}}, address, ), - istanbul.NewRoundChangeMessage( - &istanbul.RoundChange{View: v, PreparedCertificate: istanbul.EmptyPreparedCertificate()}, + istanbul.NewRoundChangeV2Message( + &istanbul.RoundChangeV2{ + Request: istanbul.RoundChangeRequest{View: *v}, PreparedProposal: makeBlock(1)}, address, ), } diff --git a/consensus/istanbul/core/commit_test.go b/consensus/istanbul/core/commit_test.go index a5f5ea18c0..7fcc3dad67 100644 --- a/consensus/istanbul/core/commit_test.go +++ b/consensus/istanbul/core/commit_test.go @@ -56,7 +56,7 @@ func TestHandleCommit(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) // same view as the expected one to everyone - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) @@ -80,13 +80,13 @@ func TestHandleCommit(t *testing.T) { c := backend.engine.(*core) if i == 0 { // replica 0 is the proposer - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) c.current.(*roundStateImpl).state = StatePreprepared } else { - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), // proposal from 1 round in the future @@ -111,13 +111,13 @@ func TestHandleCommit(t *testing.T) { if i == 0 { // replica 0 is the proposer - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) c.current.(*roundStateImpl).state = StatePreprepared } else { - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), // we're 2 blocks before so this is indeed a @@ -141,7 +141,7 @@ func TestHandleCommit(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: proposal.Number(), @@ -174,13 +174,13 @@ func TestHandleCommit(t *testing.T) { c := backend.engine.(*core) if i == 0 { // replica 0 is the proposer - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) c.current.(*roundStateImpl).state = StatePrepared } else { - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(1), Sequence: big.NewInt(0).Sub(proposal.Number(), common.Big1), @@ -297,7 +297,7 @@ func TestVerifyCommit(t *testing.T) { Digest: newTestProposal().Hash(), }, }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, valSet, ), @@ -311,7 +311,7 @@ func TestVerifyCommit(t *testing.T) { Digest: newTestProposal().Hash(), }, }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, valSet, ), @@ -325,7 +325,7 @@ func TestVerifyCommit(t *testing.T) { Digest: common.BytesToHash([]byte("1234567890")), }, }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, valSet, ), @@ -339,7 +339,7 @@ func TestVerifyCommit(t *testing.T) { Digest: newTestProposal().Hash(), }, }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, valSet, ), @@ -353,7 +353,7 @@ func TestVerifyCommit(t *testing.T) { Digest: newTestProposal().Hash(), }, }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, valSet, ), @@ -367,7 +367,7 @@ func TestVerifyCommit(t *testing.T) { Digest: newTestProposal().Hash(), }, }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, valSet, ), @@ -404,7 +404,7 @@ func BenchmarkHandleCommit(b *testing.B) { for i, backend := range sys.backends { c := backend.engine.(*core) // same view as the expected one to everyone - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) diff --git a/consensus/istanbul/core/core.go b/consensus/istanbul/core/core.go index 7116763572..13c35ce3d7 100644 --- a/consensus/istanbul/core/core.go +++ b/consensus/istanbul/core/core.go @@ -131,7 +131,6 @@ type core struct { currentMu sync.RWMutex handlerWg *sync.WaitGroup - roundChangeSet *roundChangeSet roundChangeSetV2 *roundChangeSetV2 pendingRequests *prque.Prque @@ -457,42 +456,6 @@ func GetAggregatedEpochValidatorSetSeal(blockNumber, epoch uint64, seals Message return types.IstanbulEpochValidatorSetSeal{Bitmap: bitmap, Signature: asig}, nil } -// getPreprepareWithRoundChangeCertificate Generates the next preprepare request and associated round change certificate -func (c *core) getPreprepareWithRoundChangeCertificate(round *big.Int) (*istanbul.Request, istanbul.RoundChangeCertificate, error) { - logger := c.newLogger("func", "getPreprepareWithRoundChangeCertificate", "for_round", round) - - roundChangeCertificate, err := c.roundChangeSet.getCertificate(round, c.current.ValidatorSet().MinQuorumSize()) - if err != nil { - return &istanbul.Request{}, istanbul.RoundChangeCertificate{}, err - } - // Start with pending request - request := c.current.PendingRequest() - // Search for a valid request in round change messages. - // The proposal must come from the prepared certificate with the highest round number. - // All prepared certificates from the same round are assumed to be the same proposal or no proposal (guaranteed by quorum intersection) - maxRound := big.NewInt(-1) - for _, message := range roundChangeCertificate.RoundChangeMessages { - roundChange := message.RoundChange() - if !roundChange.HasPreparedCertificate() { - continue - } - - preparedCertificateView, err := c.getViewFromVerifiedPreparedCertificate(roundChange.PreparedCertificate) - if err != nil { - logger.Error("Unexpected: could not verify a previously received PreparedCertificate message", "src_m", message) - return &istanbul.Request{}, istanbul.RoundChangeCertificate{}, err - } - - if preparedCertificateView != nil && preparedCertificateView.Round.Cmp(maxRound) > 0 { - maxRound = preparedCertificateView.Round - request = &istanbul.Request{ - Proposal: roundChange.PreparedCertificate.Proposal, - } - } - } - return request, roundChangeCertificate, nil -} - // getPreprepareWithRoundChangeCertificateV2 Generates the next preprepare request and associated round change certificate func (c *core) getPreprepareWithRoundChangeCertificateV2(round *big.Int) (*istanbul.Request, istanbul.RoundChangeCertificateV2, error) { logger := c.newLogger("func", "getPreprepareWithRoundChangeCertificate", "for_round", round) @@ -560,7 +523,6 @@ func (c *core) startNewRound(round *big.Int, propose bool) error { var err error var request *istanbul.Request - var roundChangeCertificate istanbul.RoundChangeCertificate var roundChangeCertificateV2 istanbul.RoundChangeCertificateV2 // startNewRound is called from two different places: handleRoundChange and handleRoundChangeCertificate. @@ -574,19 +536,12 @@ func (c *core) startNewRound(round *big.Int, propose bool) error { // therefore it is not possible to create the RCC_V2 by using the same RoundChangeSet // The solution was to modify completely how the roundChangeSet works, // but since the co-existence of V1 and V2 are temporary, the propose flag should be enough. + // If necessary, removal of this flag should be done after successfully removing al v1 code. if c.address == nextProposer.Address() && propose { - if c.isConsensusFork(newView.Sequence) { - request, roundChangeCertificateV2, err = c.getPreprepareWithRoundChangeCertificateV2(round) - if err != nil { - logger.Error("Unable to produce round change certificate v2", "err", err, "new_round", round) - return nil - } - } else { - request, roundChangeCertificate, err = c.getPreprepareWithRoundChangeCertificate(round) - if err != nil { - logger.Error("Unable to produce round change certificate", "err", err, "new_round", round) - return nil - } + request, roundChangeCertificateV2, err = c.getPreprepareWithRoundChangeCertificateV2(round) + if err != nil { + logger.Error("Unable to produce round change certificate v2", "err", err, "new_round", round) + return nil } } @@ -598,11 +553,7 @@ func (c *core) startNewRound(round *big.Int, propose bool) error { c.backlog.updateState(c.current.View(), c.current.State()) if c.isProposer() && request != nil { - if c.isConsensusFork(newView.Sequence) { - c.sendPreprepareV2(request, roundChangeCertificateV2) - } else { - c.sendPreprepare(request, roundChangeCertificate) - } + c.sendPreprepareV2(request, roundChangeCertificateV2) } c.resetRoundChangeTimer() @@ -636,7 +587,6 @@ func (c *core) startNewSequence() error { Round: new(big.Int).Set(common.Big0), } valSet := c.backend.Validators(headBlock) - c.roundChangeSet = newRoundChangeSet(valSet) c.roundChangeSetV2 = newRoundChangeSetV2(valSet) // Inform the backend that a new sequence has started & bail if the backed stopped the core @@ -728,10 +678,10 @@ func (c *core) createRoundState() (RoundState, error) { } valSet := c.backend.Validators(headBlock) proposer := c.selectProposer(valSet, headAuthor, 0) - roundState = newRoundState(&istanbul.View{Sequence: nextSequence, Round: common.Big0}, valSet, proposer, c.isConsensusFork(nextSequence)) + roundState = newRoundState(&istanbul.View{Sequence: nextSequence, Round: common.Big0}, valSet, proposer) } else { logger.Info("Retrieving stored RoundState", "stored_view", lastStoredView, "requested_seq", nextSequence) - roundState, err = c.rsdb.GetRoundStateFor(lastStoredView, c.isConsensusFork(lastStoredView.Sequence)) + roundState, err = c.rsdb.GetRoundStateFor(lastStoredView) if err != nil { logger.Error("Failed to fetch lastStoredRoundState", "err", err) @@ -761,8 +711,7 @@ func (c *core) resetRoundState(view *istanbul.View, validatorSet istanbul.Valida headBlock := c.backend.GetCurrentHeadBlock() newParentCommits = newMessageSet(c.backend.ParentBlockValidators(headBlock)) } - return c.current.StartNewSequence(view.Sequence, validatorSet, nextProposer, newParentCommits, c.isConsensusFork(view.Sequence)) - + return c.current.StartNewSequence(view.Sequence, validatorSet, nextProposer, newParentCommits) } func (c *core) isProposer() bool { @@ -928,13 +877,3 @@ func (c *core) verifyProposal(proposal istanbul.Proposal) (time.Duration, error) return duration, err } } - -func (c *core) isConsensusFork(blockNumber *big.Int) bool { - if c.config.V2Block == nil { - // Default: no fork - return false - } - // Assume blockNumber is never nil - // V2 Activated for blockNumber if greater or equal than V2Block - return c.config.V2Block.Cmp(blockNumber) <= 0 -} diff --git a/consensus/istanbul/core/errors.go b/consensus/istanbul/core/errors.go index 4e6b11dbf5..04f0bd221c 100644 --- a/consensus/istanbul/core/errors.go +++ b/consensus/istanbul/core/errors.go @@ -64,8 +64,6 @@ var ( errInvalidRoundChangeCertificateMsgSignature = errors.New("invalid signature in ROUND CHANGE certificate") // errInvalidRoundChangeCertificateDuplicate is returned when the ROUND CHANGE certificate has multiple ROUND CHANGE messages from the same validator. errInvalidRoundChangeCertificateDuplicate = errors.New("duplicate message in ROUND CHANGE certificate") - // errInvalidRoundChangeCertificateMsgCode is returned when the ROUND CHANGE certificate contains a message with the wrong code. - errInvalidRoundChangeCertificateMsgCode = errors.New("non-ROUND CHANGE message in ROUND CHANGE certificate") // errInvalidRoundChangeCertificateMsgView is returned when the ROUND CHANGE certificate contains a message for the wrong view errInvalidRoundChangeCertificateMsgView = errors.New("message in ROUND CHANGE certificate for wrong view") diff --git a/consensus/istanbul/core/handler.go b/consensus/istanbul/core/handler.go index 3482e82e15..54ad81d9fc 100644 --- a/consensus/istanbul/core/handler.go +++ b/consensus/istanbul/core/handler.go @@ -38,7 +38,6 @@ func (c *core) Start() error { } c.current = roundState - c.roundChangeSet = newRoundChangeSet(c.current.ValidatorSet()) c.roundChangeSetV2 = newRoundChangeSetV2(c.current.ValidatorSet()) // Reset the Round Change timer for the current round to timeout. @@ -205,16 +204,12 @@ func (c *core) handleCheckedMsg(msg *istanbul.Message, src istanbul.Validator) e } switch msg.Code { - case istanbul.MsgPreprepare: - return catchFutureMessages(c.handlePreprepare(msg)) case istanbul.MsgPreprepareV2: return catchFutureMessages(c.handlePreprepareV2(msg)) case istanbul.MsgPrepare: return catchFutureMessages(c.handlePrepare(msg)) case istanbul.MsgCommit: return catchFutureMessages(c.handleCommit(msg)) - case istanbul.MsgRoundChange: - return catchFutureMessages(c.handleRoundChange(msg)) case istanbul.MsgRoundChangeV2: return catchFutureMessages(c.handleRoundChangeV2(msg)) default: diff --git a/consensus/istanbul/core/handler_test.go b/consensus/istanbul/core/handler_test.go index bf8ac5a883..f224ae39eb 100644 --- a/consensus/istanbul/core/handler_test.go +++ b/consensus/istanbul/core/handler_test.go @@ -47,7 +47,7 @@ func TestMalformedMessageDecoding(t *testing.T) { }, v0.Address()) // Prepare message but preprepare message code - m.Code = istanbul.MsgPreprepare + m.Code = istanbul.MsgPreprepareV2 payload, err := m.Payload() require.NoError(t, err) @@ -56,7 +56,7 @@ func TestMalformedMessageDecoding(t *testing.T) { err = msg.FromPayload(payload, r0.validateFn) assert.Error(t, err) - m = istanbul.NewPreprepareMessage(&istanbul.Preprepare{ + m = istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ View: &istanbul.View{ Sequence: big.NewInt(0), Round: big.NewInt(0), @@ -74,7 +74,7 @@ func TestMalformedMessageDecoding(t *testing.T) { err = msg.FromPayload(payload, r0.validateFn) assert.Error(t, err) - m = istanbul.NewPreprepareMessage(&istanbul.Preprepare{ + m = istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ View: &istanbul.View{ Sequence: big.NewInt(0), Round: big.NewInt(0), @@ -92,7 +92,7 @@ func TestMalformedMessageDecoding(t *testing.T) { err = msg.FromPayload(payload, r0.validateFn) assert.Error(t, err) - m = istanbul.NewPreprepareMessage(&istanbul.Preprepare{ + m = istanbul.NewPreprepareV2Message(&istanbul.PreprepareV2{ View: &istanbul.View{ Sequence: big.NewInt(0), Round: big.NewInt(0), @@ -125,7 +125,7 @@ func BenchmarkHandleMsg(b *testing.B) { for i, backend := range sys.backends { c := backend.engine.(*core) - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1), @@ -183,7 +183,7 @@ func newInitializedTestSystem(b *testing.B, useRoundStateDB bool) *testSystem { b.Errorf("Failed to create rsdb: %v", err) } - c.current = withSavingDecorator(rsdb, newTestRoundState( + c.current = withSavingDecorator(rsdb, newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1), @@ -196,7 +196,7 @@ func newInitializedTestSystem(b *testing.B, useRoundStateDB bool) *testSystem { c.current.(*rsSaveDecorator).rs.(*roundStateImpl).state = StatePreprepared } } else { - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1), diff --git a/consensus/istanbul/core/prepare_test.go b/consensus/istanbul/core/prepare_test.go index 5ca2da9692..d3cc6af560 100644 --- a/consensus/istanbul/core/prepare_test.go +++ b/consensus/istanbul/core/prepare_test.go @@ -81,7 +81,7 @@ func TestVerifyPreparedCertificate(t *testing.T) { "Invalid PREPARED certificate, includes preprepare message", func() istanbul.PreparedCertificate { preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal) - testInvalidMsg, _ := sys.backends[0].getRoundChangeMessage(view, sys.getPreparedCertificate(t, []istanbul.View{view}, proposal)) + testInvalidMsg, _ := sys.backends[0].getRoundChangeV2Message(view, sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal), proposal) preparedCertificate.PrepareOrCommitMessages[0] = testInvalidMsg return preparedCertificate }(), @@ -169,7 +169,7 @@ func TestHandlePrepare(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1), @@ -202,7 +202,7 @@ func TestHandlePrepare(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1), @@ -236,7 +236,7 @@ func TestHandlePrepare(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1), @@ -263,13 +263,13 @@ func TestHandlePrepare(t *testing.T) { c := backend.engine.(*core) if i == 0 { // replica 0 is the proposer - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) c.current.(*roundStateImpl).state = StatePreprepared } else { - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(2), Sequence: big.NewInt(3), @@ -291,13 +291,13 @@ func TestHandlePrepare(t *testing.T) { c := backend.engine.(*core) if i == 0 { // replica 0 is the proposer - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) c.current.(*roundStateImpl).state = StatePreprepared } else { - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(0), @@ -319,13 +319,13 @@ func TestHandlePrepare(t *testing.T) { c := backend.engine.(*core) if i == 0 { // replica 0 is the proposer - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) c.current.(*roundStateImpl).state = StatePreprepared } else { - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1)}, @@ -347,7 +347,7 @@ func TestHandlePrepare(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - c.current = newTestRoundState( + c.current = newTestRoundStateV2( expectedSubject.View, backend.peers, ) @@ -455,7 +455,7 @@ func TestVerifyPrepare(t *testing.T) { View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, Digest: newTestProposal().Hash(), }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, valSet, ), @@ -467,7 +467,7 @@ func TestVerifyPrepare(t *testing.T) { View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, Digest: newTestProposal().Hash(), }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, valSet, ), @@ -479,7 +479,7 @@ func TestVerifyPrepare(t *testing.T) { View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, Digest: common.BytesToHash([]byte("1234567890")), }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, valSet, ), @@ -491,7 +491,7 @@ func TestVerifyPrepare(t *testing.T) { View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, Digest: newTestProposal().Hash(), }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, valSet, ), @@ -503,7 +503,7 @@ func TestVerifyPrepare(t *testing.T) { View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, Digest: newTestProposal().Hash(), }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, valSet, ), @@ -515,7 +515,7 @@ func TestVerifyPrepare(t *testing.T) { View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, Digest: newTestProposal().Hash(), }, - roundState: newTestRoundState( + roundState: newTestRoundStateV2( &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, valSet, ), @@ -544,7 +544,7 @@ func BenchmarkHandlePrepare(b *testing.B) { for i, backend := range sys.backends { c := backend.engine.(*core) - c.current = newTestRoundState( + c.current = newTestRoundStateV2( &istanbul.View{ Round: big.NewInt(0), Sequence: big.NewInt(1), diff --git a/consensus/istanbul/core/prepare_v2_test.go b/consensus/istanbul/core/prepare_v2_test.go index 7956e5a2f4..f7ecc55282 100644 --- a/consensus/istanbul/core/prepare_v2_test.go +++ b/consensus/istanbul/core/prepare_v2_test.go @@ -85,7 +85,7 @@ func TestVerifyPreparedCertificateV2(t *testing.T) { "Invalid PREPARED certificate, includes preprepare message", func() istanbul.PreparedCertificateV2 { preparedCertificate := sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal) - testInvalidMsg, _ := sys.backends[0].getRoundChangeMessage(view, sys.getPreparedCertificate(t, []istanbul.View{view}, proposal)) + testInvalidMsg, _ := sys.backends[0].getRoundChangeV2Message(view, sys.getPreparedCertificateV2(t, []istanbul.View{view}, proposal), proposal) preparedCertificate.PrepareOrCommitMessages[0] = testInvalidMsg return preparedCertificate }(), diff --git a/consensus/istanbul/core/preprepare.go b/consensus/istanbul/core/preprepare.go deleted file mode 100644 index c1ab885b24..0000000000 --- a/consensus/istanbul/core/preprepare.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "errors" - "time" - - "github.com/celo-org/celo-blockchain/common" - "github.com/celo-org/celo-blockchain/consensus" - "github.com/celo-org/celo-blockchain/consensus/istanbul" -) - -func (c *core) sendPreprepare(request *istanbul.Request, roundChangeCertificate istanbul.RoundChangeCertificate) { - logger := c.newLogger("func", "sendPreprepare") - - // If I'm the proposer and I have the same sequence with the proposal - if c.current.Sequence().Cmp(request.Proposal.Number()) == 0 && c.isProposer() { - m := istanbul.NewPreprepareMessage(&istanbul.Preprepare{ - View: c.current.View(), - Proposal: request.Proposal, - RoundChangeCertificate: roundChangeCertificate, - }, c.address) - logger.Debug("Sending preprepare", "m", m) - c.broadcast(m) - } -} - -func (c *core) handlePreprepare(msg *istanbul.Message) error { - defer c.handlePrePrepareTimer.UpdateSince(time.Now()) - - logger := c.newLogger("func", "handlePreprepare", "tag", "handleMsg", "from", msg.Address) - logger.Trace("Got preprepare message", "m", msg) - - preprepare := msg.Preprepare() - // Check consensus fork - if c.isConsensusFork(preprepare.View.Sequence) { - logger.Info("Received Preprepare (V1) for forked block sequence", "sequence", preprepare.View.Sequence.Uint64()) - return errors.New("Received Preprepare (V1) for forked block") - } - - logger = logger.New("msg_num", preprepare.Proposal.Number(), "msg_hash", preprepare.Proposal.Hash(), "msg_seq", preprepare.View.Sequence, "msg_round", preprepare.View.Round) - - // Verify that the proposal is for the sequence number of the view we verified. - if preprepare.View.Sequence.Cmp(preprepare.Proposal.Number()) != 0 { - logger.Warn("Received preprepare with invalid block number") - return errInvalidProposal - } - - // Ensure we have the same view with the PREPREPARE message. - if err := c.checkMessage(istanbul.MsgPreprepare, preprepare.View); err != nil { - if err == errOldMessage { - // Get validator set for the given proposal - valSet := c.backend.ParentBlockValidators(preprepare.Proposal) - prevBlockAuthor := c.backend.AuthorForBlock(preprepare.Proposal.Number().Uint64() - 1) - proposer := c.selectProposer(valSet, prevBlockAuthor, preprepare.View.Round.Uint64()) - - // We no longer broadcast a COMMIT if this is a PREPREPARE from the correct proposer for an existing block. - // However, we log a WARN for potential future debugging value. - if proposer.Address() == msg.Address && c.backend.HasBlock(preprepare.Proposal.Hash(), preprepare.Proposal.Number()) { - logger.Warn("Would have sent a commit message for an old block") - return nil - } - } - // Probably shouldn't errFutureMessage as we should have moved to that round in handleRoundChangeCertificate - logger.Trace("Check preprepare failed", "err", err) - return err - } - - // Check proposer is valid for the message's view (this may be a subsequent round) - headBlock, headProposer := c.backend.GetCurrentHeadBlockAndAuthor() - if headBlock == nil { - logger.Error("Could not determine head proposer") - return errNotFromProposer - } - proposerForMsgRound := c.selectProposer(c.current.ValidatorSet(), headProposer, preprepare.View.Round.Uint64()) - if proposerForMsgRound.Address() != msg.Address { - logger.Warn("Ignore preprepare message from non-proposer", "actual_proposer", proposerForMsgRound.Address()) - return errNotFromProposer - } - - // If round > 0, handle the ROUND CHANGE certificate. If round = 0, it should not have a ROUND CHANGE certificate - if preprepare.View.Round.Cmp(common.Big0) > 0 { - if !preprepare.HasRoundChangeCertificate() { - logger.Error("Preprepare for non-zero round did not contain a round change certificate.") - return errMissingRoundChangeCertificate - } - subject := istanbul.Subject{ - View: preprepare.View, - Digest: preprepare.Proposal.Hash(), - } - // This also moves us to the next round if the certificate is valid. - err := c.handleRoundChangeCertificate(subject, preprepare.RoundChangeCertificate) - if err != nil { - logger.Warn("Invalid round change certificate with preprepare.", "err", err) - return err - } - } else if preprepare.HasRoundChangeCertificate() { - logger.Error("Preprepare for round 0 has a round change certificate.") - return errInvalidProposal - } - - // Verify the proposal we received - if duration, err := c.verifyProposal(preprepare.Proposal); err != nil { - logger.Warn("Failed to verify proposal", "err", err, "duration", duration) - // if it's a future block, we will handle it again after the duration - if err == consensus.ErrFutureBlock { - c.stopFuturePreprepareTimer() - c.futurePreprepareTimer = time.AfterFunc(duration, func() { - c.sendEvent(backlogEvent{ - msg: msg, - }) - }) - } - return err - } - - if c.current.State() == StateAcceptRequest { - logger.Trace("Accepted preprepare", "tag", "stateTransition") - c.consensusTimestamp = time.Now() - - err := c.current.TransitionToPreprepared(preprepare) - if err != nil { - return err - } - - // Process Backlog Messages - c.backlog.updateState(c.current.View(), c.current.State()) - c.sendPrepare() - } - - return nil -} diff --git a/consensus/istanbul/core/preprepare_test.go b/consensus/istanbul/core/preprepare_test.go deleted file mode 100644 index f8949d804b..0000000000 --- a/consensus/istanbul/core/preprepare_test.go +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "math/big" - "reflect" - "testing" - - "github.com/celo-org/celo-blockchain/consensus/istanbul" -) - -func newTestPreprepare(v *istanbul.View) *istanbul.Preprepare { - return &istanbul.Preprepare{ - View: v, - Proposal: newTestProposal(), - } -} - -func TestHandlePreprepare(t *testing.T) { - N := uint64(4) // replica 0 is the proposer, it will send messages to others - F := uint64(1) // F does not affect tests - - getRoundState := func(c *core) *roundStateImpl { - return c.current.(*rsSaveDecorator).rs.(*roundStateImpl) - } - - testCases := []struct { - name string - system func() *testSystem - getCert func(*testSystem) istanbul.RoundChangeCertificate - expectedRequest istanbul.Proposal - expectedErr error - existingBlock bool - }{ - { - "normal case", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for _, backend := range sys.backends { - backend.engine.(*core).Start() - } - return sys - }, - func(_ *testSystem) istanbul.RoundChangeCertificate { - return istanbul.RoundChangeCertificate{} - }, - newTestProposal(), - nil, - false, - }, - { - "proposal sequence doesn't match message sequence", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for _, backend := range sys.backends { - backend.engine.(*core).Start() - } - return sys - }, - func(_ *testSystem) istanbul.RoundChangeCertificate { - return istanbul.RoundChangeCertificate{} - }, - makeBlock(3), - errInvalidProposal, - false, - }, - { - "non-proposer", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - // force remove replica 0, let replica 1 be the proposer - sys.backends = sys.backends[1:] - - for i, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - if i != 0 { - // replica 0 is the proposer - getRoundState(c).state = StatePreprepared - } - } - return sys - }, - func(_ *testSystem) istanbul.RoundChangeCertificate { - return istanbul.RoundChangeCertificate{} - }, - makeBlock(1), - errNotFromProposer, - false, - }, - { - "errOldMessage", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for i, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - if i != 0 { - getRoundState(c).state = StatePreprepared - getRoundState(c).sequence = big.NewInt(10) - getRoundState(c).round = big.NewInt(10) - getRoundState(c).desiredRound = getRoundState(c).round - } - } - return sys - }, - func(_ *testSystem) istanbul.RoundChangeCertificate { - return istanbul.RoundChangeCertificate{} - }, - makeBlock(1), - errOldMessage, - false, - }, - { - "test existing block", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for _, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).state = StatePreprepared - getRoundState(c).sequence = big.NewInt(10) - getRoundState(c).round = big.NewInt(10) - getRoundState(c).desiredRound = getRoundState(c).round - } - return sys - }, - func(_ *testSystem) istanbul.RoundChangeCertificate { - return istanbul.RoundChangeCertificate{} - }, - // In the method testbackend_test.go:HasBlockMatching(), it will return true if the proposal's block number == 5 - makeBlock(5), - nil, - true, - }, - { - "ROUND CHANGE certificate missing", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for _, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).state = StatePreprepared - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - } - return sys - }, - func(_ *testSystem) istanbul.RoundChangeCertificate { - return istanbul.RoundChangeCertificate{} - }, - makeBlock(1), - errMissingRoundChangeCertificate, - false, - }, - { - "ROUND CHANGE certificate invalid, duplicate messages.", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for _, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).state = StatePreprepared - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - } - return sys - }, - func(sys *testSystem) istanbul.RoundChangeCertificate { - // Duplicate messages - roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, istanbul.EmptyPreparedCertificate()) - roundChangeCertificate.RoundChangeMessages[1] = roundChangeCertificate.RoundChangeMessages[0] - return roundChangeCertificate - }, - makeBlock(1), - errInvalidRoundChangeCertificateDuplicate, - false, - }, - { - "ROUND CHANGE certificate contains PREPARED certificate with inconsistent views among the cert's messages", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for _, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).state = StatePreprepared - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - getRoundState(c).sequence = big.NewInt(1) - c.current.TransitionToPreprepared(&istanbul.Preprepare{ - View: &istanbul.View{ - Round: big.NewInt(int64(N)), - Sequence: big.NewInt(1), - }, - Proposal: makeBlock(1), - RoundChangeCertificate: istanbul.RoundChangeCertificate{}, - }) - } - return sys - }, - func(sys *testSystem) istanbul.RoundChangeCertificate { - view1 := *(sys.backends[0].engine.(*core).current.View()) - - var view2 istanbul.View - view2.Sequence = big.NewInt(view1.Sequence.Int64()) - view2.Round = big.NewInt(view1.Round.Int64() + 1) - - preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view1, view2}, makeBlock(1)) - roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate) - return roundChangeCertificate - }, - makeBlock(1), - errInvalidPreparedCertificateInconsistentViews, - false, - }, - { - "ROUND CHANGE certificate contains PREPARED certificate for a different block.", - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for _, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).state = StatePreprepared - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - c.current.TransitionToPreprepared(&istanbul.Preprepare{ - View: &istanbul.View{ - Round: big.NewInt(int64(N)), - Sequence: big.NewInt(0), - }, - Proposal: makeBlock(2), - RoundChangeCertificate: istanbul.RoundChangeCertificate{}, - }) - } - return sys - }, - func(sys *testSystem) istanbul.RoundChangeCertificate { - preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(2)) - roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate) - return roundChangeCertificate - }, - makeBlock(1), - errInvalidPreparedCertificateDigestMismatch, - false, - }, - { - "ROUND CHANGE certificate for N+1 round with valid PREPARED certificates", - // Round is N+1 to match the correct proposer. - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for i, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - if i != 0 { - getRoundState(c).state = StateAcceptRequest - } - } - return sys - }, - func(sys *testSystem) istanbul.RoundChangeCertificate { - preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(1)) - roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate) - return roundChangeCertificate - }, - makeBlock(1), - nil, - false, - }, - { - "ROUND CHANGE certificate for N+1 round with empty PREPARED certificates", - // Round is N+1 to match the correct proposer. - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for i, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - if i != 0 { - getRoundState(c).state = StateAcceptRequest - } - } - return sys - }, - func(sys *testSystem) istanbul.RoundChangeCertificate { - roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, istanbul.EmptyPreparedCertificate()) - return roundChangeCertificate - }, - makeBlock(1), - nil, - false, - }, - { - "ROUND CHANGE certificate for N+1 or later rounds with empty PREPARED certificates", - // Round is N+1 to match the correct proposer. - func() *testSystem { - sys := NewTestSystemWithBackend(N, F) - - for i, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - if i != 0 { - getRoundState(c).state = StateAcceptRequest - } - } - return sys - }, - func(sys *testSystem) istanbul.RoundChangeCertificate { - v1 := *(sys.backends[0].engine.(*core).current.View()) - v2 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 1)} - v3 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 2)} - roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{v1, v2, v3}, istanbul.EmptyPreparedCertificate()) - return roundChangeCertificate - }, - makeBlock(1), - nil, - false, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - - t.Log("Running", "test", test.name) - sys := test.system() - - // Even those we pass core=false here, cores do get started and need stopping. - sys.Run(false) - defer sys.Stop(true) - - v0 := sys.backends[0] - r0 := v0.engine.(*core) - - curView := r0.current.View() - - preprepareView := curView - if test.existingBlock { - preprepareView = &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)} - } - - msg := istanbul.NewPreprepareMessage( - &istanbul.Preprepare{ - View: preprepareView, - Proposal: test.expectedRequest, - RoundChangeCertificate: test.getCert(sys), - }, - v0.Address(), - ) - - for i, v := range sys.backends { - // i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others. - if i == 0 { - continue - } - - c := v.engine.(*core) - - // run each backends and verify handlePreprepare function. - if err := c.handlePreprepare(msg); err != nil { - if err != test.expectedErr { - t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) - } - return - } - - if c.current.State() != StatePreprepared { - t.Errorf("state mismatch: have %v, want %v", c.current.State(), StatePreprepared) - } - - if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) { - t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView) - } - - if test.existingBlock && len(v.sentMsgs) > 0 { - t.Errorf("expecting to ignore commits for old messages %v", v.sentMsgs) - } else { - continue - } - - // verify prepare messages - decodedMsg := new(istanbul.Message) - err := decodedMsg.FromPayload(v.sentMsgs[0], nil) - if err != nil { - t.Errorf("error mismatch: have %v, want nil", err) - } - - expectedCode := istanbul.MsgPrepare - if test.existingBlock { - expectedCode = istanbul.MsgCommit - } - if decodedMsg.Code != expectedCode { - t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode) - } - - subject := decodedMsg.Prepare() - if decodedMsg.Code == istanbul.MsgCommit { - subject = decodedMsg.Commit().Subject - } - - if err != nil { - t.Errorf("error mismatch: have %v, want nil", err) - } - - expectedSubject := c.current.Subject() - if test.existingBlock { - expectedSubject = &istanbul.Subject{View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)}, - Digest: test.expectedRequest.Hash()} - } - - if !reflect.DeepEqual(subject, expectedSubject) { - t.Errorf("subject mismatch: have %v, want %v", subject, expectedSubject) - } - - if expectedCode == istanbul.MsgCommit { - srcValidator := c.current.GetValidatorByAddress(v.address) - - if err := c.verifyCommittedSeal(decodedMsg.Commit(), srcValidator); err != nil { - t.Errorf("invalid seal. verify commmited seal error: %v, subject: %v, committedSeal: %v", err, expectedSubject, decodedMsg.Commit().CommittedSeal) - } - } - } - }) - } -} - -// benchMarkHandleRoundChange benchmarks handling a round change messages with n prepare or commit messages in the prepared certificate -func benchMarkHandleRoundChange(n int, b *testing.B) { - // Setup - N := uint64(n) - F := uint64(1) // F does not affect tests - sys := NewMutedTestSystemWithBackend(N, F) - c := sys.backends[0].engine.(*core) - c.Start() - sys.backends[1].engine.(*core).Start() - // getPreparedCertificate defaults to 50% commits, 50% prepares. Modify the function to change the ratio. - preparedCertificate := sys.getPreparedCertificate(b, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(1)) - msg, err := sys.backends[1].getRoundChangeMessage(istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, preparedCertificate) - if err != nil { - b.Errorf("Error creating a round change message. err: %v", err) - } - - // benchmarked portion - b.ResetTimer() - for i := 0; i < b.N; i++ { - err = c.handleRoundChange(&msg) - if err != nil { - b.Errorf("Error handling the round change message. err: %v", err) - } - } -} - -func BenchmarkHandleRoundChange_10(b *testing.B) { benchMarkHandleRoundChange(10, b) } -func BenchmarkHandleRoundChange_50(b *testing.B) { benchMarkHandleRoundChange(50, b) } -func BenchmarkHandleRoundChange_90(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(90, b) -} -func BenchmarkHandleRoundChange_100(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(100, b) -} -func BenchmarkHandleRoundChange_120(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(120, b) -} -func BenchmarkHandleRoundChange_150(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(150, b) -} -func BenchmarkHandleRoundChange_200(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(200, b) -} - -// benchMarkHandlePreprepare benchmarks handling a preprepare with a round change certificate that has -// filled round change messages (i.e. the round change messages have prepared certificates that are not empty) -func benchMarkHandlePreprepare(n int, b *testing.B) { - // Setup - getRoundState := func(c *core) *roundStateImpl { - return c.current.(*rsSaveDecorator).rs.(*roundStateImpl) - } - N := uint64(n) - F := uint64(1) // F does not affect tests - sys := NewMutedTestSystemWithBackend(N, F) - - for i, backend := range sys.backends { - backend.engine.(*core).Start() - c := backend.engine.(*core) - getRoundState(c).round = big.NewInt(int64(N)) - getRoundState(c).desiredRound = getRoundState(c).round - if i != 0 { - getRoundState(c).state = StateAcceptRequest - } - } - c := sys.backends[0].engine.(*core) - - // Create pre-prepare - block := makeBlock(1) - nextView := istanbul.View{Round: big.NewInt(int64(N)), Sequence: big.NewInt(1)} - // getPreparedCertificate defaults to 50% commits, 50% prepares. Modify the function to change the ratio. - preparedCertificate := sys.getPreparedCertificate(b, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, block) - roundChangeCertificate := sys.getRoundChangeCertificate(b, []istanbul.View{nextView}, preparedCertificate) - msg, err := sys.backends[0].getPreprepareMessage(nextView, roundChangeCertificate, block) - if err != nil { - b.Errorf("Error creating a pre-prepare message. err: %v", err) - } - - // benchmarked portion - b.ResetTimer() - for i := 0; i < b.N; i++ { - err = c.handlePreprepare(&msg) - if err != nil { - b.Errorf("Error handling the pre-prepare message. err: %v", err) - } - } -} - -func BenchmarkHandlePreprepare_10(b *testing.B) { benchMarkHandlePreprepare(10, b) } -func BenchmarkHandlePreprepare_50(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandlePreprepare(50, b) -} -func BenchmarkHandlePreprepare_90(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandlePreprepare(90, b) -} -func BenchmarkHandlePreprepare_100(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandlePreprepare(100, b) -} -func BenchmarkHandlePreprepare_120(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandlePreprepare(120, b) -} -func BenchmarkHandlePreprepare_150(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandlePreprepare(150, b) -} -func BenchmarkHandlePreprepare_200(b *testing.B) { - b.Skip("Skipping slow benchmark") - benchMarkHandlePreprepare(200, b) -} diff --git a/consensus/istanbul/core/preprepare_v2.go b/consensus/istanbul/core/preprepare_v2.go index 858865e2cd..16a14037cc 100644 --- a/consensus/istanbul/core/preprepare_v2.go +++ b/consensus/istanbul/core/preprepare_v2.go @@ -34,15 +34,9 @@ func (c *core) ResendPreprepare() error { if st != StatePreprepared && st != StatePrepared && st != StateCommitted { return errors.New("Cant resend preprepare if not in preprepared, prepared, or committed state") } - if c.isConsensusFork(c.current.Sequence()) { - m := istanbul.NewPreprepareV2Message(c.current.PreprepareV2(), c.address) - logger.Debug("Re-Sending preprepare v2", "m", m) - c.broadcast(m) - } else { - m := istanbul.NewPreprepareMessage(c.current.Preprepare(), c.address) - logger.Debug("Re-Sending preprepare", "m", m) - c.broadcast(m) - } + m := istanbul.NewPreprepareV2Message(c.current.PreprepareV2(), c.address) + logger.Debug("Re-Sending preprepare v2", "m", m) + c.broadcast(m) return nil } @@ -53,11 +47,6 @@ func (c *core) handlePreprepareV2(msg *istanbul.Message) error { logger.Trace("Got preprepareV2 message", "m", msg) preprepareV2 := msg.PreprepareV2() - // Check consensus fork - if !c.isConsensusFork(preprepareV2.View.Sequence) { - logger.Info("Received PreprepareV2 for unforked block sequence", "sequence", preprepareV2.View.Sequence.Uint64()) - return errors.New("Received PreprepareV2 for not forked block") - } logger = logger.New("msg_num", preprepareV2.Proposal.Number(), "msg_hash", preprepareV2.Proposal.Hash(), "msg_seq", preprepareV2.View.Sequence, "msg_round", preprepareV2.View.Round) diff --git a/consensus/istanbul/core/preprepare_v2_test.go b/consensus/istanbul/core/preprepare_v2_test.go index e6ddc8066b..b929454ad8 100644 --- a/consensus/istanbul/core/preprepare_v2_test.go +++ b/consensus/istanbul/core/preprepare_v2_test.go @@ -53,8 +53,6 @@ func TestHandlePreprepareV2(t *testing.T) { sys := NewTestSystemWithBackend(N, F) for _, backend := range sys.backends { - // activate v2 consensus block - backend.engine.(*core).config.V2Block = big.NewInt(0) backend.engine.(*core).Start() } return sys @@ -72,8 +70,6 @@ func TestHandlePreprepareV2(t *testing.T) { sys := NewTestSystemWithBackend(N, F) for _, backend := range sys.backends { - // activate v2 consensus block - backend.engine.(*core).config.V2Block = big.NewInt(0) backend.engine.(*core).Start() } return sys @@ -95,8 +91,6 @@ func TestHandlePreprepareV2(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() if i != 0 { // replica 0 is the proposer @@ -119,8 +113,6 @@ func TestHandlePreprepareV2(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() if i != 0 { getRoundState(c).state = StatePreprepared @@ -145,8 +137,6 @@ func TestHandlePreprepareV2(t *testing.T) { for _, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).state = StatePreprepared getRoundState(c).sequence = big.NewInt(10) @@ -170,8 +160,6 @@ func TestHandlePreprepareV2(t *testing.T) { for _, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).state = StatePreprepared getRoundState(c).round = big.NewInt(int64(N)) @@ -193,8 +181,6 @@ func TestHandlePreprepareV2(t *testing.T) { for _, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).state = StatePreprepared getRoundState(c).round = big.NewInt(int64(N)) @@ -220,8 +206,6 @@ func TestHandlePreprepareV2(t *testing.T) { for _, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).state = StatePreprepared getRoundState(c).round = big.NewInt(int64(N)) @@ -261,8 +245,6 @@ func TestHandlePreprepareV2(t *testing.T) { for _, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).state = StatePreprepared getRoundState(c).round = big.NewInt(int64(N)) @@ -296,8 +278,6 @@ func TestHandlePreprepareV2(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).round = big.NewInt(int64(N)) getRoundState(c).desiredRound = getRoundState(c).round @@ -325,8 +305,6 @@ func TestHandlePreprepareV2(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).round = big.NewInt(int64(N)) getRoundState(c).desiredRound = getRoundState(c).round @@ -353,8 +331,6 @@ func TestHandlePreprepareV2(t *testing.T) { for i, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).round = big.NewInt(int64(N)) getRoundState(c).desiredRound = getRoundState(c).round @@ -489,10 +465,6 @@ func benchMarkHandleRoundChangeV2(n int, b *testing.B) { N := uint64(n) F := uint64(1) // F does not affect tests sys := NewMutedTestSystemWithBackend(N, F) - for _, b := range sys.backends { - // activate v2 consensus block - b.engine.(*core).config.V2Block = big.NewInt(0) - } c := sys.backends[0].engine.(*core) c.Start() sys.backends[1].engine.(*core).Start() @@ -526,15 +498,15 @@ func BenchmarkHandleRoundChange_100V2(b *testing.B) { } func BenchmarkHandleRoundChange_120V2(b *testing.B) { b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(120, b) + benchMarkHandleRoundChangeV2(120, b) } func BenchmarkHandleRoundChange_150V2(b *testing.B) { b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(150, b) + benchMarkHandleRoundChangeV2(150, b) } func BenchmarkHandleRoundChange_200V2(b *testing.B) { b.Skip("Skipping slow benchmark") - benchMarkHandleRoundChange(200, b) + benchMarkHandleRoundChangeV2(200, b) } // benchMarkHandlePreprepare benchmarks handling a preprepare with a round change certificate that has @@ -550,8 +522,6 @@ func benchMarkHandlePreprepareV2(n int, b *testing.B) { for i, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) backend.engine.(*core).Start() getRoundState(c).round = big.NewInt(int64(N)) getRoundState(c).desiredRound = getRoundState(c).round diff --git a/consensus/istanbul/core/request.go b/consensus/istanbul/core/request.go index 951558701a..d4c70ab8cc 100644 --- a/consensus/istanbul/core/request.go +++ b/consensus/istanbul/core/request.go @@ -43,11 +43,7 @@ func (c *core) handleRequest(request *istanbul.Request) error { // Must go through startNewRound to send proposals for round > 0 to ensure a round change certificate is generated. if c.current.State() == StateAcceptRequest && c.current.Round().Cmp(common.Big0) == 0 { - if c.isConsensusFork(c.current.Sequence()) { - c.sendPreprepareV2(request, istanbul.RoundChangeCertificateV2{}) - } else { - c.sendPreprepare(request, istanbul.RoundChangeCertificate{}) - } + c.sendPreprepareV2(request, istanbul.RoundChangeCertificateV2{}) } return nil } diff --git a/consensus/istanbul/core/request_test.go b/consensus/istanbul/core/request_test.go index 2c3fee8248..313a6ffb5c 100644 --- a/consensus/istanbul/core/request_test.go +++ b/consensus/istanbul/core/request_test.go @@ -35,7 +35,7 @@ func TestCheckRequestMsg(t *testing.T) { current: newRoundState(&istanbul.View{ Sequence: big.NewInt(1), Round: big.NewInt(0), - }, valSet, valSet.GetByIndex(0), false), + }, valSet, valSet.GetByIndex(0)), } // invalid request @@ -91,7 +91,7 @@ func TestStoreRequestMsg(t *testing.T) { current: newRoundState(&istanbul.View{ Sequence: big.NewInt(0), Round: big.NewInt(0), - }, valSet, valSet.GetByIndex(0), false), + }, valSet, valSet.GetByIndex(0)), pendingRequests: prque.New(nil), pendingRequestsMu: new(sync.Mutex), } diff --git a/consensus/istanbul/core/roundchange.go b/consensus/istanbul/core/roundchange.go deleted file mode 100644 index ddf4ccea9d..0000000000 --- a/consensus/istanbul/core/roundchange.go +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "errors" - "math/big" - - "github.com/celo-org/celo-blockchain/common" - "github.com/celo-org/celo-blockchain/consensus/istanbul" -) - -// buildRoundChangeMsg creates a round change msg for the given round -func (c *core) buildRoundChangeMsgV1(round *big.Int) *istanbul.Message { - nextView := &istanbul.View{ - Round: new(big.Int).Set(round), - Sequence: new(big.Int).Set(c.current.View().Sequence), - } - return istanbul.NewRoundChangeMessage(&istanbul.RoundChange{ - View: nextView, - PreparedCertificate: c.current.PreparedCertificate(), - }, c.address) -} - -func (c *core) handleRoundChangeCertificate(proposal istanbul.Subject, roundChangeCertificate istanbul.RoundChangeCertificate) error { - logger := c.newLogger("func", "handleRoundChangeCertificate", "proposal_round", proposal.View.Round, "proposal_seq", proposal.View.Sequence, "proposal_digest", proposal.Digest.String()) - - if len(roundChangeCertificate.RoundChangeMessages) > c.current.ValidatorSet().Size() || len(roundChangeCertificate.RoundChangeMessages) < c.current.ValidatorSet().MinQuorumSize() { - return errInvalidRoundChangeCertificateNumMsgs - } - - maxRound := big.NewInt(-1) - preferredDigest := common.Hash{} - seen := make(map[common.Address]bool) - decodedMessages := make([]istanbul.RoundChange, len(roundChangeCertificate.RoundChangeMessages)) - for i := range roundChangeCertificate.RoundChangeMessages { - // use a different variable each time since we'll store a pointer to the variable - message := roundChangeCertificate.RoundChangeMessages[i] - - // Verify message signed by a validator - if err := istanbul.CheckSignedBy(&message, message.Signature, - message.Address, errInvalidRoundChangeCertificateMsgSignature, c.validateFn); err != nil { - return err - } - - // Check for duplicate ROUND CHANGE messages - if seen[message.Address] { - return errInvalidRoundChangeCertificateDuplicate - } - seen[message.Address] = true - - // Check that the message is a ROUND CHANGE message - if istanbul.MsgRoundChange != message.Code { - return errInvalidRoundChangeCertificateMsgCode - } - - roundChange := message.RoundChange() - if roundChange.View == nil || roundChange.View.Sequence == nil || roundChange.View.Round == nil { - return errInvalidRoundChangeCertificateMsgView - } - - msgLogger := logger.New("msg_round", roundChange.View.Round, "msg_seq", roundChange.View.Sequence) - - // Verify ROUND CHANGE message is for the same sequence AND an equal or subsequent round as the proposal. - // We have already called checkMessage by this point and checked the proposal's and PREPREPARE's sequence match. - if roundChange.View.Sequence.Cmp(proposal.View.Sequence) != 0 || roundChange.View.Round.Cmp(proposal.View.Round) < 0 { - msgLogger.Error("Round change in certificate for a different sequence or an earlier round") - return errInvalidRoundChangeCertificateMsgView - } - - if roundChange.HasPreparedCertificate() { - msgLogger.Trace("Round change message has prepared certificate") - preparedView, err := c.verifyPreparedCertificate(roundChange.PreparedCertificate) - if err != nil { - return err - } - // We must use the proposal in the prepared certificate with the highest round number. (See OSDI 99, Section 4.4) - // Older prepared certificates may be generated, but if no node committed, there is no guarantee that - // it will be the next pre-prepare. If one node committed, that block is guaranteed (by quorum intersection) - // to be the next pre-prepare. That (higher view) prepared cert should override older perpared certs for - // blocks that were not committed. - // Also reject round change messages where the prepared view is greater than the round change view. - msgLogger = msgLogger.New("prepared_round", preparedView.Round, "prepared_seq", preparedView.Sequence) - if preparedView == nil || preparedView.Round.Cmp(proposal.View.Round) > 0 { - return errInvalidRoundChangeViewMismatch - } else if preparedView.Round.Cmp(maxRound) > 0 { - msgLogger.Trace("Prepared certificate is latest in round change certificate") - maxRound = preparedView.Round - preferredDigest = roundChange.PreparedCertificate.Proposal.Hash() - } - } - - decodedMessages[i] = *roundChange - // TODO(joshua): startNewRound needs these round change messages to generate a - // round change certificate even if this node is not the next proposer - c.roundChangeSet.Add(roundChange.View.Round, &message) - } - - if maxRound.Cmp(big.NewInt(-1)) > 0 && proposal.Digest != preferredDigest { - return errInvalidPreparedCertificateDigestMismatch - } - - // May have already moved to this round based on quorum round change messages. - logger.Trace("Trying to move to round change certificate's round", "target round", proposal.View.Round) - - return c.startNewRound(proposal.View.Round, false) -} - -func (c *core) handleRoundChange(msg *istanbul.Message) error { - logger := c.newLogger("func", "handleRoundChange", "tag", "handleMsg", "from", msg.Address) - - rc := msg.RoundChange() - - // Check consensus fork - if c.isConsensusFork(rc.View.Sequence) { - logger.Info("Received RoundChange (V1) for forked block sequence", "sequence", rc.View.Sequence.Uint64()) - return errors.New("Received RoundChange (V1) for forked block") - } - - logger = logger.New("msg_round", rc.View.Round, "msg_seq", rc.View.Sequence) - - // Must be same sequence and future round. - err := c.checkMessage(istanbul.MsgRoundChange, rc.View) - - // If the RC message is for the current sequence but a prior round, help the sender fast forward - // by sending back to it (not broadcasting) a round change message for our desired round. - if err == errOldMessage && rc.View.Sequence.Cmp(c.current.Sequence()) == 0 { - logger.Trace("Sending round change for desired round to node with a previous desired round", "msg_round", rc.View.Round) - c.sendRoundChangeAgain(msg.Address) - return nil - } else if err != nil { - logger.Debug("Check round change message failed", "err", err) - return err - } - - // Verify the PREPARED certificate if present. - if rc.HasPreparedCertificate() { - preparedView, err := c.verifyPreparedCertificate(rc.PreparedCertificate) - if err != nil { - return err - } else if preparedView == nil || preparedView.Round.Cmp(rc.View.Round) > 0 { - return errInvalidRoundChangeViewMismatch - } - } - - roundView := rc.View - - // Add the ROUND CHANGE message to its message set. - if err := c.roundChangeSet.Add(roundView.Round, msg); err != nil { - logger.Warn("Failed to add round change message", "roundView", roundView, "err", err) - return err - } - - // Skip to the highest round we know F+1 (one honest validator) is at, but - // don't start a round until we have a quorum who want to start a given round. - ffRound := c.roundChangeSet.MaxRound(c.current.ValidatorSet().F() + 1) - quorumRound := c.roundChangeSet.MaxOnOneRound(c.current.ValidatorSet().MinQuorumSize()) - logger = logger.New("ffRound", ffRound, "quorumRound", quorumRound) - logger.Trace("Got round change message", "rcs", c.roundChangeSet.String()) - // On f+1 round changes we send a round change and wait for the next round if we haven't done so already - // On quorum round change messages we go to the next round immediately. - if quorumRound != nil && quorumRound.Cmp(c.current.DesiredRound()) >= 0 { - logger.Debug("Got quorum round change messages, starting new round.") - return c.startNewRound(quorumRound, true) - } else if ffRound != nil { - logger.Debug("Got f+1 round change messages, sending own round change message and waiting for next round.") - c.waitForDesiredRound(ffRound) - } - - return nil -} - -// ---------------------------------------------------------------------------- - -// CurrentRoundChangeSet returns the current round change set summary. -func (c *core) CurrentRoundChangeSet() *RoundChangeSetSummary { - if c.isConsensusFork(c.current.Sequence()) { - rcs := c.roundChangeSetV2 - if rcs != nil { - return rcs.Summary() - } - } else { - rcs := c.roundChangeSet - if rcs != nil { - return rcs.Summary() - } - } - return nil -} diff --git a/consensus/istanbul/core/roundchange_test.go b/consensus/istanbul/core/roundchange_test.go deleted file mode 100644 index 3289dd79cd..0000000000 --- a/consensus/istanbul/core/roundchange_test.go +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "math/big" - "testing" - "time" - - "github.com/celo-org/celo-blockchain/common" - "github.com/celo-org/celo-blockchain/consensus/istanbul" - "github.com/celo-org/celo-blockchain/consensus/istanbul/validator" -) - -func TestRoundChangeSet(t *testing.T) { - vals, _, _ := generateValidators(4) - vset := validator.NewSet(vals) - rc := newRoundChangeSet(vset) - - view := &istanbul.View{ - Sequence: big.NewInt(1), - Round: big.NewInt(1), - } - r := &istanbul.Subject{ - View: view, - Digest: common.Hash{}, - } - - // Test Add() - // Add message from all validators - for i, v := range vset.List() { - rc.Add(view.Round, istanbul.NewPrepareMessage(r, v.Address())) - if rc.msgsForRound[view.Round.Uint64()].Size() != i+1 { - t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), i+1) - } - } - - // Add message again from all validators, but the size should be the same - for _, v := range vset.List() { - rc.Add(view.Round, istanbul.NewPrepareMessage(r, v.Address())) - if rc.msgsForRound[view.Round.Uint64()].Size() != vset.Size() { - t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), vset.Size()) - } - } - - // Test MaxRound() - for i := 0; i < 10; i++ { - maxRound := rc.MaxRound(i) - if i <= vset.Size() { - if maxRound == nil || maxRound.Cmp(view.Round) != 0 { - t.Errorf("MaxRound mismatch: have %v, want %v", maxRound, view.Round) - } - } else if maxRound != nil { - t.Errorf("MaxRound mismatch: have %v, want nil", maxRound) - } - } - - // Test Clear() - for i := int64(0); i < 2; i++ { - rc.Clear(big.NewInt(i)) - if rc.msgsForRound[view.Round.Uint64()].Size() != vset.Size() { - t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), vset.Size()) - } - } - rc.Clear(big.NewInt(2)) - if rc.msgsForRound[view.Round.Uint64()] != nil { - t.Errorf("the change messages mismatch: have %v, want nil", rc.msgsForRound[view.Round.Uint64()]) - } - - // Test Add() - // Add message from all validators - for i, v := range vset.List() { - rc.Add(view.Round, istanbul.NewPrepareMessage(r, v.Address())) - if rc.msgsForRound[view.Round.Uint64()].Size() != i+1 { - t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.msgsForRound[view.Round.Uint64()].Size(), i+1) - } - } - - rc.Clear(big.NewInt(2)) - if rc.msgsForRound[view.Round.Uint64()] != nil { - t.Errorf("the change messages mismatch: have %v, want nil", rc.msgsForRound[view.Round.Uint64()]) - } - - // Test that we only store the msg with the highest round for each validator - roundMultiplier := 1 - for j := 1; j <= roundMultiplier; j++ { - for i, v := range vset.List() { - view := &istanbul.View{ - Sequence: big.NewInt(1), - Round: big.NewInt(int64((i + 1) * j)), - } - r := &istanbul.Subject{ - View: view, - Digest: common.Hash{}, - } - m, _ := Encode(r) - msg := &istanbul.Message{ - Code: istanbul.MsgRoundChange, - Msg: m, - Address: v.Address(), - } - err := rc.Add(view.Round, msg) - if err != nil { - t.Errorf("Round change message: unexpected error %v", err) - } - } - } - - for i, v := range vset.List() { - lookingForValAtRound := uint64(roundMultiplier * (i + 1)) - if rc.msgsForRound[lookingForValAtRound].Size() != 1 { - t.Errorf("Round change messages at unexpected rounds: %v", rc.msgsForRound) - } - if rc.latestRoundForVal[v.Address()] != lookingForValAtRound { - t.Errorf("Round change messages at unexpected rounds: for %v want %v have %v", - i+1, rc.latestRoundForVal[v.Address()], lookingForValAtRound) - } - } - - for threshold := 1; threshold <= vset.Size(); threshold++ { - r := rc.MaxRound(threshold).Uint64() - expectedR := uint64((vset.Size() - threshold + 1) * roundMultiplier) - if r != expectedR { - t.Errorf("MaxRound: %v want %v have %v", rc.String(), expectedR, r) - } - } - - // Test getCertificate - for r := 1; r < vset.Size(); r += roundMultiplier { - expectedMsgsAtRound := vset.Size() - r + 1 - for quorum := 1; quorum < 10; quorum++ { - cert, err := rc.getCertificate(big.NewInt(int64(r)), quorum) - if expectedMsgsAtRound < quorum { - // Expecting fewer than quorum. - if err != errFailedCreateRoundChangeCertificate || len(cert.RoundChangeMessages) != 0 { - t.Errorf("problem in getCertificate r=%v q=%v expMsgs=%v - want 0 have %v err=%v -- %v -- %v", r, quorum, expectedMsgsAtRound, len(cert.RoundChangeMessages), err, cert, rc) - } - } else { - // Number msgs available at this round is >= quorum. Expecting a cert with =quorum RC messages. - if err != nil || len(cert.RoundChangeMessages) != quorum { - t.Errorf("problem in getCertificate r=%v q=%v expMsgs=%v - want %v have %v -- %v -- %v", r, quorum, quorum, expectedMsgsAtRound, len(cert.RoundChangeMessages), cert, rc) - } - } - } - } -} - -func TestHandleRoundChangeCertificate(t *testing.T) { - N := uint64(4) // replica 0 is the proposer, it will send messages to others - F := uint64(1) - view := istanbul.View{ - Round: big.NewInt(1), - Sequence: big.NewInt(1), - } - - testCases := []struct { - name string - getCertificate func(*testing.T, *testSystem) istanbul.RoundChangeCertificate - expectedErr error - }{ - { - "Valid round change certificate without PREPARED certificate", - func(t *testing.T, sys *testSystem) istanbul.RoundChangeCertificate { - return sys.getRoundChangeCertificate(t, []istanbul.View{view}, istanbul.EmptyPreparedCertificate()) - }, - nil, - }, - { - "Valid round change certificate with PREPARED certificate", - func(t *testing.T, sys *testSystem) istanbul.RoundChangeCertificate { - return sys.getRoundChangeCertificate(t, []istanbul.View{view}, sys.getPreparedCertificate(t, []istanbul.View{view}, makeBlock(0))) - }, - nil, - }, - { - "Invalid round change certificate, duplicate message", - func(t *testing.T, sys *testSystem) istanbul.RoundChangeCertificate { - roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{view}, istanbul.EmptyPreparedCertificate()) - roundChangeCertificate.RoundChangeMessages[1] = roundChangeCertificate.RoundChangeMessages[0] - return roundChangeCertificate - }, - errInvalidRoundChangeCertificateDuplicate, - }, - { - "Empty certificate", - func(t *testing.T, sys *testSystem) istanbul.RoundChangeCertificate { - return istanbul.RoundChangeCertificate{} - }, - errInvalidRoundChangeCertificateNumMsgs, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - sys := NewTestSystemWithBackend(N, F) - for i, backend := range sys.backends { - c := backend.engine.(*core) - c.Start() - certificate := test.getCertificate(t, sys) - subject := istanbul.Subject{ - View: &view, - Digest: makeBlock(0).Hash(), - } - err := c.handleRoundChangeCertificate(subject, certificate) - - if err != test.expectedErr { - t.Errorf("error mismatch for test case %v: have %v, want %v", i, err, test.expectedErr) - } - if err == nil && c.current.View().Cmp(&view) != 0 { - t.Errorf("view mismatch for test case %v: have %v, want %v", i, c.current.View(), view) - } - } - for _, backend := range sys.backends { - backend.engine.Stop() - } - close(sys.quit) - }) - } -} - -func TestHandleRoundChange(t *testing.T) { - N := uint64(4) // replica 0 is the proposer, it will send messages to others - F := uint64(1) // F does not affect tests - - buildEmptyCertificate := func(_ *testing.T, _ *testSystem) istanbul.PreparedCertificate { - return istanbul.EmptyPreparedCertificate() - } - - noopPrepare := func(_ *testSystem) {} - - testCases := []struct { - name string - prepareSystem func(*testSystem) - getCert func(*testing.T, *testSystem) istanbul.PreparedCertificate - expectedErr error - }{ - { - "normal case", - noopPrepare, - buildEmptyCertificate, - nil, - }, - { - "normal case with valid prepared certificate", - noopPrepare, - func(t *testing.T, sys *testSystem) istanbul.PreparedCertificate { - return sys.getPreparedCertificate(t, []istanbul.View{*sys.backends[0].engine.(*core).current.View()}, makeBlock(1)) - }, - nil, - }, - { - "normal case with invalid prepared certificate", - noopPrepare, - func(t *testing.T, sys *testSystem) istanbul.PreparedCertificate { - preparedCert := sys.getPreparedCertificate(t, []istanbul.View{*sys.backends[0].engine.(*core).current.View()}, makeBlock(1)) - preparedCert.PrepareOrCommitMessages[0] = preparedCert.PrepareOrCommitMessages[1] - return preparedCert - }, - errInvalidPreparedCertificateDuplicate, - }, - { - "valid message for future round", - func(sys *testSystem) { - sys.backends[0].engine.(*core).current.(*rsSaveDecorator).rs.(*roundStateImpl).round = big.NewInt(10) - }, - func(t *testing.T, _ *testSystem) istanbul.PreparedCertificate { - return istanbul.EmptyPreparedCertificate() - }, - nil, - }, - { - "invalid message for future sequence", - func(sys *testSystem) { - sys.backends[0].engine.(*core).current.(*rsSaveDecorator).rs.(*roundStateImpl).sequence = big.NewInt(10) - }, - buildEmptyCertificate, - errFutureMessage, - }, - { - "invalid message for previous round", - func(sys *testSystem) { - sys.backends[0].engine.(*core).current.(*rsSaveDecorator).rs.(*roundStateImpl).round = big.NewInt(0) - }, - buildEmptyCertificate, - nil, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - sys := NewTestSystemWithBackend(N, F) - - closer := sys.Run(false) - defer closer() - - for _, v := range sys.backends { - v.engine.(*core).Start() - } - test.prepareSystem(sys) - - v0 := sys.backends[0] - r0 := v0.engine.(*core) - - curView := r0.current.View() - nextView := &istanbul.View{ - Round: new(big.Int).Add(curView.Round, common.Big1), - Sequence: curView.Sequence, - } - - msg := istanbul.NewRoundChangeMessage(&istanbul.RoundChange{ - View: nextView, - PreparedCertificate: test.getCert(t, sys), - }, v0.Address()) - - for i, v := range sys.backends { - // i == 0 is primary backend, it is responsible for send ROUND CHANGE messages to others. - if i == 0 { - continue - } - - c := v.engine.(*core) - - // run each backends and verify handlePreprepare function. - err := c.handleRoundChange(msg) - if err != test.expectedErr { - t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) - } - return - } - }) - } -} - -// This tests the liveness issue present in the initial implementation of Istanbul, described in -// more detail here: https://arxiv.org/pdf/1901.07160.pdf -// To test this, a block is proposed, for which 2F + 1 PREPARE messages are sent to F nodes. -// In the original implementation, these F nodes would lock onto that block, and eventually everyone would -// round change. If the next proposer was byzantine, they could send a PREPREPARE with a different block, -// get the remaining 2F non-byzantine nodes to lock onto that new block, causing a deadlock. -// In the new implementation, the PREPREPARE will include a ROUND CHANGE certificate, -// and all nodes will accept the newly proposed block. -func TestCommitsBlocksAfterRoundChange(t *testing.T) { - sys := NewTestSystemWithBackend(4, 1) - - for _, b := range sys.backends { - b.engine.Start() // start Istanbul core - block := makeBlock(1) - b.NewRequest(block) - } - - newBlocks := sys.backends[0].EventMux().Subscribe(istanbul.FinalCommittedEvent{}) - defer newBlocks.Unsubscribe() - - timeout := sys.backends[3].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) - defer timeout.Unsubscribe() - - istMsgDistribution := map[uint64]map[int]bool{} - - // Allow everyone to see the initial proposal - // Send all PREPARE messages to F nodes. - // Send COMMIT messages (we don't expect these to be sent in the first round anyway). - // Send ROUND CHANGE messages to the remaining 2F + 1 nodes. - istMsgDistribution[istanbul.MsgPreprepare] = gossip - istMsgDistribution[istanbul.MsgPrepare] = sendToF - istMsgDistribution[istanbul.MsgCommit] = gossip - istMsgDistribution[istanbul.MsgRoundChange] = sendTo2FPlus1 - - go sys.distributeIstMsgs(t, sys, istMsgDistribution) - - <-timeout.Chan() - - // Turn PREPAREs back on for round 1. - testLogger.Info("Turn PREPAREs back on for round 1") - istMsgDistribution[istanbul.MsgPrepare] = gossip - - // Eventually we should get a block again - select { - case <-time.After(2 * time.Second): - t.Error("Did not finalize a block within 2 secs") - case _, ok := <-newBlocks.Chan(): - if !ok { - t.Error("Error reading block") - } - // Wait for all backends to finalize the block. - <-time.After(1 * time.Second) - testLogger.Info("Expected all backends to finalize") - expectedCommitted, _ := sys.backends[0].GetCurrentHeadBlockAndAuthor() - for i, b := range sys.backends { - committed, _ := b.GetCurrentHeadBlockAndAuthor() - // We don't expect any particular block to be committed here. We do expect them to be consistent. - if committed.Number().Cmp(common.Big1) != 0 { - t.Errorf("Backend %v got committed block with unexpected number: expected %v, got %v", i, 1, committed.Number()) - } - if expectedCommitted.Hash() != committed.Hash() { - t.Errorf("Backend %v got committed block with unexpected hash: expected %v, got %v", i, expectedCommitted.Hash(), committed.Hash()) - } - } - } - - // Manually open and close b/c hijacking sys.listen - for _, b := range sys.backends { - b.engine.Stop() // stop Istanbul core - } - close(sys.quit) -} - -// This tests that when F+1 nodes receive 2F+1 PREPARE messages for a particular proposal, the -// system enforces that as the only valid proposal for this sequence. -func TestPreparedCertificatePersistsThroughRoundChanges(t *testing.T) { - sys := NewTestSystemWithBackend(4, 1) - - for _, b := range sys.backends { - b.engine.Start() // start Istanbul core - block := makeBlock(1) - b.NewRequest(block) - } - - newBlocks := sys.backends[3].EventMux().Subscribe(istanbul.FinalCommittedEvent{}) - defer newBlocks.Unsubscribe() - - timeout := sys.backends[3].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) - defer timeout.Unsubscribe() - - istMsgDistribution := map[uint64]map[int]bool{} - - // Send PREPARE messages to F + 1 nodes so we guarantee a PREPARED certificate in the ROUND CHANGE certificate.. - istMsgDistribution[istanbul.MsgPreprepare] = gossip - istMsgDistribution[istanbul.MsgPrepare] = sendToFPlus1 - istMsgDistribution[istanbul.MsgCommit] = gossip - istMsgDistribution[istanbul.MsgRoundChange] = gossip - - go sys.distributeIstMsgs(t, sys, istMsgDistribution) - - // Turn PREPARE messages off for round 1 to force reuse of the PREPARED certificate. - <-time.After(1 * time.Second) - istMsgDistribution[istanbul.MsgPrepare] = noGossip - - // Wait for round 1 to start. - <-timeout.Chan() - // Turn PREPARE messages back on in time for round 2. - <-time.After(1 * time.Second) - istMsgDistribution[istanbul.MsgPrepare] = gossip - - // Wait for round 2 to start. - <-timeout.Chan() - - select { - case <-timeout.Chan(): - t.Error("Did not finalize a block in round 2.") - case _, ok := <-newBlocks.Chan(): - if !ok { - t.Error("Error reading block") - } - // Wait for all backends to finalize the block. - <-time.After(2 * time.Second) - for i, b := range sys.backends { - committed, _ := b.GetCurrentHeadBlockAndAuthor() - // We expect to commit the block proposed by the first proposer. - expectedCommitted := makeBlock(1) - if committed.Number().Cmp(common.Big1) != 0 { - t.Errorf("Backend %v got committed block with unexpected number: expected %v, got %v", i, 1, committed.Number()) - } - if expectedCommitted.Hash() != committed.Hash() { - t.Errorf("Backend %v got committed block with unexpected hash: expected %v, got %v", i, expectedCommitted.Hash(), committed.Hash()) - } - } - } - - // Manually open and close b/c hijacking sys.listen - for _, b := range sys.backends { - b.engine.Stop() // stop Istanbul core - } - close(sys.quit) -} - -// Test periodic round changes at high rounds -func TestPeriodicRoundChanges(t *testing.T) { - sys := NewTestSystemWithBackend(4, 1) - - for _, b := range sys.backends { - b.engine.Start() // start Istanbul core - block := makeBlock(1) - b.NewRequest(block) - } - - newBlocks := sys.backends[3].EventMux().Subscribe(istanbul.FinalCommittedEvent{}) - defer newBlocks.Unsubscribe() - - timeoutMoveToNextRound := sys.backends[3].EventMux().Subscribe(timeoutAndMoveToNextRoundEvent{}) - defer timeoutMoveToNextRound.Unsubscribe() - - timeoutResendRC := sys.backends[3].EventMux().Subscribe(resendRoundChangeEvent{}) - defer timeoutResendRC.Unsubscribe() - - istMsgDistribution := map[uint64]map[int]bool{} - istMsgDistribution[istanbul.MsgPreprepare] = noGossip - istMsgDistribution[istanbul.MsgPrepare] = noGossip - istMsgDistribution[istanbul.MsgCommit] = noGossip - istMsgDistribution[istanbul.MsgRoundChange] = noGossip - - go sys.distributeIstMsgs(t, sys, istMsgDistribution) - - for _, b := range sys.backends { - b.engine.(*core).waitForDesiredRound(big.NewInt(5)) - } - - // Expect at least one repeat RC before move to next round. - timeoutResends := 0 -loop: - for { - select { - case <-timeoutResendRC.Chan(): - testLogger.Info("Got timeoutResendRC") - timeoutResends++ - case <-timeoutMoveToNextRound.Chan(): - if timeoutResends == 0 { - t.Errorf("No Repeat events before moving to next round") - } - break loop - } - } - - istMsgDistribution[istanbul.MsgPreprepare] = gossip - istMsgDistribution[istanbul.MsgPrepare] = gossip - istMsgDistribution[istanbul.MsgCommit] = gossip - istMsgDistribution[istanbul.MsgRoundChange] = gossip - - // Make sure we finalize block in next two rounds. - roundTimeouts := 0 -loop2: - for { - select { - case <-timeoutMoveToNextRound.Chan(): - roundTimeouts++ - if roundTimeouts > 1 { - t.Error("Did not finalize a block.") - } - case _, ok := <-newBlocks.Chan(): - if !ok { - t.Error("Error reading block") - } - // Wait for all backends to finalize the block. - <-time.After(2 * time.Second) - for i, b := range sys.backends { - committed, _ := b.GetCurrentHeadBlockAndAuthor() - // We expect to commit the block proposed by proposer 6 mod 4 = 2. - expectedCommitted := makeBlock(1) - if committed.Number().Cmp(common.Big1) != 0 { - t.Errorf("Backend %v got committed block with unexpected number: expected %v, got %v", i, 1, committed.Number()) - } - if expectedCommitted.Hash() != committed.Hash() { - t.Errorf("Backend %v got committed block with unexpected hash: expected %v, got %v", i, expectedCommitted.Hash(), committed.Hash()) - } - } - break loop2 - } - } - - // Manually open and close b/c hijacking sys.listen - for _, b := range sys.backends { - b.engine.Stop() // stop Istanbul core - } - close(sys.quit) -} diff --git a/consensus/istanbul/core/roundchange_v2.go b/consensus/istanbul/core/roundchange_v2.go index 5e0d9de14b..748724572f 100644 --- a/consensus/istanbul/core/roundchange_v2.go +++ b/consensus/istanbul/core/roundchange_v2.go @@ -17,7 +17,6 @@ package core import ( - "errors" "math/big" "github.com/celo-org/celo-blockchain/common" @@ -26,32 +25,24 @@ import ( // sendRoundChange broadcasts a ROUND CHANGE message with the current desired round. func (c *core) sendRoundChange() { - if c.isConsensusFork(c.current.Sequence()) { - msg, err := c.buildSignedRoundChangeMsgV2(c.current.DesiredRound()) - if err != nil { - logger := c.newLogger("func", "sendRoundChange") - logger.Warn("Cannot build signed roundChangeV2 message", "error", err) - return - } - c.broadcast(msg) - } else { - c.broadcast(c.buildRoundChangeMsgV1(c.current.DesiredRound())) + msg, err := c.buildSignedRoundChangeMsgV2(c.current.DesiredRound()) + if err != nil { + logger := c.newLogger("func", "sendRoundChange") + logger.Warn("Cannot build signed roundChangeV2 message", "error", err) + return } + c.broadcast(msg) } // sendRoundChange sends a ROUND CHANGE message for the current desired round back to a single address func (c *core) sendRoundChangeAgain(addr common.Address) { - if c.isConsensusFork(c.current.Sequence()) { - msg, err := c.buildSignedRoundChangeMsgV2(c.current.DesiredRound()) - if err != nil { - logger := c.newLogger("func", "sendRoundChangeAgain", "addr", addr) - logger.Warn("Cannot build signed roundChangeV2 message", "error", err) - return - } - c.unicast(msg, addr) - } else { - c.unicast(c.buildRoundChangeMsgV1(c.current.DesiredRound()), addr) + msg, err := c.buildSignedRoundChangeMsgV2(c.current.DesiredRound()) + if err != nil { + logger := c.newLogger("func", "sendRoundChangeAgain", "addr", addr) + logger.Warn("Cannot build signed roundChangeV2 message", "error", err) + return } + c.unicast(msg, addr) } // buildRoundChangeV2 builds a roundChangeV2 instance with an empty prepared certificate @@ -164,12 +155,6 @@ func (c *core) handleRoundChangeV2(msg *istanbul.Message) error { rc := msg.RoundChangeV2() - // Check consensus fork - if !c.isConsensusFork(rc.Request.View.Sequence) { - logger.Info("Received RoundChangeV2 for unforked block sequence", "sequence", rc.Request.View.Sequence.Uint64()) - return errors.New("Received RoundChangeV2 for not forked block") - } - // Check signature of the internal Request if err := istanbul.CheckSignedBy(&rc.Request, rc.Request.Signature, rc.Request.Address, errInvalidRoundChangeRequestSignature, c.validateFn); err != nil { @@ -238,7 +223,7 @@ func (c *core) handleRoundChangeV2(msg *istanbul.Message) error { // ---------------------------------------------------------------------------- // CurrentRoundChangeSet returns the current round change set summary. -func (c *core) CurrentRoundChangeSetV2() *RoundChangeSetSummary { +func (c *core) CurrentRoundChangeSet() *RoundChangeSetSummary { rcs := c.roundChangeSetV2 if rcs != nil { return rcs.Summary() diff --git a/consensus/istanbul/core/roundchange_v2_test.go b/consensus/istanbul/core/roundchange_v2_test.go index a39349e809..fb1fc5db80 100644 --- a/consensus/istanbul/core/roundchange_v2_test.go +++ b/consensus/istanbul/core/roundchange_v2_test.go @@ -208,8 +208,6 @@ func TestHandleRoundChangeCertificateV2(t *testing.T) { sys := NewTestSystemWithBackend(N, F) for i, backend := range sys.backends { c := backend.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) c.Start() certificate, prop := test.getCertificate(t, sys) err := c.handleRoundChangeCertificateV2(view, certificate, prop) @@ -309,8 +307,6 @@ func TestHandleRoundChangeV2(t *testing.T) { for _, v := range sys.backends { c := v.engine.(*core) - // activate v2 consensus block - c.config.V2Block = big.NewInt(0) c.Start() } test.prepareSystem(sys) @@ -437,8 +433,6 @@ func TestCommitsBlocksAfterRoundChangeV2(t *testing.T) { sys := NewTestSystemWithBackend(4, 1) for _, b := range sys.backends { - // activate v2 consensus block - b.engine.(*core).config.V2Block = big.NewInt(0) b.engine.Start() // start Istanbul core block := makeBlock(1) b.NewRequest(block) @@ -504,8 +498,6 @@ func TestUseRoundChangeCertificateWithPC(t *testing.T) { sys := NewTestSystemWithBackend(4, 1) for i, b := range sys.backends { - // activate v2 consensus block - b.engine.(*core).config.V2Block = big.NewInt(0) b.engine.Start() // start Istanbul core block := makeBlock(1) block.Header().GasUsed = uint64(i) @@ -588,8 +580,6 @@ func TestPreparedCertificatePersistsThroughRoundChangesV2(t *testing.T) { sys := NewTestSystemWithBackend(4, 1) for _, b := range sys.backends { - // activate v2 consensus block - b.engine.(*core).config.V2Block = big.NewInt(0) b.engine.Start() // start Istanbul core block := makeBlock(1) b.NewRequest(block) @@ -658,8 +648,6 @@ func TestPeriodicRoundChangesV2(t *testing.T) { sys := NewTestSystemWithBackend(4, 1) for _, b := range sys.backends { - // activate v2 consensus block - b.engine.(*core).config.V2Block = big.NewInt(0) b.engine.Start() // start Istanbul core block := makeBlock(1) b.NewRequest(block) diff --git a/consensus/istanbul/core/roundchangeset.go b/consensus/istanbul/core/roundchangeset.go deleted file mode 100644 index 58e6f7e01a..0000000000 --- a/consensus/istanbul/core/roundchangeset.go +++ /dev/null @@ -1,207 +0,0 @@ -package core - -import ( - "fmt" - "math/big" - "sort" - "strings" - "sync" - - "github.com/celo-org/celo-blockchain/common" - "github.com/celo-org/celo-blockchain/consensus/istanbul" -) - -func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet { - return &roundChangeSet{ - validatorSet: valSet, - msgsForRound: make(map[uint64]MessageSet), - latestRoundForVal: make(map[common.Address]uint64), - mu: new(sync.Mutex), - } -} - -type roundChangeSet struct { - validatorSet istanbul.ValidatorSet - msgsForRound map[uint64]MessageSet - latestRoundForVal map[common.Address]uint64 - mu *sync.Mutex -} - -// Summary returns a print friendly summary of the messages in the set. -func (rcs *roundChangeSet) Summary() *RoundChangeSetSummary { - rcs.mu.Lock() - defer rcs.mu.Unlock() - rounds := make(map[common.Address]uint64) - for v, r := range rcs.latestRoundForVal { - rounds[v] = r - } - vals := make(map[uint64][]common.Address) - for r, vs := range rcs.msgsForRound { - vs2 := make([]common.Address, 0, vs.Size()) - for _, v := range vs.Values() { - vs2 = append(vs2, v.Address) - } - vals[r] = vs2 - } - return &RoundChangeSetSummary{ - RoundForVal: rounds, - ValsInRound: vals, - } -} - -// Add adds the round and message into round change set -func (rcs *roundChangeSet) Add(r *big.Int, msg *istanbul.Message) error { - rcs.mu.Lock() - defer rcs.mu.Unlock() - - src := msg.Address - round := r.Uint64() - - if prevLatestRound, ok := rcs.latestRoundForVal[src]; ok { - if prevLatestRound > round { - // Reject as we have an RC for a later round from this validator. - return errOldMessage - } else if prevLatestRound < round { - // Already got an RC for an earlier round from this validator. - // Forget that and remember this. - if rcs.msgsForRound[prevLatestRound] != nil { - rcs.msgsForRound[prevLatestRound].Remove(src) - if rcs.msgsForRound[prevLatestRound].Size() == 0 { - delete(rcs.msgsForRound, prevLatestRound) - } - } - } - } - - rcs.latestRoundForVal[src] = round - - if rcs.msgsForRound[round] == nil { - rcs.msgsForRound[round] = newMessageSet(rcs.validatorSet) - } - return rcs.msgsForRound[round].Add(msg) -} - -// Clear deletes the messages with smaller round -func (rcs *roundChangeSet) Clear(round *big.Int) { - rcs.mu.Lock() - defer rcs.mu.Unlock() - - for k, rms := range rcs.msgsForRound { - if rms.Size() == 0 || k < round.Uint64() { - for _, msg := range rms.Values() { - delete(rcs.latestRoundForVal, msg.Address) // no need to check if msg.Address is present - } - delete(rcs.msgsForRound, k) - } - } -} - -// MaxRound returns the max round which the number of messages is equal or larger than num -func (rcs *roundChangeSet) MaxRound(num int) *big.Int { - rcs.mu.Lock() - defer rcs.mu.Unlock() - - // Sort rounds descending - var sortedRounds []uint64 - for r := range rcs.msgsForRound { - sortedRounds = append(sortedRounds, r) - } - sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) - - acc := 0 - for _, r := range sortedRounds { - rms := rcs.msgsForRound[r] - acc += rms.Size() - if acc >= num { - return new(big.Int).SetUint64(r) - } - } - - return nil -} - -// MaxOnOneRound returns the max round which the number of messages is >= num -func (rcs *roundChangeSet) MaxOnOneRound(num int) *big.Int { - rcs.mu.Lock() - defer rcs.mu.Unlock() - - // Sort rounds descending - var sortedRounds []uint64 - for r := range rcs.msgsForRound { - sortedRounds = append(sortedRounds, r) - } - sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) - - for _, r := range sortedRounds { - rms := rcs.msgsForRound[r] - if rms.Size() >= num { - return new(big.Int).SetUint64(r) - } - } - return nil -} - -func (rcs *roundChangeSet) String() string { - rcs.mu.Lock() - defer rcs.mu.Unlock() - - // Sort rounds descending - var sortedRounds []uint64 - for r := range rcs.msgsForRound { - sortedRounds = append(sortedRounds, r) - } - sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) - - modeRound := uint64(0) - modeRoundSize := 0 - msgsForRoundStr := make([]string, 0, len(sortedRounds)) - for _, r := range sortedRounds { - rms := rcs.msgsForRound[r] - if rms.Size() > modeRoundSize { - modeRound = r - modeRoundSize = rms.Size() - } - msgsForRoundStr = append(msgsForRoundStr, fmt.Sprintf("%v: %v", r, rms.String())) - } - - return fmt.Sprintf("RCS len=%v mode_round=%v mode_round_len=%v unique_rounds=%v %v", - len(rcs.latestRoundForVal), - modeRound, - modeRoundSize, - len(rcs.msgsForRound), - strings.Join(msgsForRoundStr, ", ")) -} - -// Gets a round change certificate for a specific round. Includes quorumSize messages of that round or later. -// If the total is less than quorumSize, returns an empty cert and errFailedCreateRoundChangeCertificate. -func (rcs *roundChangeSet) getCertificate(minRound *big.Int, quorumSize int) (istanbul.RoundChangeCertificate, error) { - rcs.mu.Lock() - defer rcs.mu.Unlock() - - // Sort rounds descending - var sortedRounds []uint64 - for r := range rcs.msgsForRound { - sortedRounds = append(sortedRounds, r) - } - sort.Slice(sortedRounds, func(i, j int) bool { return sortedRounds[i] > sortedRounds[j] }) - - var messages []istanbul.Message - for _, r := range sortedRounds { - if r < minRound.Uint64() { - break - } - for _, message := range rcs.msgsForRound[r].Values() { - messages = append(messages, *message) - - // Stop when we've added a quorum of the highest-round messages. - if len(messages) >= quorumSize { - return istanbul.RoundChangeCertificate{ - RoundChangeMessages: messages, - }, nil - } - } - } - - // Didn't find a quorum of messages. Return an empty certificate with error. - return istanbul.RoundChangeCertificate{}, errFailedCreateRoundChangeCertificate -} diff --git a/consensus/istanbul/core/roundstate.go b/consensus/istanbul/core/roundstate.go index 4ef48b2225..1d970ed0c2 100644 --- a/consensus/istanbul/core/roundstate.go +++ b/consensus/istanbul/core/roundstate.go @@ -42,8 +42,7 @@ type RoundState interface { // mutation functions StartNewRound(nextRound *big.Int, validatorSet istanbul.ValidatorSet, nextProposer istanbul.Validator) error StartNewSequence(nextSequence *big.Int, validatorSet istanbul.ValidatorSet, - nextProposer istanbul.Validator, parentCommits MessageSet, consensusFork bool) error - TransitionToPreprepared(preprepare *istanbul.Preprepare) error + nextProposer istanbul.Validator, parentCommits MessageSet) error TransitionToPrepreparedV2(preprepareV2 *istanbul.PreprepareV2) error TransitionToWaitingForNewRound(r *big.Int, nextProposer istanbul.Validator) error TransitionToCommitted() error @@ -64,7 +63,6 @@ type RoundState interface { Proposer() istanbul.Validator IsProposer(address common.Address) bool Subject() *istanbul.Subject - Preprepare() *istanbul.Preprepare PreprepareV2() *istanbul.PreprepareV2 Proposal() istanbul.Proposal Round() *big.Int @@ -82,15 +80,12 @@ type RoundState interface { // RoundState stores the consensus state type roundStateImpl struct { - forked bool - state State round *big.Int desiredRound *big.Int sequence *big.Int // data for current round - preprepare *istanbul.Preprepare preprepareV2 *istanbul.PreprepareV2 prepares MessageSet commits MessageSet @@ -138,14 +133,11 @@ type RoundStateSummary struct { PreparedCertificate *istanbul.PreparedCertificateSummary `json:"preparedCertificate"` } -func newRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet, proposer istanbul.Validator, forked bool) RoundState { +func newRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet, proposer istanbul.Validator) RoundState { if proposer == nil { log.Crit("Proposer cannot be nil") } return &roundStateImpl{ - // consensus fork flag - forked: forked, - state: StateAcceptRequest, round: view.Round, desiredRound: view.Round, @@ -209,17 +201,10 @@ func (rs *roundStateImpl) GetPrepareOrCommitSize() int { func (rs *roundStateImpl) lockedPreprepareProposal() *istanbul.Proposal { // Assume locked - if rs.forked { - if rs.preprepareV2 == nil { - return nil - } - return &rs.preprepareV2.Proposal - } else { - if rs.preprepare == nil { - return nil - } - return &rs.preprepare.Proposal + if rs.preprepareV2 == nil { + return nil } + return &rs.preprepareV2.Proposal } func (rs *roundStateImpl) Subject() *istanbul.Subject { @@ -270,13 +255,6 @@ func (rs *roundStateImpl) GetValidatorByAddress(address common.Address) istanbul return validator } -func (rs *roundStateImpl) Preprepare() *istanbul.Preprepare { - rs.mu.RLock() - defer rs.mu.RUnlock() - - return rs.preprepare -} - func (rs *roundStateImpl) PreprepareV2() *istanbul.PreprepareV2 { rs.mu.RLock() defer rs.mu.RUnlock() @@ -321,7 +299,6 @@ func (rs *roundStateImpl) changeRound(nextRound *big.Int, validatorSet istanbul. rs.proposer = nextProposer // ?? - rs.preprepare = nil rs.preprepareV2 = nil } @@ -335,12 +312,11 @@ func (rs *roundStateImpl) StartNewRound(nextRound *big.Int, validatorSet istanbu } func (rs *roundStateImpl) StartNewSequence(nextSequence *big.Int, validatorSet istanbul.ValidatorSet, - nextProposer istanbul.Validator, parentCommits MessageSet, consensusFork bool) error { + nextProposer istanbul.Validator, parentCommits MessageSet) error { rs.mu.Lock() defer rs.mu.Unlock() logger := rs.newLogger() - rs.forked = consensusFork rs.validatorSet = validatorSet rs.changeRound(big.NewInt(0), validatorSet, nextProposer) @@ -367,27 +343,10 @@ func (rs *roundStateImpl) TransitionToCommitted() error { return nil } -func (rs *roundStateImpl) TransitionToPreprepared(preprepare *istanbul.Preprepare) error { - rs.mu.Lock() - defer rs.mu.Unlock() - - if rs.forked { - // can't used preprepare v1 when consensus forked - return errors.New("Cannot use Preprepare v1 when consensus forked") - } - rs.preprepare = preprepare - rs.state = StatePreprepared - return nil -} - func (rs *roundStateImpl) TransitionToPrepreparedV2(preprepareV2 *istanbul.PreprepareV2) error { rs.mu.Lock() defer rs.mu.Unlock() - if !rs.forked { - // can't used preprepare v2 when consensus not forked - return errors.New("Cannot use PreprepareV2 when consensus not forked") - } rs.preprepareV2 = preprepareV2 rs.state = StatePreprepared return nil @@ -577,14 +536,8 @@ func (rs *roundStateImpl) Summary() *RoundStateSummary { summary.PendingRequestHash = &hash } - if rs.forked { - if rs.preprepareV2 != nil { - summary.Preprepare = rs.preprepareV2.Summary() - } - } else { - if rs.preprepare != nil { - summary.Preprepare = rs.preprepare.Summary() - } + if rs.preprepareV2 != nil { + summary.Preprepare = rs.preprepareV2.Summary() } if !rs.preparedCertificate.IsEmpty() { @@ -665,23 +618,12 @@ func (rs *roundStateImpl) EncodeRLP(w io.Writer) error { } var serializedPreprepare []byte - if rs.forked { - if rs.preprepareV2 == nil { - serializedPreprepare = rlp.EmptyList - } else { - serializedPreprepare, err = rlp.EncodeToBytes(rs.preprepareV2) - if err != nil { - return err - } - } + if rs.preprepareV2 == nil { + serializedPreprepare = rlp.EmptyList } else { - if rs.preprepare == nil { - serializedPreprepare = rlp.EmptyList - } else { - serializedPreprepare, err = rlp.EncodeToBytes(rs.preprepare) - if err != nil { - return err - } + serializedPreprepare, err = rlp.EncodeToBytes(rs.preprepareV2) + if err != nil { + return err } } @@ -707,8 +649,6 @@ func (rs *roundStateImpl) EncodeRLP(w io.Writer) error { // Stream. It is not forbidden to read less or more, but it might // be confusing. func (rs *roundStateImpl) DecodeRLP(stream *rlp.Stream) error { - // rs.forked was already set by caller - var data roundStateRLP err := stream.Decode(&data) if err != nil { @@ -758,21 +698,12 @@ func (rs *roundStateImpl) DecodeRLP(stream *rlp.Stream) error { } if !bytes.Equal(data.SerializedPreprepare, rlp.EmptyList) { - if rs.forked { - var value istanbul.PreprepareV2 - err := rlp.DecodeBytes(data.SerializedPreprepare, &value) - if err != nil { - return err - } - rs.preprepareV2 = &value - } else { - var value istanbul.Preprepare - err := rlp.DecodeBytes(data.SerializedPreprepare, &value) - if err != nil { - return err - } - rs.preprepare = &value + var value istanbul.PreprepareV2 + err := rlp.DecodeBytes(data.SerializedPreprepare, &value) + if err != nil { + return err } + rs.preprepareV2 = &value } return nil diff --git a/consensus/istanbul/core/roundstate_db.go b/consensus/istanbul/core/roundstate_db.go index 3a2f932820..44de342856 100644 --- a/consensus/istanbul/core/roundstate_db.go +++ b/consensus/istanbul/core/roundstate_db.go @@ -47,7 +47,7 @@ type RoundStateDB interface { // GetOldestValidView returns the oldest valid view that can be stored on the db // it might or might not be present on the db GetOldestValidView() (*istanbul.View, error) - GetRoundStateFor(view *istanbul.View, consensusForked bool) (RoundState, error) + GetRoundStateFor(view *istanbul.View) (RoundState, error) UpdateLastRoundState(rs RoundState) error Close() error } @@ -216,7 +216,7 @@ func (rsdb *roundStateDBImpl) GetOldestValidView() (*istanbul.View, error) { return &istanbul.View{Sequence: oldestValidSequence, Round: common.Big0}, nil } -func (rsdb *roundStateDBImpl) GetRoundStateFor(view *istanbul.View, consensusForked bool) (RoundState, error) { +func (rsdb *roundStateDBImpl) GetRoundStateFor(view *istanbul.View) (RoundState, error) { viewKey := view2Key(view) rawEntry, err := rsdb.db.Get(viewKey, nil) if err != nil { @@ -224,7 +224,6 @@ func (rsdb *roundStateDBImpl) GetRoundStateFor(view *istanbul.View, consensusFor } var entry roundStateImpl - entry.forked = consensusForked if err = rlp.DecodeBytes(rawEntry, &entry); err != nil { return nil, err } diff --git a/consensus/istanbul/core/roundstate_db_test.go b/consensus/istanbul/core/roundstate_db_test.go index 81b8d35027..f7752475d8 100644 --- a/consensus/istanbul/core/roundstate_db_test.go +++ b/consensus/istanbul/core/roundstate_db_test.go @@ -21,7 +21,7 @@ func TestRSDBRoundStateDB(t *testing.T) { {Address: common.BytesToAddress([]byte(string(rune(2)))), BLSPublicKey: pubkey1}, {Address: common.BytesToAddress([]byte(string(rune(4)))), BLSPublicKey: pubkey2}, }) - return newRoundState(newView(2, 1), valSet, valSet.GetByIndex(0), false) + return newRoundState(newView(2, 1), valSet, valSet.GetByIndex(0)) } t.Run("Should save view & roundState", func(t *testing.T) { @@ -34,7 +34,7 @@ func TestRSDBRoundStateDB(t *testing.T) { finishOnError(t, err) assertEqualView(t, view, rs.View()) - savedRs, err := rsdb.GetRoundStateFor(view, false) + savedRs, err := rsdb.GetRoundStateFor(view) finishOnError(t, err) assertEqualRoundState(t, savedRs, rs) }) @@ -44,7 +44,7 @@ func TestRSDBRoundStateDB(t *testing.T) { rs := dummyRoundState() err := rsdb.UpdateLastRoundState(rs) finishOnError(t, err) - rs.StartNewSequence(common.Big32, rs.ValidatorSet(), rs.ValidatorSet().GetByIndex(1), rs.ParentCommits(), false) + rs.StartNewSequence(common.Big32, rs.ValidatorSet(), rs.ValidatorSet().GetByIndex(1), rs.ParentCommits()) err = rsdb.UpdateLastRoundState(rs) finishOnError(t, err) @@ -63,7 +63,7 @@ func TestRSDBDeleteEntriesOlderThan(t *testing.T) { {Address: common.BytesToAddress([]byte(string(rune(2)))), BLSPublicKey: pubkey1}, {Address: common.BytesToAddress([]byte(string(rune(4)))), BLSPublicKey: pubkey2}, }) - return newRoundState(view, valSet, valSet.GetByIndex(0), false) + return newRoundState(view, valSet, valSet.GetByIndex(0)) } rsdb, _ := newRoundStateDB("", &RoundStateDBOptions{withGarbageCollector: false}) @@ -147,7 +147,7 @@ func TestRSDBGetOldestValidView(t *testing.T) { if viewToStore != nil { t.Logf("Saving RoundState") - err := rsdb.UpdateLastRoundState(newRoundState(viewToStore, valSet, valSet.GetByIndex(0), false)) + err := rsdb.UpdateLastRoundState(newRoundState(viewToStore, valSet, valSet.GetByIndex(0))) if err != nil { t.Fatalf("UpdateLastRoundState error: %v", err) } diff --git a/consensus/istanbul/core/roundstate_save_decorator.go b/consensus/istanbul/core/roundstate_save_decorator.go index f39d4c2dc9..0428277ef4 100644 --- a/consensus/istanbul/core/roundstate_save_decorator.go +++ b/consensus/istanbul/core/roundstate_save_decorator.go @@ -50,11 +50,8 @@ func (rsp *rsSaveDecorator) StartNewRound(nextRound *big.Int, validatorSet istan return rsp.persistOnNoError(rsp.rs.StartNewRound(nextRound, validatorSet, nextProposer)) } func (rsp *rsSaveDecorator) StartNewSequence(nextSequence *big.Int, validatorSet istanbul.ValidatorSet, - nextProposer istanbul.Validator, parentCommits MessageSet, consensusFork bool) error { - return rsp.persistOnNoError(rsp.rs.StartNewSequence(nextSequence, validatorSet, nextProposer, parentCommits, consensusFork)) -} -func (rsp *rsSaveDecorator) TransitionToPreprepared(preprepare *istanbul.Preprepare) error { - return rsp.persistOnNoError(rsp.rs.TransitionToPreprepared(preprepare)) + nextProposer istanbul.Validator, parentCommits MessageSet) error { + return rsp.persistOnNoError(rsp.rs.StartNewSequence(nextSequence, validatorSet, nextProposer, parentCommits)) } func (rsp *rsSaveDecorator) TransitionToPrepreparedV2(preprepareV2 *istanbul.PreprepareV2) error { return rsp.persistOnNoError(rsp.rs.TransitionToPrepreparedV2(preprepareV2)) @@ -121,9 +118,6 @@ func (rsp *rsSaveDecorator) Sequence() *big.Int { return rsp.rs.Sequence() } // View implements RoundState.View func (rsp *rsSaveDecorator) View() *istanbul.View { return rsp.rs.View() } -// Preprepare implements RoundState.Preprepare -func (rsp *rsSaveDecorator) Preprepare() *istanbul.Preprepare { return rsp.rs.Preprepare() } - // PreprepareV2 implements RoundState.PreprepareV2 func (rsp *rsSaveDecorator) PreprepareV2() *istanbul.PreprepareV2 { return rsp.rs.PreprepareV2() } diff --git a/consensus/istanbul/core/roundstate_test.go b/consensus/istanbul/core/roundstate_test.go index b10aac03ae..ef4ca7b5bd 100644 --- a/consensus/istanbul/core/roundstate_test.go +++ b/consensus/istanbul/core/roundstate_test.go @@ -23,7 +23,7 @@ func TestRoundStateRLPEncoding(t *testing.T) { {Address: common.HexToAddress("4"), BLSPublicKey: blscrypto.SerializedPublicKey{3, 1, 4}}, }) view := &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(2)} - return newRoundState(view, valSet, valSet.GetByIndex(0), false) + return newRoundState(view, valSet, valSet.GetByIndex(0)) } t.Run("With nil fields", func(t *testing.T) { @@ -61,13 +61,13 @@ func TestRoundStateRLPEncoding(t *testing.T) { assertEqualRoundState(t, rs, result) }) - t.Run("With a Preprepare", func(t *testing.T) { + t.Run("With a PreprepareV2", func(t *testing.T) { rs := dummyRoundState() - rs.TransitionToPreprepared(&istanbul.Preprepare{ - Proposal: makeBlock(1), - View: rs.View(), - RoundChangeCertificate: istanbul.RoundChangeCertificate{}, + rs.TransitionToPrepreparedV2(&istanbul.PreprepareV2{ + Proposal: makeBlock(1), + View: rs.View(), + RoundChangeCertificateV2: istanbul.RoundChangeCertificateV2{}, }) rawVal, err := rlp.EncodeToBytes(rs) @@ -105,7 +105,7 @@ func TestRoundStateSummary(t *testing.T) { } valSet := validator.NewSet(valData) - rs := newRoundState(view, valSet, valSet.GetByIndex(0), false) + rs := newRoundState(view, valSet, valSet.GetByIndex(0)) // Add a few prepares rs.AddPrepare(&istanbul.Message{ @@ -245,17 +245,17 @@ func TestRoundStateSummary(t *testing.T) { t.Run("With a Preprepare", func(t *testing.T) { rs := dummyRoundState() block := makeBlock(1) - preprepare := &istanbul.Preprepare{ + preprepare := &istanbul.PreprepareV2{ Proposal: block, View: rs.View(), - RoundChangeCertificate: istanbul.RoundChangeCertificate{ - RoundChangeMessages: []istanbul.Message{ - {Code: istanbul.MsgRoundChange, Address: validatorAddresses[3]}, + RoundChangeCertificateV2: istanbul.RoundChangeCertificateV2{ + Requests: []istanbul.RoundChangeRequest{ + {Address: validatorAddresses[3]}, }, }, } - rs.TransitionToPreprepared(preprepare) + rs.TransitionToPrepreparedV2(preprepare) rsSummary := rs.Summary() diff --git a/consensus/istanbul/core/testbackend_test.go b/consensus/istanbul/core/testbackend_test.go index ec19ec03a9..545f0965e7 100644 --- a/consensus/istanbul/core/testbackend_test.go +++ b/consensus/istanbul/core/testbackend_test.go @@ -274,16 +274,6 @@ func (self *testSystemBackend) finalizeAndReturnMessage(msg *istanbul.Message) ( return *message, err } -func (self *testSystemBackend) getPreprepareMessage(view istanbul.View, roundChangeCertificate istanbul.RoundChangeCertificate, proposal istanbul.Proposal) (istanbul.Message, error) { - msg := istanbul.NewPreprepareMessage(&istanbul.Preprepare{ - View: &view, - RoundChangeCertificate: roundChangeCertificate, - Proposal: proposal, - }, self.address) - - return self.finalizeAndReturnMessage(msg) -} - func (self *testSystemBackend) getPreprepareV2Message(view istanbul.View, roundChangeCertificateV2 istanbul.RoundChangeCertificateV2, proposal istanbul.Proposal) (istanbul.Message, error) { @@ -333,15 +323,6 @@ func (self *testSystemBackend) getCommitMessage(view istanbul.View, proposal ist return message, err } -func (self *testSystemBackend) getRoundChangeMessage(view istanbul.View, preparedCert istanbul.PreparedCertificate) (istanbul.Message, error) { - msg := istanbul.NewRoundChangeMessage(&istanbul.RoundChange{ - View: &view, - PreparedCertificate: preparedCert, - }, common.Address{}) - - return self.finalizeAndReturnMessage(msg) -} - func (self *testSystemBackend) getRoundChangeV2Message(view istanbul.View, preparedCertV2 istanbul.PreparedCertificateV2, proposal istanbul.Proposal) (istanbul.Message, error) { req, err := self.getRoundChangeRequest(view, preparedCertV2) if err != nil { @@ -437,7 +418,6 @@ func newTestSystemWithBackend(n, f uint64, v2Block *big.Int) *testSystem { config.TimeoutBackoffFactor = 100 config.MinResendRoundChangeTimeout = 1000 config.MaxResendRoundChangeTimeout = 10000 - config.V2Block = v2Block for i := uint64(0); i < n; i++ { vset := validator.NewSet(validators) @@ -612,21 +592,6 @@ func (sys *testSystem) getPreparedCertificateV2(t ErrorReporter, views []istanbu return istanbul.PCV2FromPCV1(pc) } -func (sys *testSystem) getRoundChangeCertificate(t ErrorReporter, views []istanbul.View, preparedCertificate istanbul.PreparedCertificate) istanbul.RoundChangeCertificate { - var roundChangeCertificate istanbul.RoundChangeCertificate - for i, backend := range sys.backends { - if uint64(i) == sys.MinQuorumSize() { - break - } - msg, err := backend.getRoundChangeMessage(views[i%len(views)], preparedCertificate) - if err != nil { - t.Errorf("Failed to create ROUND CHANGE message: %v", err) - } - roundChangeCertificate.RoundChangeMessages = append(roundChangeCertificate.RoundChangeMessages, msg) - } - return roundChangeCertificate -} - func (sys *testSystem) getRoundChangeCertificateV2(t ErrorReporter, views []istanbul.View, preparedCertificateV2 istanbul.PreparedCertificateV2) istanbul.RoundChangeCertificateV2 { var roundChangeCertificateV2 istanbul.RoundChangeCertificateV2 for i, backend := range sys.backends { diff --git a/consensus/istanbul/core/testutils_test.go b/consensus/istanbul/core/testutils_test.go index b323c49da2..4e45a15e57 100644 --- a/consensus/istanbul/core/testutils_test.go +++ b/consensus/istanbul/core/testutils_test.go @@ -28,14 +28,8 @@ func newView(seq, round uint64) *istanbul.View { return &istanbul.View{Round: new(big.Int).SetUint64(round), Sequence: new(big.Int).SetUint64(seq)} } -func newTestRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet) RoundState { - current := newRoundState(view, validatorSet, validatorSet.GetByIndex(0), false) - current.(*roundStateImpl).preprepare = newTestPreprepare(view) - return current -} - func newTestRoundStateV2(view *istanbul.View, validatorSet istanbul.ValidatorSet) RoundState { - current := newRoundState(view, validatorSet, validatorSet.GetByIndex(0), true) + current := newRoundState(view, validatorSet, validatorSet.GetByIndex(0)) current.(*roundStateImpl).preprepareV2 = newTestPreprepareV2(view) return current } @@ -76,15 +70,15 @@ func assertEqualRoundState(t *testing.T, have, want RoundState) { testEqual("PendingRequest.Proposal.Hash", haveBlock.Hash(), wantBlock.Hash()) } - if have.Preprepare() == nil || want.Preprepare() == nil { - testEqual("Preprepare", have.Preprepare(), want.Preprepare()) + if have.PreprepareV2() == nil || want.PreprepareV2() == nil { + testEqual("PreprepareV2", have.PreprepareV2(), want.PreprepareV2()) } else { - testEqual("Preprepare.Proposal.Hash", have.Preprepare().Proposal.Hash(), want.Preprepare().Proposal.Hash()) - testEqual("Preprepare.View", have.Preprepare().View, want.Preprepare().View) - testEqual("Preprepare.RoundChangeCertificate.IsEmpty", have.Preprepare().RoundChangeCertificate.IsEmpty(), want.Preprepare().RoundChangeCertificate.IsEmpty()) + testEqual("PreprepareV2.Proposal.Hash", have.PreprepareV2().Proposal.Hash(), want.PreprepareV2().Proposal.Hash()) + testEqual("PreprepareV2.View", have.PreprepareV2().View, want.PreprepareV2().View) + testEqual("PreprepareV2.RoundChangeCertificateV2.IsEmpty", have.PreprepareV2().RoundChangeCertificateV2.IsEmpty(), want.PreprepareV2().RoundChangeCertificateV2.IsEmpty()) - if !have.Preprepare().RoundChangeCertificate.IsEmpty() && !want.Preprepare().RoundChangeCertificate.IsEmpty() { - testEqual("Preprepare.RoundChangeCertificate.RoundChangeMessages", have.Preprepare().RoundChangeCertificate.RoundChangeMessages, want.Preprepare().RoundChangeCertificate.RoundChangeMessages) + if !have.PreprepareV2().RoundChangeCertificateV2.IsEmpty() && !want.PreprepareV2().RoundChangeCertificateV2.IsEmpty() { + testEqual("PreprepareV2.RoundChangeCertificateV2.RoundChangeMessages", have.PreprepareV2().RoundChangeCertificateV2.Requests, want.PreprepareV2().RoundChangeCertificateV2.Requests) } } diff --git a/consensus/istanbul/core/types_test.go b/consensus/istanbul/core/types_test.go index 338e91280b..505e50877c 100644 --- a/consensus/istanbul/core/types_test.go +++ b/consensus/istanbul/core/types_test.go @@ -28,14 +28,14 @@ import ( ) func testPreprepare(t *testing.T) { - pp := &istanbul.Preprepare{ + pp := &istanbul.PreprepareV2{ View: &istanbul.View{ Round: big.NewInt(1), Sequence: big.NewInt(2), }, Proposal: makeBlock(1), } - m := istanbul.NewPreprepareMessage(pp, common.HexToAddress("0x1234567890")) + m := istanbul.NewPreprepareV2Message(pp, common.HexToAddress("0x1234567890")) msgPayload, err := m.Payload() if err != nil { t.Errorf("error mismatch: have %v, want nil", err) @@ -47,7 +47,7 @@ func testPreprepare(t *testing.T) { t.Errorf("error mismatch: have %v, want nil", err) } - decodedPP := decodedMsg.Preprepare() + decodedPP := decodedMsg.PreprepareV2() // if block is encoded/decoded by rlp, we cannot to compare interface data type using reflect.DeepEqual. (like istanbul.Proposal) // so individual comparison here. if !reflect.DeepEqual(pp.Proposal.Hash(), decodedPP.Proposal.Hash()) { diff --git a/consensus/istanbul/types.go b/consensus/istanbul/types.go index 8a7c2c4fbd..c7e6bf8c98 100644 --- a/consensus/istanbul/types.go +++ b/consensus/istanbul/types.go @@ -29,7 +29,6 @@ import ( "github.com/celo-org/celo-blockchain/core/types" "github.com/celo-org/celo-blockchain/crypto" blscrypto "github.com/celo-org/celo-blockchain/crypto/bls" - "github.com/celo-org/celo-blockchain/log" "github.com/celo-org/celo-blockchain/p2p/enode" "github.com/celo-org/celo-blockchain/rlp" ) @@ -127,218 +126,12 @@ func (v *View) Cmp(y *View) int { return 0 } -// ## RoundChangeCertificate ############################################################## -// To considerably reduce the bandwidth used by the RoundChangeCertificate type (which often -// contains repeated Proposal from different RoundChange messages), we break it apart during -// RLP encoding and then build it back during decoding. Proposals are sent just once, and -// Messages referencing them will use their Hash instead. - -type RoundChangeCertificate struct { - RoundChangeMessages []Message -} - -func (b *RoundChangeCertificate) IsEmpty() bool { - return len(b.RoundChangeMessages) == 0 -} - -// EncodeRLP serializes b into the Ethereum RLP format. -func (c *RoundChangeCertificate) EncodeRLP(w io.Writer) error { - proposals, messages, err := c.asValues() - if err != nil { - return err - } - log.Debug("Round change certificate proposals", "count", len(proposals)) - return rlp.Encode(w, []interface{}{proposals, messages}) -} - -// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. -func (c *RoundChangeCertificate) DecodeRLP(s *rlp.Stream) error { - var decodestr struct { - Proposals []*types.Block - IndexedMessages []IndexedRoundChangeMessage - } - - if err := s.Decode(&decodestr); err != nil { - return err - } - return c.setValues(decodestr.Proposals, decodestr.IndexedMessages) -} - -// setValues recreates the RoundChange messages from the props (Proposal set/index) and the -// list of IndexedRoundChangeMessage, which is supposed to be the same as the RoundChange -// Messages but with the proposals just referenced to the Proposals set. -func (c *RoundChangeCertificate) setValues(props []*types.Block, iMess []IndexedRoundChangeMessage) error { - // create a Proposal index from the list - propIndex := make(map[common.Hash]Proposal) - for _, prop := range props { - propIndex[prop.Hash()] = prop - } - // Recreate Messages one by one - mess := make([]Message, len(iMess)) - for i, im := range iMess { - mess[i] = Message{ - Code: im.Message.Code, - Address: im.Message.Address, - Signature: im.Message.Signature, - } - - // Add the proposal to the message if it had one - roundChange, err := im.Message.TryRoundChange() - if err != nil { - return err - } - - if proposal, ok := propIndex[im.ProposalHash]; ok { - roundChange.PreparedCertificate.Proposal = proposal - } - - setMessageBytes(&mess[i], roundChange) - mess[i].roundChange = roundChange - } - c.RoundChangeMessages = mess - return nil -} - -type IndexedRoundChangeMessage struct { - ProposalHash common.Hash - Message Message // PreparedCertificate.Proposal = nil if any -} - -// asValues presents the RoundChangeCertificate as values for RLP Serialization. -// This is done using a list of proposals, and the RoundChange messages using -// hash references instead of the full proposal objects, to reduce bandwidth. -func (c *RoundChangeCertificate) asValues() ([]*types.Block, []*IndexedRoundChangeMessage, error) { - var err error - - messages := make([]*IndexedRoundChangeMessage, len(c.RoundChangeMessages)) - proposalsMap := make(map[common.Hash]*types.Block) - - for i, message := range c.RoundChangeMessages { - var proposal *types.Block - proposal, messages[i], err = extractProposal(&message) - if err != nil { - return nil, nil, err - } - - if proposal != nil { - // we don't use the height since we know they MUST be the same - proposalsMap[proposal.Hash()] = proposal - } - } - - // Iterate values. RLP does not support maps - proposals := make([]*types.Block, len(proposalsMap)) - var i = 0 - for _, p := range proposalsMap { - proposals[i] = p - i++ - } - return proposals, messages, nil -} - -func extractProposal(message *Message) (*types.Block, *IndexedRoundChangeMessage, error) { - roundChange, err := message.TryRoundChange() - if err != nil { - return nil, nil, err - } - - pc := roundChange.PreparedCertificate - - // Assume message.Code = MsgRoundChange - indexedMsg := IndexedRoundChangeMessage{ - Message: Message{ - Code: message.Code, - Address: message.Address, - Signature: message.Signature, - }, - } - - if pc.Proposal != nil { - indexedMsg.ProposalHash = pc.Proposal.Hash() - } - - curatedPC := EmptyPreparedCertificate() - curatedPC.PrepareOrCommitMessages = pc.PrepareOrCommitMessages - - setMessageBytes(&indexedMsg.Message, - &RoundChange{ - View: roundChange.View, - PreparedCertificate: curatedPC, - }) - - return pc.Proposal.(*types.Block), &indexedMsg, nil -} - -// ## Preprepare ############################################################## - -// NewPreprepareMessage constructs a Message instance with the given sender and -// prePrepare. Both the prePrepare instance and the serialized bytes of -// prePrepare are part of the returned Message. -func NewPreprepareMessage(prePrepare *Preprepare, sender common.Address) *Message { - message := &Message{ - Address: sender, - Code: MsgPreprepare, - prePrepare: prePrepare, - } - setMessageBytes(message, prePrepare) - return message -} - -type Preprepare struct { - View *View - Proposal Proposal - RoundChangeCertificate RoundChangeCertificate -} - -type PreprepareData struct { - View *View - Proposal *types.Block - RoundChangeCertificate RoundChangeCertificate -} - type PreprepareSummary struct { View *View `json:"view"` ProposalHash common.Hash `json:"proposalHash"` RoundChangeCertificateSenders []common.Address `json:"roundChangeCertificateSenders"` } -func (pp *Preprepare) HasRoundChangeCertificate() bool { - return !pp.RoundChangeCertificate.IsEmpty() -} - -func (pp *Preprepare) AsData() *PreprepareData { - return &PreprepareData{ - View: pp.View, - Proposal: pp.Proposal.(*types.Block), - RoundChangeCertificate: pp.RoundChangeCertificate, - } -} - -func (pp *Preprepare) Summary() *PreprepareSummary { - return &PreprepareSummary{ - View: pp.View, - ProposalHash: pp.Proposal.Hash(), - RoundChangeCertificateSenders: MapMessagesToSenders(pp.RoundChangeCertificate.RoundChangeMessages), - } -} - -// RLP Encoding --------------------------------------------------------------- - -// EncodeRLP serializes b into the Ethereum RLP format. -func (pp *Preprepare) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, pp.AsData()) -} - -// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. -func (pp *Preprepare) DecodeRLP(s *rlp.Stream) error { - var data PreprepareData - if err := s.Decode(&data); err != nil { - return err - } - pp.View, pp.Proposal, pp.RoundChangeCertificate = data.View, data.Proposal, data.RoundChangeCertificate - return nil -} - // ## PreparedCertificate ##################################################### type PreparedCertificate struct { @@ -424,49 +217,6 @@ func (pc *PreparedCertificate) DecodeRLP(s *rlp.Stream) error { } -// ## RoundChange ############################################################# - -// NewRoundChangeMessage constructs a Message instance with the given sender and -// roundChange. Both the roundChange instance and the serialized bytes of -// roundChange are part of the returned Message. -func NewRoundChangeMessage(roundChange *RoundChange, sender common.Address) *Message { - message := &Message{ - Address: sender, - Code: MsgRoundChange, - roundChange: roundChange, - } - setMessageBytes(message, roundChange) - return message -} - -type RoundChange struct { - View *View - PreparedCertificate PreparedCertificate -} - -func (b *RoundChange) HasPreparedCertificate() bool { - return !b.PreparedCertificate.IsEmpty() -} - -// EncodeRLP serializes b into the Ethereum RLP format. -func (b *RoundChange) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{b.View, &b.PreparedCertificate}) -} - -// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream. -func (b *RoundChange) DecodeRLP(s *rlp.Stream) error { - var roundChange struct { - View *View - PreparedCertificate PreparedCertificate - } - - if err := s.Decode(&roundChange); err != nil { - return err - } - b.View, b.PreparedCertificate = roundChange.View, roundChange.PreparedCertificate - return nil -} - // ## Subject ################################################################# // NewPrepareMessage constructs a Message instance with the given sender and @@ -631,24 +381,22 @@ func (qed *QueryEnodeData) DecodeRLP(s *rlp.Stream) error { // ## Consensus Message codes ########################################################## const ( - MsgPreprepare uint64 = iota + DEPRECATED_MsgPreprepare uint64 = iota // Moved to V2 MsgPrepare MsgCommit - MsgRoundChange + DEPRECATED_MsgRoundChange // Moved to V2 MsgRoundChangeV2 MsgPreprepareV2 ) -// IsRoundChangeCode returns true if and only if the message code equals -// MsgRoundChange or MsgRoundChangeV2 +// IsRoundChangeCode returns true if and only if the message code equals MsgRoundChangeV2 func IsRoundChangeCode(istanbulMsgCode uint64) bool { - return istanbulMsgCode == MsgRoundChange || istanbulMsgCode == MsgRoundChangeV2 + return istanbulMsgCode == MsgRoundChangeV2 } -// IsPreprepareCode returns true if and only if the message code equals -// MsgPreprepare or MsgPreprepareV2 +// IsPreprepareCode returns true if and only if the message code equals MsgPreprepareV2 func IsPreprepareCode(istanbulMsgCode uint64) bool { - return istanbulMsgCode == MsgPreprepare || istanbulMsgCode == MsgPreprepareV2 + return istanbulMsgCode == MsgPreprepareV2 } // Message is a wrapper used for all istanbul communication. It encapsulates @@ -673,10 +421,8 @@ type Message struct { // serializable since they are private. They are set when calling // Message.FromPayload, or at message construction time. committedSubject *CommittedSubject - prePrepare *Preprepare prePrepareV2 *PreprepareV2 prepare *Subject - roundChange *RoundChange roundChangeV2 *RoundChangeV2 queryEnode *QueryEnodeData forwardMessage *ForwardMessage @@ -709,13 +455,6 @@ func (m *Message) Sign(signingFn func(data []byte) ([]byte, error)) error { func (m *Message) DecodeMessage() error { var err error switch m.Code { - case MsgPreprepare: - var p *Preprepare - err = m.decode(&p) - if err != nil { - return err - } - m.prePrepare = p case MsgPreprepareV2: var p *PreprepareV2 err = m.decode(&p) @@ -731,13 +470,6 @@ func (m *Message) DecodeMessage() error { var cs *CommittedSubject err = m.decode(&cs) m.committedSubject = cs - case MsgRoundChange: - var p *RoundChange - err = m.decode(&p) - if err != nil { - return err - } - m.roundChange = p case MsgRoundChangeV2: var p *RoundChangeV2 err = m.decode(&p) @@ -833,11 +565,6 @@ func (m *Message) Commit() *CommittedSubject { return m.committedSubject } -// Preprepare returns preprepare if this is a preprepare message. -func (m *Message) Preprepare() *Preprepare { - return m.prePrepare -} - // PreprepareV2 returns preprepare if this is a preprepare message. func (m *Message) PreprepareV2() *PreprepareV2 { return m.prePrepareV2 @@ -848,25 +575,6 @@ func (m *Message) Prepare() *Subject { return m.prepare } -// TryRoundChange returns a round change if this is a round change message. -func (m *Message) TryRoundChange() (*RoundChange, error) { - if m.roundChange != nil { - return m.roundChange, nil - } - if m.Code != MsgRoundChange { - return nil, fmt.Errorf("expected round change message, received code: %d", m.Code) - } - if err := m.DecodeMessage(); err != nil { - return nil, err - } - return m.roundChange, nil -} - -// RoundChange returns a round change if this is a round change message. -func (m *Message) RoundChange() *RoundChange { - return m.roundChange -} - // RoundChangeV2 returns a round change v2 if this is a round change v2 message. func (m *Message) RoundChangeV2() *RoundChangeV2 { return m.roundChangeV2 diff --git a/consensus/istanbul/types_test.go b/consensus/istanbul/types_test.go index 5439b393a0..2f2a9d6fa9 100644 --- a/consensus/istanbul/types_test.go +++ b/consensus/istanbul/types_test.go @@ -26,7 +26,6 @@ import ( "github.com/celo-org/celo-blockchain/core/types" "github.com/celo-org/celo-blockchain/rlp" "golang.org/x/crypto/sha3" - "gotest.tools/assert" ) // testHasher is the helper tool for transaction/receipt list hashing. @@ -135,32 +134,6 @@ func dummyMessage(code uint64) *Message { return msg } -func dummyRoundChangeMessage() *Message { - msg := NewPrepareMessage(dummySubject(), common.HexToAddress("AABB")) - // Set empty rather than nil signature since this is how rlp decodes non - // existent slices. - msg.Signature = []byte{} - msg.Code = MsgRoundChange - roundChange := &RoundChange{ - View: &View{ - Round: common.Big1, - Sequence: common.Big2, - }, - PreparedCertificate: PreparedCertificate{ - PrepareOrCommitMessages: []Message{}, - Proposal: dummyBlock(2), - }, - } - setMessageBytes(msg, roundChange) - return msg -} - -func dummyRoundChangeCertificate() *RoundChangeCertificate { - return &RoundChangeCertificate{ - RoundChangeMessages: []Message{*dummyRoundChangeMessage(), *dummyRoundChangeMessage(), *dummyRoundChangeMessage()}, - } -} - func dummyPreparedCertificate() *PreparedCertificate { return &PreparedCertificate{ PrepareOrCommitMessages: []Message{*dummyMessage(42), *dummyMessage(32), *dummyMessage(15)}, @@ -210,85 +183,6 @@ func TestMessageRLPEncoding(t *testing.T) { } } -func TestRoundChangeCertificateRLPEncoding(t *testing.T) { - var result, original *RoundChangeCertificate - original = dummyRoundChangeCertificate() - - rawVal, err := rlp.EncodeToBytes(original) - if err != nil { - t.Fatalf("Error %v", err) - } - - if err = rlp.DecodeBytes(rawVal, &result); err != nil { - t.Fatalf("Error %v", err) - } - - assert.Equal(t, len(original.RoundChangeMessages), len(original.RoundChangeMessages)) - o1 := original.RoundChangeMessages[0] - r1 := result.RoundChangeMessages[0] - if !reflect.DeepEqual(o1.Code, r1.Code) { - t.Fatalf("RLP Encode/Decode mismatch at first Code") - } - - if !reflect.DeepEqual(o1.Code, r1.Code) { - t.Fatalf("RLP Encode/Decode mismatch at first Code") - } - - if !reflect.DeepEqual(o1.Address, r1.Address) { - t.Fatalf("RLP Encode/Decode mismatch at first Address") - } - - if !reflect.DeepEqual(o1.Signature, r1.Signature) { - t.Fatalf("RLP Encode/Decode mismatch at first Signature") - } - - if !reflect.DeepEqual(o1.Msg, r1.Msg) { - t.Fatalf("RLP Encode/Decode mismatch at first internal Msg bytes. %v ----- %v", o1.Msg, r1.Msg) - } - - original.RoundChangeMessages[0].prepare = nil - original.RoundChangeMessages[1].prepare = nil - original.RoundChangeMessages[2].prepare = nil - result.RoundChangeMessages[0].roundChange = nil - result.RoundChangeMessages[1].roundChange = nil - result.RoundChangeMessages[2].roundChange = nil - if !reflect.DeepEqual(original, result) { - t.Fatalf("RLP Encode/Decode mismatch. Got %v, expected %v", result, original) - } -} - -func TestPreprepareRLPEncoding(t *testing.T) { - var result, original *Preprepare - original = &Preprepare{ - View: dummyView(), - RoundChangeCertificate: *dummyRoundChangeCertificate(), - Proposal: dummyBlock(1), - } - - rawVal, err := rlp.EncodeToBytes(original) - if err != nil { - t.Fatalf("Error %v", err) - } - - if err = rlp.DecodeBytes(rawVal, &result); err != nil { - t.Fatalf("Error %v", err) - } - - o := original.RoundChangeCertificate - o.RoundChangeMessages[0].prepare = nil - o.RoundChangeMessages[1].prepare = nil - o.RoundChangeMessages[2].prepare = nil - r := result.RoundChangeCertificate - r.RoundChangeMessages[0].roundChange = nil - r.RoundChangeMessages[1].roundChange = nil - r.RoundChangeMessages[2].roundChange = nil - - // decoded Blocks don't equal Original ones so we need to check equality differently - assertEqual(t, "RLP Encode/Decode mismatch: View", result.View, original.View) - assertEqual(t, "RLP Encode/Decode mismatch: RoundChangeCertificate", result.RoundChangeCertificate, original.RoundChangeCertificate) - assertEqual(t, "RLP Encode/Decode mismatch: BlockHash", result.Proposal.Hash(), original.Proposal.Hash()) -} - func TestPreparedCertificateRLPEncoding(t *testing.T) { var result, original *PreparedCertificate original = dummyPreparedCertificate() @@ -307,28 +201,6 @@ func TestPreparedCertificateRLPEncoding(t *testing.T) { assertEqual(t, "RLP Encode/Decode mismatch: BlockHash", result.Proposal.Hash(), original.Proposal.Hash()) } -func TestRoundChangeRLPEncoding(t *testing.T) { - var result, original *RoundChange - original = &RoundChange{ - View: dummyView(), - PreparedCertificate: *dummyPreparedCertificate(), - } - - rawVal, err := rlp.EncodeToBytes(original) - if err != nil { - t.Fatalf("Error %v", err) - } - - if err = rlp.DecodeBytes(rawVal, &result); err != nil { - t.Fatalf("Error %v", err) - } - - // decoded Blocks don't equal Original ones so we need to check equality differently - assertEqual(t, "RLP Encode/Decode mismatch: View", result.View, original.View) - assertEqual(t, "RLP Encode/Decode mismatch: PreparedCertificate.PrepareOrCommitMessages", result.PreparedCertificate.PrepareOrCommitMessages, original.PreparedCertificate.PrepareOrCommitMessages) - assertEqual(t, "RLP Encode/Decode mismatch: PreparedCertificate.BlockHash", result.PreparedCertificate.Proposal.Hash(), original.PreparedCertificate.Proposal.Hash()) -} - func TestSubjectRLPEncoding(t *testing.T) { var result, original *Subject original = dummySubject() diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 5d528b267e..e1b932933c 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math/big" - "os" "os/exec" "strings" "sync" @@ -23,17 +22,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestMain(m *testing.M) { - println("====== RUNNING e2e tests with original consensus protocol") - code1 := m.Run() - - // Activate V2 consensus block - test.BaseEthConfig.Istanbul.V2Block = big.NewInt(0) - println("====== RUNNING e2e tests with v2 consensus protocol") - code2 := m.Run() - os.Exit(code1 | code2) -} - func init() { // This statement is commented out but left here since its very useful for // debugging problems and its non trivial to construct. diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 2c65903220..4793c0dabf 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -160,9 +160,6 @@ type Config struct { // E block override (TODO: remove after the fork) OverrideEHardfork *big.Int `toml:",omitempty"` - // V2 istanbul fork block number override (TODO: remove after the fork) - OverrideV2IstanbulFork *big.Int `toml:",omitempty"` - // The minimum required peers in order for syncing to be initiated, if left // at 0 then the default will be used. MinSyncPeers int `toml:",omitempty"` @@ -179,9 +176,6 @@ func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, co if err := istanbul.ApplyParamsChainConfigToConfig(chainConfig, &config.Istanbul); err != nil { log.Crit("Invalid Configuration for Istanbul Engine", "err", err) } - if config.OverrideV2IstanbulFork != nil { - config.Istanbul.V2Block = config.OverrideV2IstanbulFork - } return istanbulBackend.New(&config.Istanbul, db) } log.Error(fmt.Sprintf("Only Istanbul Consensus is supported: %v", chainConfig)) diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 919fb61aeb..b79a743a09 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -59,10 +59,10 @@ func (c Config) MarshalTOML() (interface{}, error) { RPCGasInflationRate float64 RPCGasCap uint64 RPCTxFeeCap float64 + RPCEthCompatibility bool Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideEHardfork *big.Int `toml:",omitempty"` - OverrideV2IstanbulFork *big.Int `toml:",omitempty"` MinSyncPeers int `toml:",omitempty"` } var enc Config @@ -108,10 +108,10 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.RPCGasInflationRate = c.RPCGasInflationRate enc.RPCGasCap = c.RPCGasCap enc.RPCTxFeeCap = c.RPCTxFeeCap + enc.RPCEthCompatibility = c.RPCEthCompatibility enc.Checkpoint = c.Checkpoint enc.CheckpointOracle = c.CheckpointOracle enc.OverrideEHardfork = c.OverrideEHardfork - enc.OverrideV2IstanbulFork = c.OverrideV2IstanbulFork enc.MinSyncPeers = c.MinSyncPeers return &enc, nil } @@ -161,10 +161,10 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { RPCGasInflationRate *float64 RPCGasCap *uint64 RPCTxFeeCap *float64 + RPCEthCompatibility *bool Checkpoint *params.TrustedCheckpoint `toml:",omitempty"` CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` OverrideEHardfork *big.Int `toml:",omitempty"` - OverrideV2IstanbulFork *big.Int `toml:",omitempty"` MinSyncPeers *int `toml:",omitempty"` } var dec Config @@ -297,6 +297,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.RPCTxFeeCap != nil { c.RPCTxFeeCap = *dec.RPCTxFeeCap } + if dec.RPCEthCompatibility != nil { + c.RPCEthCompatibility = *dec.RPCEthCompatibility + } if dec.Checkpoint != nil { c.Checkpoint = dec.Checkpoint } @@ -306,9 +309,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.OverrideEHardfork != nil { c.OverrideEHardfork = dec.OverrideEHardfork } - if dec.OverrideV2IstanbulFork != nil { - c.OverrideV2IstanbulFork = dec.OverrideV2IstanbulFork - } if dec.MinSyncPeers != nil { c.MinSyncPeers = *dec.MinSyncPeers } diff --git a/params/config.go b/params/config.go index 21e1d7bb88..cc9469ae72 100644 --- a/params/config.go +++ b/params/config.go @@ -71,7 +71,6 @@ var ( BlockPeriod: 5, RequestTimeout: 3000, LookbackWindow: 12, - V2Block: big.NewInt(16068685), }, } @@ -97,7 +96,6 @@ var ( BlockPeriod: 5, RequestTimeout: 3000, LookbackWindow: 12, - V2Block: big.NewInt(13612887), }, } @@ -123,7 +121,6 @@ var ( BlockPeriod: 5, RequestTimeout: 10000, LookbackWindow: 12, - V2Block: big.NewInt(14287656), }, } @@ -255,8 +252,6 @@ type IstanbulConfig struct { // have timeouts of this + additional time that increases with round // number. RequestTimeout uint64 `json:"requesttimeout,omitempty"` - - V2Block *big.Int `json:"v2block,omitempty"` // Activation block for the V2 istanbul consensus fork (nil = no fork, 0 = already activated) } // String implements the stringer interface, returning the consensus engine details.