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.

400 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.Debug("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.Debug("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.Debug("enterPropose: our turn to propose",
  112. "proposer", address,
  113. "privValidator", cs.privValidator,
  114. )
  115. cs.decideProposal(height, round)
  116. } else {
  117. logger.Debug("enterPropose: not our turn to propose",
  118. "proposer", cs.Validators.GetProposer().Address,
  119. "privValidator", cs.privValidator,
  120. )
  121. }
  122. }
  123. func defaultEnterPrevote(cs *State, height int64, round int32) {
  124. logger := cs.Logger.With("height", height, "round", round)
  125. // If a block is locked, prevote that.
  126. if cs.LockedBlock != nil {
  127. logger.Debug("enterPrevote: already locked on a block, prevoting locked block")
  128. cs.signAddVote(tmproto.PrevoteType, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header())
  129. return
  130. }
  131. // If ProposalBlock is nil, prevote nil.
  132. if cs.ProposalBlock == nil {
  133. logger.Debug("enterPrevote: ProposalBlock is nil")
  134. cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
  135. return
  136. }
  137. // Validate proposal block
  138. err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock)
  139. if err != nil {
  140. // ProposalBlock is invalid, prevote nil.
  141. logger.Error("enterPrevote: ProposalBlock is invalid", "err", err)
  142. cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
  143. return
  144. }
  145. // Prevote cs.ProposalBlock
  146. // NOTE: the proposal signature is validated when it is received,
  147. // and the proposal block parts are validated as they are received (against the merkle hash in the proposal)
  148. logger.Debug("enterPrevote: ProposalBlock is valid")
  149. cs.signAddVote(tmproto.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header())
  150. }
  151. func defaultEnterPrecommit(cs *State, height int64, round int32) {
  152. logger := cs.Logger.With("height", height, "round", round)
  153. // check for a polka
  154. blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority()
  155. // If we don't have a polka, we must precommit nil.
  156. if !ok {
  157. if cs.LockedBlock != nil {
  158. logger.Debug("enterPrecommit: no +2/3 prevotes during enterPrecommit while we're locked; precommitting nil")
  159. } else {
  160. logger.Debug("enterPrecommit: no +2/3 prevotes during enterPrecommit; precommitting nil.")
  161. }
  162. cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
  163. return
  164. }
  165. // At this point +2/3 prevoted for a particular block or nil.
  166. _ = cs.eventBus.PublishEventPolka(cs.RoundStateEvent())
  167. // the latest POLRound should be this round.
  168. polRound, _ := cs.Votes.POLInfo()
  169. if polRound < round {
  170. panic(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound))
  171. }
  172. // +2/3 prevoted nil. Unlock and precommit nil.
  173. if len(blockID.Hash) == 0 {
  174. if cs.LockedBlock == nil {
  175. logger.Debug("enterPrecommit: +2/3 prevoted for nil")
  176. } else {
  177. logger.Debug("enterPrecommit: +2/3 prevoted for nil; unlocking")
  178. cs.LockedRound = -1
  179. cs.LockedBlock = nil
  180. cs.LockedBlockParts = nil
  181. _ = cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
  182. }
  183. cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
  184. return
  185. }
  186. // At this point, +2/3 prevoted for a particular block.
  187. // If we're already locked on that block, precommit it, and update the LockedRound
  188. if cs.LockedBlock.HashesTo(blockID.Hash) {
  189. logger.Debug("enterPrecommit: +2/3 prevoted locked block; relocking")
  190. cs.LockedRound = round
  191. _ = cs.eventBus.PublishEventRelock(cs.RoundStateEvent())
  192. cs.signAddVote(tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader)
  193. return
  194. }
  195. // If +2/3 prevoted for proposal block, stage and precommit it
  196. if cs.ProposalBlock.HashesTo(blockID.Hash) {
  197. logger.Debug("enterPrecommit: +2/3 prevoted proposal block; locking", "hash", blockID.Hash)
  198. // Validate the block.
  199. if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil {
  200. panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
  201. }
  202. cs.LockedRound = round
  203. cs.LockedBlock = cs.ProposalBlock
  204. cs.LockedBlockParts = cs.ProposalBlockParts
  205. _ = cs.eventBus.PublishEventLock(cs.RoundStateEvent())
  206. cs.signAddVote(tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader)
  207. return
  208. }
  209. // There was a polka in this round for a block we don't have.
  210. // Fetch that block, unlock, and precommit nil.
  211. // The +2/3 prevotes for this round is the POL for our unlock.
  212. logger.Debug("enterPrecommit: +2/3 prevotes for a block we don't have; voting nil", "blockID", blockID)
  213. cs.LockedRound = -1
  214. cs.LockedBlock = nil
  215. cs.LockedBlockParts = nil
  216. if !cs.ProposalBlockParts.HasHeader(blockID.PartSetHeader) {
  217. cs.ProposalBlock = nil
  218. cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader)
  219. }
  220. _ = cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
  221. cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
  222. }
  223. func defaultReceivePrevote(cs *State, vote *types.Vote) {
  224. height := cs.Height
  225. prevotes := cs.Votes.Prevotes(vote.Round)
  226. // If +2/3 prevotes for a block or nil for *any* round:
  227. if blockID, ok := prevotes.TwoThirdsMajority(); ok {
  228. // There was a polka!
  229. // If we're locked but this is a recent polka, unlock.
  230. // If it matches our ProposalBlock, update the ValidBlock
  231. // Unlock if `cs.LockedRound < vote.Round <= cs.Round`
  232. // NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round
  233. if (cs.LockedBlock != nil) &&
  234. (cs.LockedRound < vote.Round) &&
  235. (vote.Round <= cs.Round) &&
  236. !cs.LockedBlock.HashesTo(blockID.Hash) {
  237. cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
  238. cs.LockedRound = -1
  239. cs.LockedBlock = nil
  240. cs.LockedBlockParts = nil
  241. _ = cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
  242. }
  243. // Update Valid* if we can.
  244. // NOTE: our proposal block may be nil or not what received a polka..
  245. if len(blockID.Hash) != 0 && (cs.ValidRound < vote.Round) && (vote.Round == cs.Round) {
  246. if cs.ProposalBlock.HashesTo(blockID.Hash) {
  247. cs.Logger.Info(
  248. "Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round)
  249. cs.ValidRound = vote.Round
  250. cs.ValidBlock = cs.ProposalBlock
  251. cs.ValidBlockParts = cs.ProposalBlockParts
  252. } else {
  253. cs.Logger.Info(
  254. "valid block we do not know about; set ProposalBlock=nil",
  255. "proposal", cs.ProposalBlock.Hash(),
  256. "blockID", blockID.Hash,
  257. )
  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. }