【问题标题】:Blockhash not found when sending transaction发送交易时未找到 Blockhash
【发布时间】:2023-02-14 14:26:46
【问题描述】:

使用 Solana web3 发送交易时,有时会显示此错误:
Error: failed to send transaction: Transaction simulation failed: Blockhash not found

除了重试之外,处理此错误的正确方法是什么X次数?
有没有办法保证在发送交易时不会发生这个问题?

这是我如何发送交易的示例:

const web3 = require("@solana/web3.js")
const bs58 = require('bs58')

const publicKey = new web3.PublicKey(new Uint8Array(bs58.decode("BASE_58_PUBLIC_KEY").toJSON().data))
const secretKey = new Uint8Array(bs58.decode("BASE_58_SECRET_KEY").toJSON().data)

const connection = new web3.Connection(
  "https://api.mainnet-beta.solana.com", "finalized",
  {
    commitment: "finalized",
    confirmTransactionInitialTimeout: 30000
  }
)
const transaction = new web3.Transaction().add(
  web3.SystemProgram.transfer({
    fromPubkey: publicKey,
    toPubkey: publicKey,
    lamports: 1
  })
)
web3.sendAndConfirmTransaction(
  connection,
  transaction,
  [{publicKey: publicKey, secretKey: secretKey}],
  {commitment: "finalized"}
)


我该如何改进以避免出现 Blockhash not found 错误?

【问题讨论】:

  • 由于我想不出其他任何东西,我最终进行了重试退避。如果有更好的方法,请告诉我!
  • 嘿,你能发布你的解决方案吗?我有同样的错误,下面的参考资料都没有解决它。谢谢你!

标签: javascript blockchain smartcontracts solana solana-web3js


【解决方案1】:

重试不是坏事!在某些情况下,它实际上是处理丢失事务的首选方式。例如,这意味着做:

// assuming you have a transaction named `transaction` already
const blockhashResponse = await connection.getLatestBlockhashAndContext();
const lastValidBlockHeight = blockhashResponse.context.slot + 150;
const rawTransaction = transaction.serialize();
let blockheight = await connection.getBlockHeight();

while (blockheight < lastValidBlockHeight) {
  connection.sendRawTransaction(rawTransaction, {
    skipPreflight: true,
  });
  await sleep(500);
  blockheight = await connection.getBlockHeight();
}

你可能想通读这篇关于重试交易的食谱条目:https://solanacookbook.com/guides/retrying-transactions.html

具体来说,它解释了如何实现一些重试逻辑:https://solanacookbook.com/guides/retrying-transactions.html#customizing-rebroadcast-logic

以及重试的具体含义:https://solanacookbook.com/guides/retrying-transactions.html#when-to-re-sign-transactions

【讨论】:

  • 对于您链接的最后一篇文章,它声明仅在 blockhash 无效后重试,可以通过 isBlockhashValid 进行检查。这个 rpc 调用不会失败,因为节点可能不同步吗?
  • 我目前正在做的是使用斐波那契重试退避,超时为 2 分钟。一旦超过 2 分钟,它将停止重试。我选择 2 分钟的原因是因为这个 article 说一个区块哈希值在大约 2 分钟内有效。我的方法好吗?还是我应该进行 isBlockhashValid 检查而不是 2 分钟超时,以防某天区块哈希过期的时间发生变化?
  • 我对此并不完全确定,但我认为 blockhash 有效性在节点之间将是非常标准的并且不应该太不同步,因为它已经被观察到。可能出现的主要问题是使用来自少数分支的区块哈希,但这是一个完全不同的问题。我建议经常使用 isBlockhashValid 来仔细检查,而不是硬编码 2 分钟。
  • 已经有一段时间了,但文章最近更新了,而不是使用isBlockhashValid,建议使用getEpochInfo并将blockHeight与tx的lastValidBlockHeight进行比较。如果节点不同步,可以保存 blockHeight 更高的上下文槽(即 tx 过期时的槽)并等待状态检查槽(getSignatureStatus)相等或领先。如果该槽的状态仍然为空,则可以安全地假设 tx 已过期。
  • 这是一个仅链接的答案 - StackOverflow 上的答案应该有代码。
【解决方案2】:

您可以使用 maxRetries 选项:对于 web3 / Solana native:

web3.sendAndConfirmTransaction(
  connection,
  transaction,
  {
     maxRetries: 5
  }
)

或者对于 SPL 代币:

const signature = await transfer(
  connection,
  sender,
  senderTokenAccount.address,
  recipientTokenAccount.address,
  sender.publicKey,
  amount,
  [],
  {
    maxRetries: 6,
  }
);

【讨论】:

  • 我更喜欢这个答案,因为它有一些实际的代码 - 谢谢!我已经添加了 SPL 版本,希望你不介意。
【解决方案3】:

Strata 基金会有一个用于 solana 的软件包,它是生产级的。

我在生产应用程序中使用过它。

    const signature = await sendAndConfirmWithRetry(
      connection, 
      tx.serialize(), 
      {
        maxRetries: 5,
        skipPreflight: true
      },
      "processed");

【讨论】:

    【解决方案4】:

    来自here

    RecentBlockhash 是交易的重要价值。你的 如果您使用过期的最近区块哈希,交易将被拒绝 (150 块后)

    solana 的出块时间为 400 毫秒。那意味着

    150 * 400ms = 60 seconds
    

    这就是为什么您需要非常快速地查询和发送交易。否则,您的交易将被永久删除,您将收到该错误

    【讨论】:

      猜你喜欢
      • 2020-03-19
      • 1970-01-01
      • 2016-10-07
      • 1970-01-01
      • 2019-03-06
      • 2018-07-28
      • 2022-11-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多