diff --git a/api/proto/message/message.pb.go b/api/proto/message/message.pb.go index 601bce19f..8b118fbbb 100644 --- a/api/proto/message/message.pb.go +++ b/api/proto/message/message.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.23.0 -// protoc v3.6.1 +// protoc v3.12.3 // source: message.proto package message diff --git a/api/service/syncing/downloader/proto/downloader.pb.go b/api/service/syncing/downloader/proto/downloader.pb.go index 108f70e33..fb5255535 100644 --- a/api/service/syncing/downloader/proto/downloader.pb.go +++ b/api/service/syncing/downloader/proto/downloader.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.23.0 -// protoc v3.6.1 +// protoc v3.12.3 // source: downloader.proto package downloader diff --git a/cmd/harmony/main.go b/cmd/harmony/main.go index 40e3ae2dc..d489aea57 100644 --- a/cmd/harmony/main.go +++ b/cmd/harmony/main.go @@ -600,7 +600,7 @@ func setupConsensusAndNode(hc harmonyConfig, nodeConfig *nodeconfig.ConfigType) // Set the consensus ID to be the current block number viewID := currentNode.Blockchain().CurrentBlock().Header().ViewID().Uint64() - currentConsensus.SetViewID(viewID + 1) + currentConsensus.SetViewIDs(viewID + 1) utils.Logger().Info(). Uint64("viewID", viewID). Msg("Init Blockchain") diff --git a/consensus/checks.go b/consensus/checks.go index ff4ce7354..174b2aaae 100644 --- a/consensus/checks.go +++ b/consensus/checks.go @@ -57,7 +57,7 @@ func (consensus *Consensus) senderKeySanityChecks(msg *msg_pb.Message, senderKey func (consensus *Consensus) isRightBlockNumAndViewID(recvMsg *FBFTMessage, ) bool { - if recvMsg.ViewID != consensus.viewID || recvMsg.BlockNum != consensus.blockNum { + if recvMsg.ViewID != consensus.GetCurViewID() || recvMsg.BlockNum != consensus.blockNum { consensus.getLogger().Debug(). Uint64("MsgViewID", recvMsg.ViewID). Uint64("MsgBlockNum", recvMsg.BlockNum). @@ -96,7 +96,7 @@ func (consensus *Consensus) onAnnounceSanityChecks(recvMsg *FBFTMessage) bool { "[OnAnnounce] Already in ViewChanging mode, conflicing announce, doing noop", ) } else { - consensus.startViewChange(consensus.viewID + 1) + consensus.startViewChange(consensus.GetCurViewID() + 1) } } consensus.getLogger().Debug(). @@ -162,51 +162,53 @@ func (consensus *Consensus) onPreparedSanityChecks( return true } +// TODO: leo: move the sanity check to p2p message validation func (consensus *Consensus) onViewChangeSanityCheck(recvMsg *FBFTMessage) bool { // TODO: if difference is only one, new leader can still propose the same committed block to avoid another view change // TODO: new leader catchup without ignore view change message + + consensus.getLogger().Info(). + Uint64("MsgBlockNum", recvMsg.BlockNum). + Uint64("MyViewChangingID", consensus.GetViewChangingID()). + Uint64("MsgViewChangingID", recvMsg.ViewID). + Msg("onViewChange") + if consensus.blockNum > recvMsg.BlockNum { consensus.getLogger().Debug(). - Uint64("MsgBlockNum", recvMsg.BlockNum). Msg("[onViewChange] Message BlockNum Is Low") return false } if consensus.blockNum < recvMsg.BlockNum { consensus.getLogger().Warn(). - Uint64("MsgBlockNum", recvMsg.BlockNum). Msg("[onViewChange] New Leader Has Lower Blocknum") return false } if consensus.IsViewChangingMode() && - consensus.current.ViewID() > recvMsg.ViewID { + consensus.GetViewChangingID() > recvMsg.ViewID { consensus.getLogger().Warn(). - Uint64("MyViewChangingID", consensus.current.ViewID()). - Uint64("MsgViewChangingID", recvMsg.ViewID). Msg("[onViewChange] ViewChanging ID Is Low") return false } - if recvMsg.ViewID-consensus.current.ViewID() > MaxViewIDDiff { + if recvMsg.ViewID-consensus.GetViewChangingID() > MaxViewIDDiff { consensus.getLogger().Debug(). - Uint64("MsgViewID", recvMsg.ViewID). - Uint64("CurrentViewID", consensus.current.ViewID()). Msg("Received viewID that is MaxViewIDDiff (100) further from the current viewID!") return false } + if len(recvMsg.SenderPubkeys) != 1 { + consensus.getLogger().Error().Msg("[onViewChange] multiple signers in view change message.") + return false + } return true } +// TODO: leo: move the sanity check to p2p message validation func (consensus *Consensus) onNewViewSanityCheck(recvMsg *FBFTMessage) bool { - if recvMsg.ViewID < consensus.viewID { + if recvMsg.ViewID < consensus.GetCurViewID() { consensus.getLogger().Warn(). - Uint64("LastSuccessfulConsensusViewID", consensus.viewID). + Uint64("LastSuccessfulConsensusViewID", consensus.GetCurViewID()). Uint64("MsgViewChangingID", recvMsg.ViewID). Msg("[onNewView] ViewID should be larger than the viewID of the last successful consensus") return false } - if !consensus.IsViewChangingMode() { - consensus.getLogger().Warn(). - Msg("[onNewView] Not in ViewChanging mode, ignoring the new view message") - return false - } return true } diff --git a/consensus/consensus.go b/consensus/consensus.go index 096a93ef8..84dedf84d 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -77,7 +77,6 @@ type Consensus struct { // blockNum: the next blockNumber that FBFT is going to agree on, // should be equal to the blockNumber of next block blockNum uint64 - viewID uint64 // Blockhash - 32 byte blockHash [32]byte // Block to run consensus on @@ -199,7 +198,7 @@ func New( // viewID has to be initialized as the height of // the blockchain during initialization as it was // displayed on explorer as Height right now - consensus.viewID = 0 + consensus.SetCurViewID(0) consensus.ShardID = shard consensus.syncReadyChan = make(chan struct{}) consensus.syncNotReadyChan = make(chan struct{}) diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index 42d7ee700..4cabbb759 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -72,11 +72,6 @@ func (consensus *Consensus) signAndMarshalConsensusMessage(message *msg_pb.Messa return marshaledMessage, nil } -// GetViewID returns the consensus ID -func (consensus *Consensus) GetViewID() uint64 { - return consensus.viewID -} - // UpdatePublicKeys updates the PublicKeys for // quorum on current subcommittee, protected by a mutex func (consensus *Consensus) UpdatePublicKeys(pubKeys []bls_cosi.PublicKeyWrapper) int64 { @@ -199,12 +194,6 @@ func (consensus *Consensus) IsValidatorInCommittee(pubKey bls.SerializedPublicKe return consensus.Decider.IndexOf(pubKey) != -1 } -// SetViewID set the viewID to the height of the blockchain -func (consensus *Consensus) SetViewID(height uint64) { - consensus.viewID = height - consensus.current.viewID = height -} - // SetMode sets the mode of consensus func (consensus *Consensus) SetMode(m Mode) { consensus.current.SetMode(m) @@ -232,26 +221,20 @@ func (consensus *Consensus) checkViewID(msg *FBFTMessage) error { //in syncing mode, node accepts incoming messages without viewID/leaderKey checking //so only set mode to normal when new node enters consensus and need checking viewID consensus.current.SetMode(Normal) - consensus.viewID = msg.ViewID - consensus.current.SetViewID(msg.ViewID) + consensus.SetViewIDs(msg.ViewID) if len(msg.SenderPubkeys) != 1 { return errors.New("Leader message can not have multiple sender keys") } consensus.LeaderPubKey = msg.SenderPubkeys[0] consensus.IgnoreViewIDCheck.UnSet() consensus.consensusTimeout[timeoutConsensus].Start() - utils.Logger().Debug(). - Uint64("viewID", consensus.viewID). + consensus.getLogger().Debug(). Str("leaderKey", consensus.LeaderPubKey.Bytes.Hex()). - Msg("viewID and leaderKey override") - utils.Logger().Debug(). - Uint64("viewID", consensus.viewID). - Uint64("block", consensus.blockNum). Msg("Start consensus timer") return nil - } else if msg.ViewID > consensus.viewID { + } else if msg.ViewID > consensus.GetCurViewID() { return consensus_engine.ErrViewIDNotMatch - } else if msg.ViewID < consensus.viewID { + } else if msg.ViewID < consensus.GetCurViewID() { return errors.New("view ID belongs to the past") } return nil @@ -282,7 +265,7 @@ func (consensus *Consensus) ReadSignatureBitmapPayload( func (consensus *Consensus) getLogger() *zerolog.Logger { logger := utils.Logger().With(). Uint64("myBlock", consensus.blockNum). - Uint64("myViewID", consensus.viewID). + Uint64("myViewID", consensus.GetCurViewID()). Interface("phase", consensus.phase). Str("mode", consensus.current.Mode().String()). Logger() @@ -470,12 +453,12 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { if len(curHeader.ShardState()) == 0 && curHeader.Number().Uint64() != 0 { leaderPubKey, err := consensus.getLeaderPubKeyFromCoinbase(curHeader) if err != nil || leaderPubKey == nil { - consensus.getLogger().Debug().Err(err). + consensus.getLogger().Error().Err(err). Msg("[UpdateConsensusInformation] Unable to get leaderPubKey from coinbase") consensus.IgnoreViewIDCheck.Set() hasError = true } else { - consensus.getLogger().Debug(). + consensus.getLogger().Info(). Str("leaderPubKey", leaderPubKey.Bytes.Hex()). Msg("[UpdateConsensusInformation] Most Recent LeaderPubKey Updated Based on BlockChain") consensus.LeaderPubKey = leaderPubKey @@ -494,10 +477,8 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { if (oldLeader != nil && consensus.LeaderPubKey != nil && !consensus.LeaderPubKey.Object.IsEqual(oldLeader.Object)) && consensus.IsLeader() { go func() { - utils.Logger().Debug(). + consensus.getLogger().Info(). Str("myKey", myPubKeys.SerializeToHexStr()). - Uint64("viewID", consensus.viewID). - Uint64("block", consensus.blockNum). Msg("[UpdateConsensusInformation] I am the New Leader") consensus.ReadySignal <- struct{}{} }() @@ -553,3 +534,20 @@ func (consensus *Consensus) addViewIDKeyIfNotExist(viewID uint64) { consensus.viewIDBitmap[viewID] = viewIDBitmap } } + +// SetViewIDs set both current view ID and view changing ID to the height +// of the blockchain. It is used during client startup to recover the state +func (consensus *Consensus) SetViewIDs(height uint64) { + consensus.SetCurViewID(height) + consensus.SetViewChangingID(height) +} + +// SetCurViewID set the current view ID +func (consensus *Consensus) SetCurViewID(viewID uint64) { + consensus.current.SetCurViewID(viewID) +} + +// SetViewChangingID set the current view change ID +func (consensus *Consensus) SetViewChangingID(viewID uint64) { + consensus.current.SetViewChangingID(viewID) +} diff --git a/consensus/consensus_service_test.go b/consensus/consensus_service_test.go index d3c4aeb01..9885d9ba8 100644 --- a/consensus/consensus_service_test.go +++ b/consensus/consensus_service_test.go @@ -31,7 +31,7 @@ func TestPopulateMessageFields(t *testing.T) { if err != nil { t.Fatalf("Cannot craeate consensus: %v", err) } - consensus.viewID = 2 + consensus.SetCurViewID(2) blockHash := [32]byte{} consensus.blockHash = blockHash @@ -72,7 +72,7 @@ func TestSignAndMarshalConsensusMessage(t *testing.T) { if err != nil { t.Fatalf("Cannot craeate consensus: %v", err) } - consensus.viewID = 2 + consensus.SetCurViewID(2) consensus.blockHash = [32]byte{} msg := &msg_pb.Message{} @@ -105,8 +105,8 @@ func TestSetViewID(t *testing.T) { } height := uint64(1000) - consensus.SetViewID(height) - if consensus.viewID != height { - t.Errorf("Cannot set consensus ID. Got: %v, Expected: %v", consensus.viewID, height) + consensus.SetViewIDs(height) + if consensus.GetCurViewID() != height { + t.Errorf("Cannot set consensus ID. Got: %v, Expected: %v", consensus.GetCurViewID(), height) } } diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index b28938e91..a9a480b9a 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -43,7 +43,7 @@ func TestConsensusInitialization(t *testing.T) { // State / consensus.current assert.Equal(t, state.mode, consensus.current.mode) - assert.Equal(t, state.viewID, consensus.current.viewID) + assert.Equal(t, state.GetViewChangingID(), consensus.current.GetViewChangingID()) // FBFT timeout assert.IsType(t, make(map[TimeoutType]*utils.Timeout), consensus.consensusTimeout) @@ -58,7 +58,7 @@ func TestConsensusInitialization(t *testing.T) { assert.Equal(t, multiBLSPrivateKey.GetPublicKeys(), consensus.GetPublicKeys()) // Misc - assert.Equal(t, uint64(0), consensus.viewID) + assert.Equal(t, uint64(0), consensus.GetViewChangingID()) assert.Equal(t, uint32(shard.BeaconChainShardID), consensus.ShardID) assert.IsType(t, make(chan struct{}), consensus.syncReadyChan) diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index b475c047b..c99bb0b53 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -163,9 +163,9 @@ func (consensus *Consensus) finalizeCommits() { if consensus.consensusTimeout[timeoutBootstrap].IsActive() { consensus.consensusTimeout[timeoutBootstrap].Stop() - consensus.getLogger().Debug().Msg("[finalizeCommits] Start consensus timer; stop bootstrap timer only once") + consensus.getLogger().Info().Msg("[finalizeCommits] Start consensus timer; stop bootstrap timer only once") } else { - consensus.getLogger().Debug().Msg("[finalizeCommits] Start consensus timer") + consensus.getLogger().Info().Msg("[finalizeCommits] Start consensus timer") } consensus.consensusTimeout[timeoutConsensus].Start() @@ -179,7 +179,7 @@ func (consensus *Consensus) finalizeCommits() { Msg("HOORAY!!!!!!! CONSENSUS REACHED!!!!!!!") // Sleep to wait for the full block time - consensus.getLogger().Debug().Msg("[finalizeCommits] Waiting for Block Time") + consensus.getLogger().Info().Msg("[finalizeCommits] Waiting for Block Time") <-time.After(time.Until(consensus.NextBlockDue)) // Send signal to Node to propose the new block for consensus @@ -281,7 +281,7 @@ func (consensus *Consensus) tryCatchup() { consensus.getLogger().Info().Msg("[TryCatchup] block found to commit") atomic.AddUint64(&consensus.blockNum, 1) - atomic.StoreUint64(&consensus.viewID, committedMsg.ViewID+1) + consensus.SetCurViewID(committedMsg.ViewID + 1) consensus.LeaderPubKey = committedMsg.SenderPubkeys[0] @@ -344,10 +344,7 @@ func (consensus *Consensus) Start( ticker := time.NewTicker(3 * time.Second) defer ticker.Stop() consensus.consensusTimeout[timeoutBootstrap].Start() - consensus.getLogger().Debug(). - Uint64("viewID", consensus.viewID). - Uint64("blockNum", consensus.blockNum). - Msg("[ConsensusMainLoop] Start bootstrap timeout (only once)") + consensus.getLogger().Info().Msg("[ConsensusMainLoop] Start bootstrap timeout (only once)") vdfInProgress := false // Set up next block due time. @@ -371,26 +368,27 @@ func (consensus *Consensus) Start( continue } if k != timeoutViewChange { - consensus.getLogger().Debug().Msg("[ConsensusMainLoop] Ops Consensus Timeout!!!") - consensus.startViewChange(consensus.viewID + 1) + consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops Consensus Timeout!!!") + viewID := consensus.GetCurViewID() + consensus.startViewChange(viewID + 1) break } else { - consensus.getLogger().Debug().Msg("[ConsensusMainLoop] Ops View Change Timeout!!!") - viewID := consensus.current.ViewID() + consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops View Change Timeout!!!") + viewID := consensus.GetViewChangingID() consensus.startViewChange(viewID + 1) break } } case <-consensus.syncReadyChan: - consensus.getLogger().Debug().Msg("[ConsensusMainLoop] syncReadyChan") + consensus.getLogger().Info().Msg("[ConsensusMainLoop] syncReadyChan") consensus.SetBlockNum(consensus.ChainReader.CurrentHeader().Number().Uint64() + 1) - consensus.SetViewID(consensus.ChainReader.CurrentHeader().ViewID().Uint64() + 1) + consensus.SetViewIDs(consensus.ChainReader.CurrentHeader().ViewID().Uint64() + 1) mode := consensus.UpdateConsensusInformation() consensus.current.SetMode(mode) consensus.getLogger().Info().Str("Mode", mode.String()).Msg("Node is IN SYNC") case <-consensus.syncNotReadyChan: - consensus.getLogger().Debug().Msg("[ConsensusMainLoop] syncNotReadyChan") + consensus.getLogger().Info().Msg("[ConsensusMainLoop] syncNotReadyChan") consensus.SetBlockNum(consensus.ChainReader.CurrentHeader().Number().Uint64() + 1) consensus.current.SetMode(Syncing) consensus.getLogger().Info().Msg("[ConsensusMainLoop] Node is OUT OF SYNC") @@ -476,7 +474,7 @@ func (consensus *Consensus) Start( startTime = time.Now() consensus.msgSender.Reset(newBlock.NumberU64()) - consensus.getLogger().Debug(). + consensus.getLogger().Info(). Int("numTxs", len(newBlock.Transactions())). Int("numStakingTxs", len(newBlock.StakingTransactions())). Time("startTime", startTime). @@ -485,23 +483,23 @@ func (consensus *Consensus) Start( consensus.announce(newBlock) case viewID := <-consensus.commitFinishChan: - consensus.getLogger().Debug().Msg("[ConsensusMainLoop] commitFinishChan") + consensus.getLogger().Info().Msg("[ConsensusMainLoop] commitFinishChan") // Only Leader execute this condition func() { consensus.mutex.Lock() defer consensus.mutex.Unlock() - if viewID == consensus.viewID { + if viewID == consensus.GetCurViewID() { consensus.finalizeCommits() } }() case <-stopChan: - consensus.getLogger().Debug().Msg("[ConsensusMainLoop] stopChan") + consensus.getLogger().Info().Msg("[ConsensusMainLoop] stopChan") return } } - consensus.getLogger().Debug().Msg("[ConsensusMainLoop] Ended.") + consensus.getLogger().Info().Msg("[ConsensusMainLoop] Ended.") }() } diff --git a/consensus/consensus_viewchange_msg.go b/consensus/consensus_viewchange_msg.go index fd2496128..fae0a9eff 100644 --- a/consensus/consensus_viewchange_msg.go +++ b/consensus/consensus_viewchange_msg.go @@ -10,7 +10,6 @@ import ( "github.com/harmony-one/harmony/api/proto" msg_pb "github.com/harmony-one/harmony/api/proto/message" bls_cosi "github.com/harmony-one/harmony/crypto/bls" - "github.com/harmony-one/harmony/internal/utils" ) // construct the view change message @@ -19,20 +18,16 @@ func (consensus *Consensus) constructViewChangeMessage(priKey *bls.PrivateKeyWra ServiceType: msg_pb.ServiceType_CONSENSUS, Type: msg_pb.MessageType_VIEWCHANGE, Request: &msg_pb.Message_Viewchange{ - Viewchange: &msg_pb.ViewChangeRequest{}, + Viewchange: &msg_pb.ViewChangeRequest{ + ViewId: consensus.GetViewChangingID(), + BlockNum: consensus.blockNum, + ShardId: consensus.ShardID, + SenderPubkey: priKey.Pub.Bytes[:], + LeaderPubkey: consensus.LeaderPubKey.Bytes[:], + }, }, } - vcMsg := message.GetViewchange() - vcMsg.ViewId = consensus.current.ViewID() - vcMsg.BlockNum = consensus.blockNum - vcMsg.ShardId = consensus.ShardID - // sender address - vcMsg.SenderPubkey = priKey.Pub.Bytes[:] - - // next leader key already updated - vcMsg.LeaderPubkey = consensus.LeaderPubKey.Bytes[:] - preparedMsgs := consensus.FBFTLog.GetMessagesByTypeSeq( msg_pb.MessageType_PREPARED, consensus.blockNum, ) @@ -41,7 +36,7 @@ func (consensus *Consensus) constructViewChangeMessage(priKey *bls.PrivateKeyWra var encodedBlock []byte if preparedMsg != nil { block := consensus.FBFTLog.GetBlockByHash(preparedMsg.BlockHash) - utils.Logger().Debug(). + consensus.getLogger().Debug(). Interface("Block", block). Interface("preparedMsg", preparedMsg). Msg("[constructViewChangeMessage] found prepared msg") @@ -58,6 +53,7 @@ func (consensus *Consensus) constructViewChangeMessage(priKey *bls.PrivateKeyWra } } + vcMsg := message.GetViewchange() var msgToSign []byte if len(encodedBlock) == 0 { msgToSign = NIL // m2 type message @@ -69,7 +65,7 @@ func (consensus *Consensus) constructViewChangeMessage(priKey *bls.PrivateKeyWra vcMsg.PreparedBlock = encodedBlock } - utils.Logger().Debug(). + consensus.getLogger().Debug(). Hex("m1Payload", vcMsg.Payload). Str("pubKey", consensus.GetPublicKeys().SerializeToHexStr()). Msg("[constructViewChangeMessage]") @@ -78,21 +74,21 @@ func (consensus *Consensus) constructViewChangeMessage(priKey *bls.PrivateKeyWra if sign != nil { vcMsg.ViewchangeSig = sign.Serialize() } else { - utils.Logger().Error().Msg("unable to serialize m1/m2 view change message signature") + consensus.getLogger().Error().Msg("unable to serialize m1/m2 view change message signature") } viewIDBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(viewIDBytes, consensus.current.ViewID()) + binary.LittleEndian.PutUint64(viewIDBytes, consensus.GetViewChangingID()) sign1 := priKey.Pri.SignHash(viewIDBytes) if sign1 != nil { vcMsg.ViewidSig = sign1.Serialize() } else { - utils.Logger().Error().Msg("unable to serialize viewID signature") + consensus.getLogger().Error().Msg("unable to serialize viewID signature") } marshaledMessage, err := consensus.signAndMarshalConsensusMessage(message, priKey.Pri) if err != nil { - utils.Logger().Error().Err(err). + consensus.getLogger().Err(err). Msg("[constructViewChangeMessage] failed to sign and marshal the viewchange message") } return proto.ConstructConsensusMessage(marshaledMessage) @@ -104,16 +100,16 @@ func (consensus *Consensus) constructNewViewMessage(viewID uint64, priKey *bls.P ServiceType: msg_pb.ServiceType_CONSENSUS, Type: msg_pb.MessageType_NEWVIEW, Request: &msg_pb.Message_Viewchange{ - Viewchange: &msg_pb.ViewChangeRequest{}, + Viewchange: &msg_pb.ViewChangeRequest{ + ViewId: consensus.GetViewChangingID(), + BlockNum: consensus.blockNum, + ShardId: consensus.ShardID, + SenderPubkey: priKey.Pub.Bytes[:], + }, }, } vcMsg := message.GetViewchange() - vcMsg.ViewId = consensus.current.ViewID() - vcMsg.BlockNum = consensus.blockNum - vcMsg.ShardId = consensus.ShardID - // sender address - vcMsg.SenderPubkey = priKey.Pub.Bytes[:] vcMsg.Payload = consensus.m1Payload if len(consensus.m1Payload) != 0 { block := consensus.FBFTLog.GetBlockByHash(consensus.blockHash) @@ -129,7 +125,7 @@ func (consensus *Consensus) constructNewViewMessage(viewID uint64, priKey *bls.P } sig2arr := consensus.GetNilSigsArray(viewID) - utils.Logger().Debug().Int("len", len(sig2arr)).Msg("[constructNewViewMessage] M2 (NIL) type signatures") + consensus.getLogger().Debug().Int("len", len(sig2arr)).Msg("[constructNewViewMessage] M2 (NIL) type signatures") if len(sig2arr) > 0 { m2Sig := bls_cosi.AggregateSig(sig2arr) vcMsg.M2Aggsigs = m2Sig.Serialize() @@ -147,7 +143,7 @@ func (consensus *Consensus) constructNewViewMessage(viewID uint64, priKey *bls.P marshaledMessage, err := consensus.signAndMarshalConsensusMessage(message, priKey.Pri) if err != nil { - utils.Logger().Error().Err(err). + consensus.getLogger().Err(err). Msg("[constructNewViewMessage] failed to sign and marshal the new view message") } return proto.ConstructConsensusMessage(marshaledMessage) diff --git a/consensus/construct.go b/consensus/construct.go index 36b662455..59b2e6131 100644 --- a/consensus/construct.go +++ b/consensus/construct.go @@ -29,7 +29,7 @@ type NetworkMessage struct { func (consensus *Consensus) populateMessageFields( request *msg_pb.ConsensusRequest, blockHash []byte, ) *msg_pb.ConsensusRequest { - request.ViewId = consensus.viewID + request.ViewId = consensus.GetCurViewID() request.BlockNum = consensus.blockNum request.ShardId = consensus.ShardID // 32 byte block hash diff --git a/consensus/construct_test.go b/consensus/construct_test.go index 660b2971d..1af732833 100644 --- a/consensus/construct_test.go +++ b/consensus/construct_test.go @@ -77,7 +77,7 @@ func TestConstructPreparedMessage(test *testing.T) { leaderPriKey.Sign(message), common.BytesToHash(consensus.blockHash[:]), consensus.blockNum, - consensus.viewID, + consensus.GetCurViewID(), ) if _, err := consensus.Decider.SubmitVote( quorum.Prepare, @@ -85,7 +85,7 @@ func TestConstructPreparedMessage(test *testing.T) { validatorPriKey.Sign(message), common.BytesToHash(consensus.blockHash[:]), consensus.blockNum, - consensus.viewID, + consensus.GetCurViewID(), ); err != nil { test.Log(err) } diff --git a/consensus/debug.go b/consensus/debug.go new file mode 100644 index 000000000..4045bd90c --- /dev/null +++ b/consensus/debug.go @@ -0,0 +1,21 @@ +package consensus + +// GetConsensusPhase returns the current phase of the consensus +func (c *Consensus) GetConsensusPhase() string { + return c.phase.String() +} + +// GetConsensusMode returns the current mode of the consensus +func (c *Consensus) GetConsensusMode() string { + return c.current.mode.String() +} + +// GetCurViewID returns the current view ID of the consensus +func (c *Consensus) GetCurViewID() uint64 { + return c.current.GetCurViewID() +} + +// GetViewChangingID returns the current view changing ID of the consensus +func (c *Consensus) GetViewChangingID() uint64 { + return c.current.GetViewChangingID() +} diff --git a/consensus/leader.go b/consensus/leader.go index 5f5fa81f5..9a961784d 100644 --- a/consensus/leader.go +++ b/consensus/leader.go @@ -110,12 +110,11 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) { // TODO(audit): make FBFT lookup using map instead of looping through all items. if !consensus.FBFTLog.HasMatchingViewAnnounce( - consensus.blockNum, consensus.viewID, recvMsg.BlockHash, + consensus.blockNum, consensus.GetCurViewID(), recvMsg.BlockHash, ) { consensus.getLogger().Debug(). Uint64("MsgViewID", recvMsg.ViewID). Uint64("MsgBlockNum", recvMsg.BlockNum). - Uint64("blockNum", consensus.blockNum). Msg("[OnPrepare] No Matching Announce message") } @@ -288,7 +287,7 @@ func (consensus *Consensus) onCommit(msg *msg_pb.Message) { //// Write - End //// Read - Start - viewID := consensus.viewID + viewID := consensus.GetCurViewID() if consensus.Decider.IsAllSigsCollected() { go func(viewID uint64) { diff --git a/consensus/view_change.go b/consensus/view_change.go index f31ad6447..66db3577e 100644 --- a/consensus/view_change.go +++ b/consensus/view_change.go @@ -28,38 +28,74 @@ const MaxViewIDDiff = 100 // State contains current mode and current viewID type State struct { - mode Mode - viewID uint64 - mux sync.Mutex + mode Mode + modeMux sync.RWMutex + + // current view id in normal mode + // it changes per successful consensus + curViewID uint64 + cViewMux sync.RWMutex + + // view changing id is used during view change mode + // it is the next view id + viewChangingID uint64 + + viewMux sync.RWMutex } // Mode return the current node mode func (pm *State) Mode() Mode { + pm.modeMux.RLock() + defer pm.modeMux.RUnlock() return pm.mode } // SetMode set the node mode as required func (pm *State) SetMode(s Mode) { - pm.mux.Lock() - defer pm.mux.Unlock() + pm.modeMux.Lock() + defer pm.modeMux.Unlock() pm.mode = s } -// ViewID return the current viewchanging id -func (pm *State) ViewID() uint64 { - return pm.viewID +// GetCurViewID return the current view id +func (pm *State) GetCurViewID() uint64 { + pm.cViewMux.RLock() + defer pm.cViewMux.RUnlock() + return pm.curViewID +} + +// SetCurViewID sets the current view id +func (pm *State) SetCurViewID(viewID uint64) { + pm.cViewMux.Lock() + defer pm.cViewMux.Unlock() + pm.curViewID = viewID +} + +// GetViewChangingID return the current view changing id +// It is meaningful during view change mode +func (pm *State) GetViewChangingID() uint64 { + pm.viewMux.RLock() + defer pm.viewMux.RUnlock() + return pm.viewChangingID } -// SetViewID sets the viewchanging id accordingly -func (pm *State) SetViewID(viewID uint64) { - pm.mux.Lock() - defer pm.mux.Unlock() - pm.viewID = viewID +// SetViewChangingID set the current view changing id +// It is meaningful during view change mode +func (pm *State) SetViewChangingID(id uint64) { + pm.viewMux.Lock() + defer pm.viewMux.Unlock() + pm.viewChangingID = id } -// GetViewID returns the current viewchange viewID -func (pm *State) GetViewID() uint64 { - return pm.viewID +// GetViewChangeDuraion return the duration of the current view change +// It increase in the power of difference betweeen view changing ID and current view ID +func (pm *State) GetViewChangeDuraion() time.Duration { + pm.viewMux.RLock() + pm.cViewMux.RLock() + defer pm.viewMux.RUnlock() + defer pm.cViewMux.RUnlock() + diff := int64(pm.viewChangingID - pm.curViewID) + return time.Duration(diff * diff * int64(viewChangeDuration)) } // switchPhase will switch FBFTPhase to nextPhase if the desirePhase equals the nextPhase @@ -126,13 +162,12 @@ func (consensus *Consensus) startViewChange(viewID uint64) { consensus.consensusTimeout[timeoutConsensus].Stop() consensus.consensusTimeout[timeoutBootstrap].Stop() consensus.current.SetMode(ViewChanging) - consensus.current.SetViewID(viewID) + consensus.SetViewChangingID(viewID) consensus.LeaderPubKey = consensus.GetNextLeaderKey() - diff := int64(viewID - consensus.viewID) - duration := time.Duration(diff * diff * int64(viewChangeDuration)) - consensus.getLogger().Info(). - Uint64("ViewChangingID", viewID). + duration := consensus.current.GetViewChangeDuraion() + consensus.getLogger().Warn(). + Uint64("viewChangingID", consensus.GetViewChangingID()). Dur("timeoutDuration", duration). Str("NextLeader", consensus.LeaderPubKey.Bytes.Hex()). Msg("[startViewChange]") @@ -151,9 +186,6 @@ func (consensus *Consensus) startViewChange(viewID uint64) { consensus.consensusTimeout[timeoutViewChange].SetDuration(duration) consensus.consensusTimeout[timeoutViewChange].Start() - consensus.getLogger().Info(). - Uint64("ViewChangingID", consensus.current.ViewID()). - Msg("[startViewChange] start view change timer") } func (consensus *Consensus) onViewChange(msg *msg_pb.Message) { @@ -182,10 +214,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) { return } - if len(recvMsg.SenderPubkeys) != 1 { - consensus.getLogger().Error().Msg("[onViewChange] multiple signers in view change message.") - return - } + // already checked the length of SenderPubkeys in onViewChangeSanityCheck senderKey := recvMsg.SenderPubkeys[0] consensus.vcLock.Lock() @@ -396,7 +425,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) { if len(consensus.m1Payload) == 0 { // TODO(Chao): explain why ReadySignal is sent only in this case but not the other case. // Make sure the newly proposed block have the correct view ID - consensus.viewID = recvMsg.ViewID + consensus.SetCurViewID(recvMsg.ViewID) go func() { consensus.ReadySignal <- struct{}{} }() @@ -447,7 +476,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) { } } - consensus.current.SetViewID(recvMsg.ViewID) + consensus.SetCurViewID(recvMsg.ViewID) msgToSend := consensus.constructNewViewMessage( recvMsg.ViewID, newLeaderPriKey, ) @@ -467,18 +496,11 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) { Msg("could not send out the NEWVIEW message") } - consensus.viewID = recvMsg.ViewID + consensus.SetCurViewID(recvMsg.ViewID) consensus.ResetViewChangeState() consensus.consensusTimeout[timeoutViewChange].Stop() consensus.consensusTimeout[timeoutConsensus].Start() - consensus.getLogger().Debug(). - Uint64("viewChangingID", consensus.current.ViewID()). - Msg("[onViewChange] New Leader Start Consensus Timer and Stop View Change Timer") - consensus.getLogger().Info(). - Str("myKey", newLeaderKey.Bytes.Hex()). - Uint64("viewID", consensus.viewID). - Uint64("block", consensus.blockNum). - Msg("[onViewChange] I am the New Leader") + consensus.getLogger().Info().Str("myKey", newLeaderKey.Bytes.Hex()).Msg("[onViewChange] I am the New Leader") } } @@ -612,8 +634,7 @@ func (consensus *Consensus) onNewView(msg *msg_pb.Message) { } // newView message verified success, override my state - consensus.viewID = recvMsg.ViewID - consensus.current.SetViewID(recvMsg.ViewID) + consensus.SetCurViewID(recvMsg.ViewID) consensus.LeaderPubKey = senderKey consensus.ResetViewChangeState() diff --git a/consensus/view_change_test.go b/consensus/view_change_test.go index 550552faf..07880634d 100644 --- a/consensus/view_change_test.go +++ b/consensus/view_change_test.go @@ -25,15 +25,11 @@ func TestBasicViewChanging(t *testing.T) { assert.Equal(t, ViewChanging, consensus.current.Mode()) // Change ViewID - assert.Equal(t, state.viewID, consensus.current.viewID) - assert.Equal(t, state.ViewID(), consensus.current.ViewID()) - assert.Equal(t, state.GetViewID(), consensus.current.GetViewID()) // Why are there two methods to retrieve the ViewID? - - newViewID := consensus.current.ViewID() + 1 - consensus.current.SetViewID(newViewID) - assert.Equal(t, newViewID, consensus.current.viewID) - assert.Equal(t, newViewID, consensus.current.ViewID()) - assert.Equal(t, newViewID, consensus.current.GetViewID()) + assert.Equal(t, state.GetViewChangingID(), consensus.current.GetViewChangingID()) + + newViewID := consensus.current.GetViewChangingID() + 1 + consensus.SetViewIDs(newViewID) + assert.Equal(t, newViewID, consensus.current.GetViewChangingID()) } func TestPhaseSwitching(t *testing.T) { diff --git a/go.mod b/go.mod index ef9ae011d..6fc8a106a 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/fjl/memsize v0.0.0-20180929194037-2a09253e352a // indirect github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c // indirect github.com/golang/mock v1.4.0 - github.com/golang/protobuf v1.4.0 + github.com/golang/protobuf v1.4.2 github.com/golangci/golangci-lint v1.22.2 github.com/gorilla/mux v1.7.4 github.com/gorilla/websocket v1.4.2 // indirect diff --git a/hmy/hmy.go b/hmy/hmy.go index 798700ffa..61c209804 100644 --- a/hmy/hmy.go +++ b/hmy/hmy.go @@ -92,6 +92,13 @@ type NodeAPI interface { ListPeer(topic string) []peer.ID ListTopic() []string ListBlockedPeer() []peer.ID + + // debug API + GetConsensusMode() string + GetConsensusPhase() string + GetConsensusViewChangingID() uint64 + GetConsensusCurViewID() uint64 + ShutDown() } // New creates a new Harmony object (including the diff --git a/node/api.go b/node/api.go index 0229f532f..e01d2146b 100644 --- a/node/api.go +++ b/node/api.go @@ -106,3 +106,23 @@ func (node *Node) APIs(harmony *hmy.Harmony) []rpc.API { }, } } + +// GetConsensusMode returns the current consensus mode +func (node *Node) GetConsensusMode() string { + return node.Consensus.GetConsensusMode() +} + +// GetConsensusPhase returns the current consensus phase +func (node *Node) GetConsensusPhase() string { + return node.Consensus.GetConsensusPhase() +} + +// GetConsensusViewChangingID returns the view changing ID +func (node *Node) GetConsensusViewChangingID() uint64 { + return node.Consensus.GetViewChangingID() +} + +// GetConsensusCurViewID returns the current view ID +func (node *Node) GetConsensusCurViewID() uint64 { + return node.Consensus.GetCurViewID() +} diff --git a/node/node.go b/node/node.go index 665a01de3..d8ed850d9 100644 --- a/node/node.go +++ b/node/node.go @@ -456,6 +456,12 @@ func (node *Node) validateShardBoundMessage( case msg_pb.MessageType_PREPARE, msg_pb.MessageType_COMMIT: return nil, nil, true, nil } + } else { + // ignore newview message if the node is not in viewchanging mode + switch m.Type { + case msg_pb.MessageType_NEWVIEW: + return nil, nil, true, nil + } } // ignore message not intended for leader, but still forward them to the network diff --git a/node/node_newblock.go b/node/node_newblock.go index 1a20c6ad3..467479f6d 100644 --- a/node/node_newblock.go +++ b/node/node_newblock.go @@ -232,7 +232,7 @@ func (node *Node) proposeNewBlock() (*types.Block, error) { } return node.Worker.FinalizeNewBlock( - sig, mask, node.Consensus.GetViewID(), + sig, mask, node.Consensus.GetCurViewID(), coinbase, crossLinksToPropose, shardState, ) } diff --git a/rpc/debug.go b/rpc/debug.go index 090e68f14..4d7c86d70 100644 --- a/rpc/debug.go +++ b/rpc/debug.go @@ -38,3 +38,31 @@ func (*PrivateDebugService) SetLogVerbosity(ctx context.Context, level int) (map utils.SetLogVerbosity(verbosity) return map[string]interface{}{"verbosity": verbosity.String()}, nil } + +// ConsensusViewChangingID return the current view changing ID to RPC +func (s *PrivateDebugService) ConsensusViewChangingID( + ctx context.Context, +) uint64 { + return s.hmy.NodeAPI.GetConsensusViewChangingID() +} + +// ConsensusCurViewID return the current view ID to RPC +func (s *PrivateDebugService) ConsensusCurViewID( + ctx context.Context, +) uint64 { + return s.hmy.NodeAPI.GetConsensusCurViewID() +} + +// GetConsensusMode return the current consensus mode +func (s *PrivateDebugService) GetConsensusMode( + ctx context.Context, +) string { + return s.hmy.NodeAPI.GetConsensusMode() +} + +// GetConsensusPhase return the current consensus mode +func (s *PrivateDebugService) GetConsensusPhase( + ctx context.Context, +) string { + return s.hmy.NodeAPI.GetConsensusPhase() +} diff --git a/scripts/install_build_tools.sh b/scripts/install_build_tools.sh index 00b68f75d..e150bb023 100755 --- a/scripts/install_build_tools.sh +++ b/scripts/install_build_tools.sh @@ -12,4 +12,4 @@ esac sed -n 's/^ _ "\([^"]*\)"$/\1/p' "${progdir}/../tools/tools.go" | \ xargs "${progdir}/goget.sh" -"${progdir}/install_protoc.sh" -V 3.6.1 +"${progdir}/install_protoc.sh" -V 3.12.3 diff --git a/scripts/install_protoc.sh b/scripts/install_protoc.sh index df64404ef..980d82cd7 100755 --- a/scripts/install_protoc.sh +++ b/scripts/install_protoc.sh @@ -58,4 +58,5 @@ curl -s -S -L -o "${filename}" "${url}" echo "Downloaded as ${filename}; unzipping into ${destdir}..." sudo unzip -o -d "${destdir}" "${filename}" echo "protoc v${version} has been installed in ${destdir}." +sudo chmod +x "${destdir}/bin/protoc" exit 0