2

If A has gas_price == 1gwei and B has gas_price == 1gwei, how does the miner decide which tx gets executed first when ordering them into a produced block? Does it depend what mining client is used? How do the popular clients differ?

4

The transactions to mine are selected given a process that is absolutely specific to each client but even more to each miner. Indeed the transaction (tx) selection and sorting process is not part of Ethereum protocol. That is, miners can decide to select and sort transactions the way they prefer.

However, miners are mainly incentivised by earning money (enough to prevent miners desire to cheat and lose the block). Miners will try to make the most money out of each mined blocks. Most clients selection process are then oriented toward gas price only.

As you will see bellow (I will show how it works in Hyperledger Besu code), the mechanism is simple. And if a miner would like to customise it, it would only require to modify a Java file, or create a new one and change the code a bit to be able to handle multiple kind of selectors and build Besu with the modified code to use it.

You could imagine a miner who wants to implement an exclude list to prevent mining tx from specific accounts or only mine value tx and no contract tx, only ERC20 tokens transfers, whatever you can imagine, anything can be coded here. It really depends on the incentive for the miner to mine.

As Besu also now have a plugin API, we could imagine (in the future as it's not yet implemented in the API) that you could change the selection mechanism by switching a plugin and that people would provide different kinds of selection plugins.


Details on implementation in Hyperledger Besu

I can't tell for other clients, but for Hyperledger Besu for instance, the code is in this evaluateTransaction method and you can notice that the selection process works as the following (bellow is interesting part of BlockTransactionSelector.java code with my comments) :

 /*
   * Passed into the PendingTransactions, and is called on each transaction until sufficient
   * transactions are found which fill a block worth of gas.
   *
   * This function will continue to be called until the block under construction is suitably
   * full (in terms of gasLimit) and the provided transaction's gasLimit does not fit within
   * the space remaining in the block.
   *
   */
  private TransactionSelectionResult evaluateTransaction(final Transaction transaction) {
    if (isCancelled.get()) {
      throw new CancellationException("Cancelled during transaction selection.");
    }

Is the block able to include the TX (enough free space)? If yes, continue to see if we will include the tx in the bloc otherwise en the check for the current tx and check for the next one until the block is as full as possible (the default minimal fullness of a block is 80% in Besu).

    if (transactionTooLargeForBlock(transaction)) {
      if (blockOccupancyAboveThreshold()) {
        return TransactionSelectionResult.COMPLETE_OPERATION;
      } else {
        return TransactionSelectionResult.CONTINUE;
      }
    }

Then check if the gas price is equal of higher than the one the miner defined. There's a default value of 1000 Wei on Besu when you run the node. This value can be overriden by the miner using a configuration variable, no need to change the code.

    // If the gas price specified by the transaction is less than this node is willing to accept,
    // do not include it in the block.
    if (minTransactionGasPrice.compareTo(transaction.getGasPrice()) > 0) {
      return TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE;
    }

    final WorldUpdater worldStateUpdater = worldState.updater();
    final BlockHashLookup blockHashLookup = new BlockHashLookup(processableBlockHeader, blockchain);

Executes the tx in the EVM to check it generates a valid result

    final TransactionProcessor.Result result =
        transactionProcessor.processTransaction(
            blockchain,
            worldStateUpdater,
            processableBlockHeader,
            transaction,
            miningBeneficiary,
            blockHashLookup,
            false,
            TransactionValidationParams.mining());

Once executed, we check the result validity, if valid, we update what's going to become the block we will mine. Otherwise, if the reason for the tx to be invalid is because the nonce is not the right one, we just keep it in the pending list until we reach a point where the nonce is correct. We could have nonce 2 included in the list before nonce 1 for instance. If the tx is invalid for any other reason, we reject it and delete it from the pending list.

    if (!result.isInvalid()) {
      worldStateUpdater.commit();
      updateTransactionResultTracking(transaction, result);
    } else {
      // If the transaction has an incorrect nonce, leave it in the pool and continue
      if (result
          .getValidationResult()
          .getInvalidReason()
          .equals(TransactionValidator.TransactionInvalidReason.INCORRECT_NONCE)) {
        return TransactionSelectionResult.CONTINUE;
      }
      // If the transaction was invalid for any other reason, delete it, and continue.
      return TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE;
    }
    return TransactionSelectionResult.CONTINUE;
  }

In your case, two tx with the same gas price will be included if they reach the minimal requirement of being valid (result and nonce), having a gas price at least equal to what miner defined (or default value) and and can fit in the block. They will then be included both in the order they were included in the pending list. The first to be added will be the first included in the block (FIFO).

There are of course some edge cases where for instance two tx from the same account are in the pending list and are added in reverse nonce order (nonce 2 added before nonce 1), then depending on the moment it happens (is the block full or not) it may end in both tx not being included in the same block.

| improve this answer | |
  • Yeah miners can put in anything they want in principle and choose their own methods, but I'm wanting to know what happens in practice. Most miners just run some mining software on default settings. My question was more around what the default behavior is for popular node software like parity of geth. I've observed that transactions with the same gas price are ordered randomly in a block, and not in the order that the miner heard them. But maybe there's some ordering that I'm unaware of (e.g. ordering by hash in the case of equal gas prices)? – the_cranky_panda Apr 11 at 20:10
  • 1
    @the_cranky_panda you may be interested in reading the following Geth code I think github.com/ethereum/go-ethereum/blob/… – Nicolas Massart Apr 11 at 21:23
1

Miners have compete latitude to include or ignore any transactions they like, in almost any order. The only invariant is that transactions emitted from a given EOA must be mined in account once order.

Miners will probably select transactions with the highest gas price and pack blocks with the most gas allowed. That usually happens.

Hope it helps.

| improve this answer | |
  • Sure miners can put in anything they want in principle, but I'm wanting to know what happens in practice. I've observed that transactions with the same gas price are ordered randomly in a block, and not in the order that the miner heard them. But maybe there's some ordering that I'm unaware of (e.g. ordering by hash in the case of equal gas prices)? – the_cranky_panda Apr 11 at 20:08
  • No there is no deterministic ordering or inclusion rule except for the nonce rule. – Rob Hitchens Apr 12 at 1:41
  • At the protocol level, you're right, but I'm talking about what people do in practice. For example the vast majority of the time, miners order by gasprice. I'm looking for their further behavior for when gasprice cannot be used (when gasprice for multiple transactions are the same) – the_cranky_panda Apr 12 at 2:52
1

The answer is whatever one the miner wants first. A miner could choose a tx with a gasPrice of 0.5 Gwei and not include either of the 1 Gwei transactions.

Most mining clients will optimize for profit (highest gasPrice). In the case of two transactions with identical profit, the client will likely choose the transaction that was broadcast first.

| improve this answer | |
  • Sure miners can put in anything they want in principle, but I'm wanting to know what happens in practice. I've observed that transactions with the same gas price are ordered randomly in a block, and not in the order that the miner heard them. But maybe there's some ordering that I'm unaware of (e.g. ordering by hash in the case of equal gas prices)? – the_cranky_panda Apr 11 at 20:08
0

This depends on the client, but also evolves over time. The best thing to do to get a definitive answer is look at the client code, which is easy because most clients are open source. The Geth client (the most popular one by far) has recently changed their algorithm so that it looks at gas price first, time of arrival second. That is, if there are two transactions with same gas price they will be ordered so that the one that the miner saw first (that arrived to its tx pool first) is first. See this PR for details: https://github.com/ethereum/go-ethereum/pull/21358

| improve this answer | |

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.