Go-Ethereum

相同gas價格的交易如何在一個區塊中排序?

  • September 10, 2020

如果 A hasgas_price == 1gwei和 B has gas_price == 1gwei,礦工如何決定在將它們排序到生產塊時首先執行哪個 tx?是否取決於使用的​​挖礦客戶端?受歡迎的客戶有何不同?

給定一個絕對特定於每個客戶但對每個礦工來說更具體的過程,選擇要開采的交易。事實上,交易 (tx) 選擇和排序過程不是乙太坊協議的一部分。也就是說,礦工可以決定以他們喜歡的方式選擇和排序交易。

然而,礦工主要通過賺錢來激勵(足以防止礦工作弊和失去區塊的慾望)。礦工將嘗試從每個開采的區塊中賺取最多的錢。大多數客戶選擇過程僅針對汽油價格。

正如您將在下面看到的(我將展示它在 Hyperledger Besu 程式碼中的工作原理),該機制很簡單。如果礦工想定制它,只需要修改一個 Java 文件,或者創建一個新文件並稍微更改程式碼以能夠處理多種選擇器並使用修改後的程式碼建構 Besu來使用它.

你可以想像一個礦工想要實施一個排除列表來防止從特定賬戶探勘 tx,或者只探勘價值 tx 而沒有合約 tx,只有 ERC20 代幣轉移,無論你能想像什麼,任何東西都可以在這裡編碼。這實際上取決於礦工開采的動機。

由於 Besu 現在也有一個外掛 API,我們可以想像(將來因為它尚未在 API 中實現)您可以通過切換外掛來更改選擇機制,並且人們會提供不同類型的選擇外掛。


Hyperledger Besu中的實施細節

我不能告訴其他客戶,但對於Hyperledger Besu來說,程式碼在這個evaluateTransaction方法中,你可以注意到選擇過程如下(下面是 BlockTransactionSelector.java 程式碼的有趣部分和我的評論):

/*
  * 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.");
   }

該塊是否能夠包含 TX(足夠的可用空間)?如果是,繼續查看我們是否將 tx 包含在 bloc 中,否則檢查目前 tx 並檢查下一個,直到塊盡可能滿(在 Besu 中,塊的預設最小填充度為 80% )。

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

然後檢查 gas 價格是否等於或高於礦工定義的價格。執行節點時,Besu 上的預設值為 1000 Wei。礦工可以使用配置變數覆蓋此值,無需更改程式碼。

   // 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);

執行 EVM 中的 tx 以檢查它是否生成有效結果

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

一旦執行,我們檢查結果的有效性,如果有效,我們更新將成為我們將要探勘的塊的內容。否則,如果 tx 無效的原因是因為 nonce 不是正確的,我們只需將其保留在待處理列表中,直到達到 nonce 正確的點。例如,我們可以在 nonce 1 之前將 nonce 2 包含在列表中。如果 tx 由於任何其他原因無效,我們會拒絕它並將其從待處理列表中刪除。

   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;
 }

在您的情況下,如果兩個具有相同 gas 價格的 tx 達到有效的最低要求(結果和 nonce),其 gas 價格至少等於礦工定義的(或預設值)並且可以適合塊。然後,它們將按照它們被包含在待處理列表中的順序被包含在內。第一個被添加的將是第一個包含在塊 (FIFO) 中的。

當然有一些邊緣情況,例如來自同一帳戶的兩個 tx 在待處理列表中並以相反的隨機數順序添加(隨機數 2 在隨機數 1 之前添加),然後取決於它發生的時刻(塊是否已滿或不是)它可能以兩個 tx 不包含在同一個塊中結束。

這取決於客戶,但也會隨著時間的推移而發展。獲得明確答案的最佳方法是查看客戶端程式碼,這很容易,因為大多數客戶端都是開源的。Geth 客戶端(迄今為止最受歡迎的客戶端)最近更改了他們的算法,使其首先查看 gas 價格,然後查看到達時間。也就是說,如果有兩個具有相同 gas 價格的交易,它們將被排序,以便礦工首先看到的交易(首先到達其 tx 池)是第一個。有關詳細資訊,請參閱此 PR:https ://github.com/ethereum/go-ethereum/pull/21358

引用自:https://ethereum.stackexchange.com/questions/78745