tendermint: the same transaction appears in two different blocks

Tendermint version (use tendermint version or git rev-parse --verify HEAD if installed from source): v0.33.9

ABCI app (name for built-in, URL for self-written if it’s publicly available): cosmos v0.39.2,okexchain v0.16.6

Environment:

  • OS (e.g. from /etc/os-release): mac os 11.2.1
  • Install tools: make
  • Others:

What happened:

  1. the same transaction appears in two different blocks

What you expected to happen:

  1. the same transaction should not appears in two different blocks, that is to say that one txhash should only appear once on the whole block chain.

Have you tried the latest version: yes/no no

How to reproduce it (as minimally and precisely as possible):

  1. change the code in https://github.com/tendermint/tendermint/blob/1baf670c60c1c97c42523f2c14c6c0768812856c/abci/client/client.go#L102 as follow to simulate a scenario in concurrency to accelerate scene reproduction:
var b bool = true

// Sets the callback for this ReqRes atomically.
// If reqRes is already done, calls cb immediately.
// NOTE: reqRes.cb should not change if reqRes.done.
// NOTE: only one callback is supported.
func (reqRes *ReqRes) SetCallback(cb func(res *types.Response)) {
	reqRes.mtx.Lock()

	if reqRes.done {
		reqRes.mtx.Unlock()
		if b {
			b = false
			time.Sleep(time.Second*10)
		}
		cb(reqRes.Response)
		return
	}

	reqRes.cb = cb
	reqRes.mtx.Unlock()
}
  1. send two txs by a account with sync mode, tx1 with sequence 1, tx2 with sequence 2. The sequence of the account on chain is 1.

  2. all txs are added into mempool, the tx2 will be added into mempool befor tx1. Then tx2 is proposed into block ahead of tx1, so the tx2’s sequence is not equal to the sequence on chain when executing antehandler in deliverTx.

  3. the tx1’s sequence is satisfied. And the tx2 will not be removed when call recheckTx, because the tx2’s sequence is satisfied after the tx1 handled, so tx2 is proposed into the next block.

Logs (paste a small part showing an error (< 10 lines) or link a pastebin, gist, etc. containing more of the log file):

Config (you can paste only the changes you’ve made):

node command runtime flags:

Please provide the output from the http://<ip>:<port>/dump_consensus_state RPC endpoint for consensus bugs

Anything else we need to know:

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 19 (19 by maintainers)

Most upvoted comments

Why is that the order of CheckTx (tx1 -> tx2) is not the same as the order of execution/inclusion in a block from the mempool? I.e. why is CheckTx tx1 -> tx2, but DeliverTx tx2 -> tx1?

@xiangjianmeng is ^ the case? did the ABCI app first see CheckTx(tx1), then CheckTx(tx2), then DeliverTx(tx2), then DeliverTx(tx1)?

Yes, it is.

Do you have a log from your ABCI app?

No, but you can add sleep code like above I have issued to reproduct this case through cosmos simulate app.

So the steps you’ve laid out all seem reasonable to me. Two different txs sent from the same account with correct nonce handling. Yet, I fail to see where the same hash is generated based on the information you provided.

AFAIK, it is the application that determines the hash of the tx based on it’s encoded value.

the same hash is the hash of tx2. That is to say tx2 will be packaged into two different blocks.

  1. tx1 and tx2 are packaged into the first block. tx2 is executed failed in the first block, because its sequence is not equal to 1, and the sequence on chain is updated to 2 after tx1 is executed on deliverTx.
  2. then tx2 is packaged into second block, and is executed succeedly because its sequence is equal to 2.
  3. so the same tx2 appears two different blocks