进阶
Durable Nonce 与多路投递
一次签名,多次提交。Durable Nonce 允许你把同一笔交易同时发给 BoltTx 和任意其他中继 —— 哪一路最快落块哪一路胜出,同时 nonce 机制保证这笔交易全网只会落块一次。
普通 blockhash 的问题
标准 Solana 交易引用一个近期 blockhash,150 个 slot(按 400ms/slot 约 60 秒)后失效。如果你想把同一笔签名交易扇出到多条投递路径做冗余,blockhash 可能在较慢的路径还没开始执行时就过期了 —— 而用新 blockhash 重新签名会改变签名,失去冗余的意义。
Durable Nonce 如何解决
永不过期
Durable Nonce 交易使用存储在链上的 nonce 代替近期 blockhash,在你显式推进 nonce 前始终保持有效。
精确一次落块
任何一个副本落块时,nonce 会原子性地推进。其他所有副本随后会自动失败 —— 不存在重复执行的风险。
一次签名,多路投递
交易只签名一次,然后把完全相同的字节并发提交给 BoltTx 与任意数量的其他中继。最快的那一路胜出。
步骤 1 —— 创建 Nonce 账户
Nonce 账户是一个 80 字节的小型链上账户,用来存储你当前的 durable nonce。每个钱包创建一次即可 —— CLI 默认提供的 0.0015 SOL 充分高于当前租金豁免下限(具体值取决于集群的租金参数,精确计算请调用 getMinimumBalanceForRentExemption(80))。你的主钱包就是 nonce authority。
# Generate a new keypair that will identify the nonce account
solana-keygen new -o nonce-account.json
# Create the nonce account, funded with 0.0015 SOL for rent exemption
solana -k sender.json create-nonce-account nonce-account.json 0.0015
# Query the current nonce value
solana nonce nonce-account.json步骤 2 —— 并发提交到 BoltTx 与其他中继
用同一个 nonce 构建一笔交易,签名一次,然后扇出发送。下面的示例把它并发提交给 BoltTx 和任意一个辅助中继(你自己的 RPC、Jito 端点等)。
import asyncio, base64
from solders.pubkey import Pubkey
from solders.keypair import Keypair
from solders.message import Message
from solders.transaction import Transaction
from solders.system_program import transfer, TransferParams
from solana.rpc.async_api import AsyncClient
async def submit_with_durable_nonce(
sender: Keypair,
nonce_account: Pubkey,
receiver: Pubkey,
tip_addr: Pubkey,
):
# 1. Fetch the current nonce value from the nonce account
rpc = AsyncClient("https://api.mainnet-beta.solana.com")
acc = await rpc.get_account_info(nonce_account)
nonce_hash = acc.value.data[40:72] # NonceState layout offset
await rpc.close()
# 2. Build the transaction with nonce + your instructions + BoltTx tip
instructions = [
transfer(TransferParams(
from_pubkey=sender.pubkey(),
to_pubkey=receiver,
lamports=1_000,
)),
transfer(TransferParams(
from_pubkey=sender.pubkey(),
to_pubkey=tip_addr,
lamports=800_000, # 0.0008 SOL — BoltTx Starter tip
)),
]
message = Message.new_with_nonce(
instructions,
payer=sender.pubkey(),
nonce_account_pubkey=nonce_account,
nonce_authority_pubkey=sender.pubkey(),
)
# 3. Sign ONCE with the nonce hash
tx = Transaction.new_unsigned(message)
tx.sign([sender], nonce_hash)
signed_b64 = base64.b64encode(bytes(tx)).decode()
# 4. Fan out to BoltTx + any secondary relay concurrently.
# The nonce guarantees only one copy actually lands.
import aiohttp
async with aiohttp.ClientSession() as http:
bolttx_task = http.post(
"https://bolttx.io/v1/send",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"transaction": signed_b64},
)
# Secondary path — your own RPC, Jito, etc.
backup_task = http.post(
"https://YOUR-BACKUP-RELAY/sendTransaction",
json={
"jsonrpc": "2.0", "id": 1, "method": "sendTransaction",
"params": [signed_b64, {"encoding": "base64"}],
},
)
results = await asyncio.gather(
bolttx_task, backup_task, return_exceptions=True
)
for i, r in enumerate(results):
label = ["BoltTx", "Backup"][i]
if isinstance(r, Exception):
print(f"{label}: failed — {r}")
else:
print(f"{label}: HTTP {r.status}")步骤 3 —— 处理结果
任何一个副本落块后,链上 nonce 会推进。下次发送前需要重新拉取最新 nonce。并发提交中只会有一笔真正拿到 slot —— 其他副本会被拒绝,返回 BlockhashNotFound (Agave 会返回与 blockhash 过期相同的 TransactionError::BlockhashNotFound —— 因为存储的 nonce 已不再等于交易的 recent_blockhash 字段)。这正是你想要的行为:精确一次执行 + 最大冗余。
重要注意事项
- •每个 Durable Nonce 交易的第一条指令必须是 nonceAdvance。大多数 Solana SDK 在你使用 new_with_nonce / createNonceAccount 辅助函数时会自动处理 —— 如果你手工构建 message,请自行确认。
- •在 BoltTx 上,Durable Nonce 交易的 Tip 与普通交易完全一致。冗余机制提升的是落块可靠性,并不是降低费用。
- •不要为了 "冗余" 而只投递到一条路径 —— 没有意义。这种模式适合那些落块可靠性比简洁性更重要的场景。