You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
13 KiB

  1. package consensus
  2. import (
  3. "fmt"
  4. tmcon "github.com/tendermint/tendermint/consensus"
  5. cstypes "github.com/tendermint/tendermint/consensus/types"
  6. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  7. "github.com/tendermint/tendermint/types"
  8. )
  9. // MisbehaviorList encompasses a list of all possible behaviors
  10. var MisbehaviorList = map[string]Misbehavior{
  11. "double-prevote": DoublePrevoteMisbehavior(),
  12. }
  13. type Misbehavior struct {
  14. Name string
  15. EnterPropose func(cs *State, height int64, round int32)
  16. EnterPrevote func(cs *State, height int64, round int32)
  17. EnterPrecommit func(cs *State, height int64, round int32)
  18. ReceivePrevote func(cs *State, prevote *types.Vote)
  19. ReceivePrecommit func(cs *State, precommit *types.Vote)
  20. ReceiveProposal func(cs *State, proposal *types.Proposal) error
  21. }
  22. // BEHAVIORS
  23. func DefaultMisbehavior() Misbehavior {
  24. return Misbehavior{
  25. Name: "default",
  26. EnterPropose: defaultEnterPropose,
  27. EnterPrevote: defaultEnterPrevote,
  28. EnterPrecommit: defaultEnterPrecommit,
  29. ReceivePrevote: defaultReceivePrevote,
  30. ReceivePrecommit: defaultReceivePrecommit,
  31. ReceiveProposal: defaultReceiveProposal,
  32. }
  33. }
  34. // DoublePrevoteMisbehavior will make a node prevote both nil and a block in the same
  35. // height and round.
  36. func DoublePrevoteMisbehavior() Misbehavior {
  37. b := DefaultMisbehavior()
  38. b.Name = "double-prevote"
  39. b.EnterPrevote = func(cs *State, height int64, round int32) {
  40. // If a block is locked, prevote that.
  41. if cs.LockedBlock != nil {
  42. cs.Logger.Info("enterPrevote: Already locked on a block, prevoting locked block")
  43. cs.signAddVote(tmproto.PrevoteType, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header())
  44. return
  45. }
  46. // If ProposalBlock is nil, prevote nil.
  47. if cs.ProposalBlock == nil {
  48. cs.Logger.Info("enterPrevote: ProposalBlock is nil")
  49. cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
  50. return
  51. }
  52. // Validate proposal block
  53. err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock)
  54. if err != nil {
  55. // ProposalBlock is invalid, prevote nil.
  56. cs.Logger.Error("enterPrevote: ProposalBlock is invalid", "err", err)
  57. cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
  58. return
  59. }
  60. if cs.sw == nil {
  61. cs.Logger.Error("nil switch")
  62. return
  63. }
  64. prevote, err := cs.signVote(tmproto.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header())
  65. if err != nil {
  66. cs.Logger.Error("enterPrevote: Unable to sign block", "err", err)
  67. }
  68. nilPrevote, err := cs.signVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
  69. if err != nil {
  70. cs.Logger.Error("enterPrevote: Unable to sign block", "err", err)
  71. }
  72. // add our own vote
  73. cs.sendInternalMessage(msgInfo{&tmcon.VoteMessage{Vote: prevote}, ""})
  74. cs.Logger.Info("Sending conflicting votes")
  75. peers := cs.sw.Peers().List()
  76. // there has to be at least two other peers connected else this behavior works normally
  77. for idx, peer := range peers {
  78. if idx%2 == 0 { // sign the proposal block
  79. peer.Send(VoteChannel, tmcon.MustEncode(&tmcon.VoteMessage{Vote: prevote}))
  80. } else { // sign a nil block
  81. peer.Send(VoteChannel, tmcon.MustEncode(&tmcon.VoteMessage{Vote: nilPrevote}))
  82. }
  83. }
  84. }
  85. return b
  86. }
  87. // DEFAULTS
  88. func defaultEnterPropose(cs *State, height int64, round int32) {
  89. logger := cs.Logger.With("height", height, "round", round)
  90. // If we don't get the proposal and all block parts quick enough, enterPrevote
  91. cs.scheduleTimeout(cs.config.Propose(round), height, round, cstypes.RoundStepPropose)
  92. // Nothing more to do if we're not a validator
  93. if cs.privValidator == nil {
  94. logger.Debug("This node is not a validator")
  95. return
  96. }
  97. logger.Debug("This node is a validator")
  98. pubKey, err := cs.privValidator.GetPubKey()
  99. if err != nil {
  100. // If this node is a validator & proposer in the currentx round, it will
  101. // miss the opportunity to create a block.
  102. logger.Error("Error on retrival of pubkey", "err", err)
  103. return
  104. }
  105. address := pubKey.Address()
  106. // if not a validator, we're done
  107. if !cs.Validators.HasAddress(address) {
  108. logger.Debug("This node is not a validator", "addr", address, "vals", cs.Validators)
  109. return
  110. }
  111. if cs.isProposer(address) {
  112. logger.Info("enterPropose: Our turn to propose",
  113. "proposer",
  114. address,
  115. "privValidator",
  116. cs.privValidator)
  117. cs.decideProposal(height, round)
  118. } else {
  119. logger.Info("enterPropose: Not our turn to propose",
  120. "proposer",
  121. cs.Validators.GetProposer().Address,
  122. "privValidator",
  123. cs.privValidator)
  124. }
  125. }
  126. func defaultEnterPrevote(cs *State, height int64, round int32) {
  127. logger := cs.Logger.With("height", height, "round", round)
  128. // If a block is locked, prevote that.
  129. if cs.LockedBlock != nil {
  130. logger.Info("enterPrevote: Already locked on a block, prevoting locked block")
  131. cs.signAddVote(tmproto.PrevoteType, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header())
  132. return
  133. }
  134. // If ProposalBlock is nil, prevote nil.
  135. if cs.ProposalBlock == nil {
  136. logger.Info("enterPrevote: ProposalBlock is nil")
  137. cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
  138. return
  139. }
  140. // Validate proposal block
  141. err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock)
  142. if err != nil {
  143. // ProposalBlock is invalid, prevote nil.
  144. logger.Error("enterPrevote: ProposalBlock is invalid", "err", err)
  145. cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
  146. return
  147. }
  148. // Prevote cs.ProposalBlock
  149. // NOTE: the proposal signature is validated when it is received,
  150. // and the proposal block parts are validated as they are received (against the merkle hash in the proposal)
  151. logger.Info("enterPrevote: ProposalBlock is valid")
  152. cs.signAddVote(tmproto.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header())
  153. }
  154. func defaultEnterPrecommit(cs *State, height int64, round int32) {
  155. logger := cs.Logger.With("height", height, "round", round)
  156. // check for a polka
  157. blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority()
  158. // If we don't have a polka, we must precommit nil.
  159. if !ok {
  160. if cs.LockedBlock != nil {
  161. logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit while we're locked. Precommitting nil")
  162. } else {
  163. logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit. Precommitting nil.")
  164. }
  165. cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
  166. return
  167. }
  168. // At this point +2/3 prevoted for a particular block or nil.
  169. _ = cs.eventBus.PublishEventPolka(cs.RoundStateEvent())
  170. // the latest POLRound should be this round.
  171. polRound, _ := cs.Votes.POLInfo()
  172. if polRound < round {
  173. panic(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound))
  174. }
  175. // +2/3 prevoted nil. Unlock and precommit nil.
  176. if len(blockID.Hash) == 0 {
  177. if cs.LockedBlock == nil {
  178. logger.Info("enterPrecommit: +2/3 prevoted for nil.")
  179. } else {
  180. logger.Info("enterPrecommit: +2/3 prevoted for nil. Unlocking")
  181. cs.LockedRound = -1
  182. cs.LockedBlock = nil
  183. cs.LockedBlockParts = nil
  184. _ = cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
  185. }
  186. cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
  187. return
  188. }
  189. // At this point, +2/3 prevoted for a particular block.
  190. // If we're already locked on that block, precommit it, and update the LockedRound
  191. if cs.LockedBlock.HashesTo(blockID.Hash) {
  192. logger.Info("enterPrecommit: +2/3 prevoted locked block. Relocking")
  193. cs.LockedRound = round
  194. _ = cs.eventBus.PublishEventRelock(cs.RoundStateEvent())
  195. cs.signAddVote(tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader)
  196. return
  197. }
  198. // If +2/3 prevoted for proposal block, stage and precommit it
  199. if cs.ProposalBlock.HashesTo(blockID.Hash) {
  200. logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash)
  201. // Validate the block.
  202. if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil {
  203. panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
  204. }
  205. cs.LockedRound = round
  206. cs.LockedBlock = cs.ProposalBlock
  207. cs.LockedBlockParts = cs.ProposalBlockParts
  208. _ = cs.eventBus.PublishEventLock(cs.RoundStateEvent())
  209. cs.signAddVote(tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader)
  210. return
  211. }
  212. // There was a polka in this round for a block we don't have.
  213. // Fetch that block, unlock, and precommit nil.
  214. // The +2/3 prevotes for this round is the POL for our unlock.
  215. logger.Info("enterPrecommit: +2/3 prevotes for a block we don't have. Voting nil", "blockID", blockID)
  216. cs.LockedRound = -1
  217. cs.LockedBlock = nil
  218. cs.LockedBlockParts = nil
  219. if !cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) {
  220. cs.ProposalBlock = nil
  221. cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader)
  222. }
  223. _ = cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
  224. cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
  225. }
  226. func defaultReceivePrevote(cs *State, vote *types.Vote) {
  227. height := cs.Height
  228. prevotes := cs.Votes.Prevotes(vote.Round)
  229. // If +2/3 prevotes for a block or nil for *any* round:
  230. if blockID, ok := prevotes.TwoThirdsMajority(); ok {
  231. // There was a polka!
  232. // If we're locked but this is a recent polka, unlock.
  233. // If it matches our ProposalBlock, update the ValidBlock
  234. // Unlock if `cs.LockedRound < vote.Round <= cs.Round`
  235. // NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round
  236. if (cs.LockedBlock != nil) &&
  237. (cs.LockedRound < vote.Round) &&
  238. (vote.Round <= cs.Round) &&
  239. !cs.LockedBlock.HashesTo(blockID.Hash) {
  240. cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
  241. cs.LockedRound = -1
  242. cs.LockedBlock = nil
  243. cs.LockedBlockParts = nil
  244. _ = cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
  245. }
  246. // Update Valid* if we can.
  247. // NOTE: our proposal block may be nil or not what received a polka..
  248. if len(blockID.Hash) != 0 && (cs.ValidRound < vote.Round) && (vote.Round == cs.Round) {
  249. if cs.ProposalBlock.HashesTo(blockID.Hash) {
  250. cs.Logger.Info(
  251. "Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round)
  252. cs.ValidRound = vote.Round
  253. cs.ValidBlock = cs.ProposalBlock
  254. cs.ValidBlockParts = cs.ProposalBlockParts
  255. } else {
  256. cs.Logger.Info(
  257. "Valid block we don't know about. Set ProposalBlock=nil",
  258. "proposal", cs.ProposalBlock.Hash(), "blockID", blockID.Hash)
  259. // We're getting the wrong block.
  260. cs.ProposalBlock = nil
  261. }
  262. if !cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) {
  263. cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader)
  264. }
  265. cs.evsw.FireEvent(types.EventValidBlock, &cs.RoundState)
  266. _ = cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent())
  267. }
  268. }
  269. // If +2/3 prevotes for *anything* for future round:
  270. switch {
  271. case cs.Round < vote.Round && prevotes.HasTwoThirdsAny():
  272. // Round-skip if there is any 2/3+ of votes ahead of us
  273. cs.enterNewRound(height, vote.Round)
  274. case cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step: // current round
  275. blockID, ok := prevotes.TwoThirdsMajority()
  276. if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) {
  277. cs.enterPrecommit(height, vote.Round)
  278. } else if prevotes.HasTwoThirdsAny() {
  279. cs.enterPrevoteWait(height, vote.Round)
  280. }
  281. case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round:
  282. // If the proposal is now complete, enter prevote of cs.Round.
  283. if cs.isProposalComplete() {
  284. cs.enterPrevote(height, cs.Round)
  285. }
  286. }
  287. }
  288. func defaultReceivePrecommit(cs *State, vote *types.Vote) {
  289. height := cs.Height
  290. precommits := cs.Votes.Precommits(vote.Round)
  291. cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort())
  292. blockID, ok := precommits.TwoThirdsMajority()
  293. if ok {
  294. // Executed as TwoThirdsMajority could be from a higher round
  295. cs.enterNewRound(height, vote.Round)
  296. cs.enterPrecommit(height, vote.Round)
  297. if len(blockID.Hash) != 0 {
  298. cs.enterCommit(height, vote.Round)
  299. if cs.config.SkipTimeoutCommit && precommits.HasAll() {
  300. cs.enterNewRound(cs.Height, 0)
  301. }
  302. } else {
  303. cs.enterPrecommitWait(height, vote.Round)
  304. }
  305. } else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() {
  306. cs.enterNewRound(height, vote.Round)
  307. cs.enterPrecommitWait(height, vote.Round)
  308. }
  309. }
  310. func defaultReceiveProposal(cs *State, proposal *types.Proposal) error {
  311. // Already have one
  312. // TODO: possibly catch double proposals
  313. if cs.Proposal != nil {
  314. return nil
  315. }
  316. // Does not apply
  317. if proposal.Height != cs.Height || proposal.Round != cs.Round {
  318. return nil
  319. }
  320. // Verify POLRound, which must be -1 or in range [0, proposal.Round).
  321. if proposal.POLRound < -1 ||
  322. (proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) {
  323. return ErrInvalidProposalPOLRound
  324. }
  325. p := proposal.ToProto()
  326. // Verify signature
  327. if !cs.Validators.GetProposer().PubKey.VerifySignature(
  328. types.ProposalSignBytes(cs.state.ChainID, p), proposal.Signature) {
  329. return ErrInvalidProposalSignature
  330. }
  331. proposal.Signature = p.Signature
  332. cs.Proposal = proposal
  333. // We don't update cs.ProposalBlockParts if it is already set.
  334. // This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round.
  335. // TODO: We can check if Proposal is for a different block as this is a sign of misbehavior!
  336. if cs.ProposalBlockParts == nil {
  337. cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartSetHeader)
  338. }
  339. cs.Logger.Info("Received proposal", "proposal", proposal)
  340. return nil
  341. }