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.

398 lines
13 KiB

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