Let’s have 2 transactions. The second one is duplicating Input of the first one.
Both have very high fees so they are selected by miner to be included in the same block but before passing validation (because it makes no sense to validate transactions if they aren’t included in the block after because the fee is too low).

Which is the transaction is going to be rejected by not being included in the block ? The first one with a slightly lower fee than the second or as it’s the same block, the second one ?

And where is the code in Bitcoin core handling this ? I mean which source files and lines ?

  • So it’s the case of duplicate inputs for 2 different transactions in the same block. – user2284570 Apr 23 at 21:41

Which is the transaction is going to be rejected ? The first one with a slightly lower fee than the second or as it’s the same block, the second one ?

Generally speaking, miners will choose the transaction that has the highest fee, but they are not obliged to. In fact they don't need to mine any of your transactions if they don't want to.

Both have very high fees so they are selected by miner to be included in the same block.

No, the miner must choose 1 or zero not both, otherwise the block they create will be invalid if it contains a double-spend. Invalid blocks will waste the miners time and money as they will be rejected by the network.

And where is the code in Bitcoin core handling this ?

Sorry, can't help with specific location.

  • 2
    @user2284570 no, a transaction is validated before it is added to a block, that is how blocks are built: From transactions that are known to be valid, as a whole. It makes no sense to only check to see if they are valid after mining a block, that would be a waste of resources. – chytrik Apr 23 at 22:00
  • 2
    @user2284570 Just repeating this claim does not make it true. As everyone is already telling you: all transactions in the mempool are already validated, or they wouldn't be in the mempool. Miners typically select from this set of valid transactions that maximizes feerate. – Pieter Wuille Apr 24 at 16:45
  • 2
    Regarding double-spend checking: Bitcoin Core maintains two "views" of the UTXO set; one from just confirmed blocks, and one from blocks + everything in the mempool. New incoming transactions are fully validated against the latter (they're allowed to spend unconfirmed outputs). When a new block comes in, the mempool is reconciled with it (removing transactions that were confirmed or now conflict with the new block). – Pieter Wuille Apr 24 at 16:47
  • 2
    @user2284570 CheckTxInputs is passed a CCoinsViewCache object in AcceptToMemoryPoolWorker, which is backed by a CCoinsViewMemPool object, which in its turn looks up transactions in the mempool. – Pieter Wuille Apr 24 at 17:43
  • 2
    @user2284570 If you can't have a civil conversation about this, I'm done explaining. There are lots of interesting details to learn about how all of this works, but you seem to reject everything knowledgeable people try to explain. – Pieter Wuille Apr 24 at 17:51

Every transaction included in a block must be valid in order for the block to be valid. An input can only be spent once, any attempt to spend it again would be invalid.

So in your example, the entire block would be invalid and thus rejected by the network. Nodes that hear about and attempt to validate the block would get to the second transaction, see it is invalid (because one of its inputs was already spent), and reject the entire block.

Generally, miners will choose the transaction that pays them the most. The bitcoin-core rules for relaying transactions are separate from this, so they aren’t of much consequence in the real world. A miner could easily run their own modified software that builds blocks in a way they like. Even if a transaction isn’t marked RBF, there is nothing stopping a miner from including a higher paying transaction they heard about instead.

  • No, I was meaning which is the block the miner will reject if he/she uses Bitcoin core. Because of course miners rejects transactions that attempt to double spend. – user2284570 Apr 23 at 21:52
  • I added a bit to my answer, hopefully that helps. – chytrik Apr 23 at 21:58

I am going to interpret this question from a different angle: If a node were to receive a block with two transactions that spend the same input, which transaction will it return to the mempool and which will it discard?

When a node receives a block that contains two or more transactions that spend the same inputs, the block would be marked invalid and thrown away entirely. None of the transactions in that block would be considered confirmed and any transaction that was already in the mempool will remain there. A transaction was not in the mempool but was in the block will not be added to the mempool.

As such, when the node receives this invalid block and validates it, when it determines it to be invalid, it throws away the block and does nothing. It does not change the UTXO set and it does not change the mempool. This means that if any of the conflicting transactions were in the mempool when the block is received, it will remain there. If none were, none of those transactions will be added to the mempool.

So if one of the conflicting transactions paid a higher fee, but the lower fee transaction was already in the node's mempool, the lower fee transaction would stay in the mempool and the higher fee one thrown away with the block.

The code for this behavior is here. ConnectBlock takes a CCoinsViewCache which is a cache for the UTXO set. This cache only writes out the changes made to it when its Flush() function is called. ConnectBlock will return false because the block is invalid and as such, this function will return here before Flush() can be called here. So no changes will be made to the UTXO set.

Furthermore, that early return due to an invalid block means that the mempool update will not occur either. The call for the mempool update here will not be reached because of the early return and thus the the transactions that were in the mempool at the time the block was received will remain there, and the ones that weren't in the mempool will be thrown out along with the block.

  • I’m talking about rejection done by miner which I don’t know because if I’m correct such invalid block rejection is performed in CChainState::ConnectBlock in validation.cpp at UpdateCoin which ensure next input check will take into account coins spent by transactions in the same block. – user2284570 Apr 24 at 10:18
  • 2
    The check for whether an input exists is done in CheckTxInputs which is called in multiple places, including ConnectBlock as well as AcceptToMemoryPoolWorker. It is not just checked when a block received; it is always checked whenever a transaction is received, whether that is in a block or over the network as an unconfirmed transaction. – Andrew Chow Apr 24 at 17:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.