Compare commits

...

2 Commits

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

View File

@@ -57,6 +57,11 @@ var (
ErrMempoolIsFull = errors.New("Mempool is full") 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 // 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 // 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 // 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, tx: tx,
} }
mem.txs.PushBack(memTx) 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() mem.notifyTxsAvailable()
} else { } else {
// ignore bad transaction // 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) // remove from cache (it might be good later)
mem.cache.Remove(tx) mem.cache.Remove(tx)

View File

@@ -90,7 +90,7 @@ func (memR *MempoolReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
case *TxMessage: case *TxMessage:
err := memR.Mempool.CheckTx(msg.Tx, nil) err := memR.Mempool.CheckTx(msg.Tx, nil)
if err != 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 // broadcasting happens from go routines per peer
default: default: