Compare commits

...

2 Commits

Author SHA1 Message Date
1b400b9027 consensus: fix addProposalBlockPart
* When create_empty_blocks=false, we don't enterPropose until we
* receive a transaction, but if we then receive a complete proposal,
* we should enterPrevote. A guard in addProposalBlockPart was checking if
* step==Propose before calling enterPrevote, but we need it to be step<=Propose,
* since we may not have seen a tx.
* This was discovered by disabling mempool broadcast, sending txs to
* peers one a time, and observing their consensus logs.
2018-06-22 15:08:39 -04:00
2748364e1b mempool: log hashes, not whole tx 2018-06-22 15:06:43 -04:00
3 changed files with 19 additions and 9 deletions

View File

@ -600,7 +600,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) {
err = cs.setProposal(msg.Proposal)
case *BlockPartMessage:
// if the proposal is complete, we'll enterPrevote or tryFinalizeCommit
_, err = cs.addProposalBlockPart(msg.Height, msg.Part)
_, err = cs.addProposalBlockPart(msg, peerID)
if err != nil && msg.Round != cs.Round {
cs.Logger.Debug("Received block part from wrong round", "height", cs.Height, "csRound", cs.Round, "blockRound", msg.Round)
err = nil
@ -1333,17 +1333,22 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
// NOTE: block is not necessarily valid.
// Asynchronously triggers either enterPrevote (before we timeout of propose) or tryFinalizeCommit, once we have the full block.
func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part) (added bool, err error) {
func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (added bool, err error) {
height, round, part := msg.Height, msg.Round, msg.Part
// Blocks might be reused, so round mismatch is OK
if cs.Height != height {
cs.Logger.Debug("Received block part from wrong height", "height", height)
cs.Logger.Debug("Received block part from wrong height", "height", height, "round", round)
return false, nil
}
// We're not expecting a block part.
if cs.ProposalBlockParts == nil {
cs.Logger.Info("Received a block part when we're not expecting any", "height", height)
return false, nil // TODO: bad peer? Return error?
// NOTE: this can happen when we've gone to a higher round and
// then receive parts from the previous round - not necessarily a bad peer.
cs.Logger.Info("Received a block part when we're not expecting any",
"height", height, "round", round, "index", part.Index, "peer", peerID)
return false, nil
}
added, err = cs.ProposalBlockParts.AddPart(part)
@ -1377,7 +1382,7 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part) (
// procedure at this point.
}
if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() {
if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() {
// Move onto the next step
cs.enterPrevote(height, cs.Round)
} else if cs.Step == cstypes.RoundStepCommit {

View File

@ -57,6 +57,11 @@ var (
ErrMempoolIsFull = errors.New("Mempool is full")
)
// TxID is the hex encoded hash of the bytes as a types.Tx.
func TxID(tx []byte) string {
return fmt.Sprintf("%X", types.Tx(tx).Hash())
}
// Mempool is an ordered in-memory pool for transactions before they are proposed in a consensus
// round. Transaction validity is checked using the CheckTx abci message before the transaction is
// added to the pool. The Mempool uses a concurrent list structure for storing transactions that
@ -268,11 +273,11 @@ func (mem *Mempool) resCbNormal(req *abci.Request, res *abci.Response) {
tx: tx,
}
mem.txs.PushBack(memTx)
mem.logger.Info("Added good transaction", "tx", fmt.Sprintf("%X", types.Tx(tx).Hash()), "res", r)
mem.logger.Info("Added good transaction", "tx", TxID(tx), "res", r)
mem.notifyTxsAvailable()
} else {
// ignore bad transaction
mem.logger.Info("Rejected bad transaction", "tx", fmt.Sprintf("%X", types.Tx(tx).Hash()), "res", r)
mem.logger.Info("Rejected bad transaction", "tx", TxID(tx), "res", r)
// remove from cache (it might be good later)
mem.cache.Remove(tx)

View File

@ -90,7 +90,7 @@ func (memR *MempoolReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
case *TxMessage:
err := memR.Mempool.CheckTx(msg.Tx, nil)
if err != nil {
memR.Logger.Info("Could not check tx", "tx", msg.Tx, "err", err)
memR.Logger.Info("Could not check tx", "tx", TxID(msg.Tx), "err", err)
}
// broadcasting happens from go routines per peer
default: