
Solana CLI 持久交易 Nonce 完全指南命令实操、账户生命周期与源码级实现剖析【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solanaSolana 交易默认的recent_blockhash有效期只有约 2 分钟这对需要长时间人工或跨机构签名的离线场景如托管服务、多签审批几乎不可用。本文基于仓库文档 Durable Transaction Nonces in the Solana CLI完整覆盖 nonce 账户的创建、查询、推进、提现、授权转移等全部 CLI 命令及参数并结合 实现提案 与核心源码讲透 durable nonce 的账户状态机、blockhash 域隔离和运行时校验机制帮助你在无法及时签名的场景下可靠构造可长期待签名的交易。一、Durable Nonce 解决什么问题普通 Solana 交易在防重放校验中依赖recent_blockhash字段交易携带的 blockhash 若太旧文档写作时约为 2 分钟网络会直接拒绝该交易。实现提案对此问题的描述是某些使用场景如托管服务 custodial services产生签名所需的时间远超 blockhash 的寿命需要一种机制让潜在的离线网络参与者也能提前构造并签名交易。提案给出的两条硬性需求是交易签名必须覆盖 nonce 值防重放nonce 不可复用即使签名私钥泄露也不可复用。解决方案是一个合同式System Program 指令机制客户端把 nonce 值暂存stash在链上账户的 data 中作为未来交易recent_blockhash字段的取值。提案将其类比为某些 CPU ISA 中的 Compare-and-Swap 原子指令。使用 durable nonce 时客户端必须先查询该账户中存储的 nonce 值然后按常规方式构造交易但有两个附加要求recent_blockhash字段使用 durable nonce 值交易的第一条指令必须是AdvanceNonceAccount指令。这一首指令约定至关重要从源码结构看运行时正是以交易首条指令是否为 advance nonce作为识别 durable nonce 交易的信号提案中 Runtime Support 一节明确写道任何未通过常规check_hash_age校验的交易都会被检测是否使用了 Durable Transaction Nonce该信号就是交易中包含AdvanceNonceAccount作为首条指令。二、Nonce 账户的状态机与指令语义提案文档 给出了完整的账户行为流程Contract Mechanics整理如下Start Create Account state Uninitialized NonceInstruction if state Uninitialized if account.balance rent_exempt error InsufficientFunds state Initialized elif state ! Initialized error BadState ... WithdrawInstruction(to, lamports) if state Uninitialized if !signers.contains(owner) error MissingRequiredSignatures elif state Initialized if !sysvar.recent_blockhashes.contains(stored_nonce) error NotReady if lamports ! account.balance lamports rent_exempt account.balance error InsufficientFunds account.balance - lamports to.balance lamports success账户状态在 SDK 中有直接对应。nonce 状态定义 是一个简单的二态枚举pub enum State { #[default] Uninitialized, Initialized(Data), }其中Data结构定义于同一文件保存了三个核心字段pub struct Data { /// Address of the account that signs transactions using the nonce account. pub authority: Pubkey, /// Durable nonce value derived from a valid previous blockhash. pub durable_nonce: DurableNonce, /// The fee calculator associated with the blockhash. pub fee_calculator: FeeCalculator, }由此可以确认提案中描述的三条关键规则在代码中的落点rent-exempt 要求nonce 账户必须免租才能持久化Initialized前若余额不足会报InsufficientFunds。提案明确写道 Nonce accounts must be rent-exempt to meet the>solana-keygen new -o nonce-keypair.json solana create-nonce-account nonce-keypair.json 1输出示例原文档给出的链上账户地址2SymGjGV4ksPdpbaqWFiDoBz8okvtiik4KE9cnMQgRHrRLySSdZ6jrEcpPifW4xUpp4z66XM9d9wM48sA7peG2XL参数要点来自 create-nonce-account 的子命令定义位置参数 / 选项说明ACCOUNT_KEYPAIRnonce 账户 keypair第一个位置参数必填AMOUNT充入 nonce 账户的 SOL 数量接受关键字ALL必填--nonce-authority PUBKEY将 nonce 操作权限指派给其他实体--seed STRING地址派生种子指定后账户位于派生地址--memo/--compute-unit-price通用附加选项memo_arg/compute_unit_price_arg原文档还提示如果想让 keypair 完全离线保存可改用纸钱包Paper Wallet的种子短语生成方式参见 docs/src/cli/wallets/paper.md。五、查询、推进与检视 nonce 值查询当前存储的 nonce 值构造持久 nonce 交易时必须把存储的 nonce 值作为--blockhash参数在签名和提交时传入。查询命令solana nonce nonce-keypair.json输出示例8GRipryfxcsxN8mAGjy8zbFo9ezaUsh47TsPzmZbuytU从 子命令定义 可以看到nonce子命令的别名是get-nonce.alias(get-nonce)参数为必填的NONCE_ACCOUNT_ADDRESS。推进存储的 nonce 值在非正常交易之外通常不需要单独推进但可以直接操作solana new-nonce nonce-keypair.json输出为新的交易签名。new-nonce的命令描述是 Generate a new nonce, rendering the existing nonce useless生成新 nonce使旧 nonce 失效。其对应的链上动作正是AdvanceNonceAccount指令——把集群最新 blockhash 写入账户。人性化地检视 nonce 账户solana nonce-account nonce-keypair.json输出示例balance: 0.5 SOL minimum balance required: 0.00136416 SOL nonce: DZar6t2EaCFQTbUP4DHKwZ1wT8gCPW2aRfkVWhydkBvS该命令同时输出余额、免租最低余额和当前 nonce 值是排查 nonce 账户状态最直接的诊断手段。从 定义 看它还有别名show-nonce-account并支持--lamports选项以 lamports 而非 SOL 显示余额。六、提现与关闭 nonce 账户从 nonce 账户提现solana withdraw-from-nonce-account nonce-keypair.json ~/.config/solana/id.json 0.5输出为交易签名。把全额余额提现即可关闭该账户——这正对应提案中描述的WithdrawNonceAccount特殊路径由于免租要求普通提现不能让余额低于免租下限但最终余额为零是例外此时账户可以被删除回收而关闭路径还额外要求存储的 nonce 值不得等于集群最新 blockhash。withdraw-from-nonce-account 的参数定义 显示NONCE_ACCOUNT_ADDRESS必填、RECIPIENT_ADDRESS提现接收方必填、AMOUNT提现 SOL 数量必填外加nonce_authority_arg()、memo_arg()与compute_unit_price_arg()。七、转移 nonce 账户权限创建之后可以随时重新指派 authoritysolana authorize-nonce-account nonce-keypair.json nonce-authority.json输出为交易签名。该命令接受两个必填位置参数NONCE_ACCOUNT_ADDRESS和新权限持有者的AUTHORITY_PUBKEY以及可选的--nonce-authority当前 authority 的签名者等参数见 authorize-nonce-account 子命令定义。八、其他支持 Durable Nonce 的子命令--nonce与--nonce-authority要在其他 CLI 子命令中使用 durable nonce需要同时支持两个参数--nonce指定存储 nonce 值的账户--nonce-authority指定可选的 nonce authority原文档列出已获得此支持的子命令pay、delegate-stake、deactivate-stake。完整示例Alice 用 durable nonce 支付 Bob下面按原文档完整复现 Alice 支付 Bob 1 SOL实际示例金额 0.01 SOL的全过程这也是所有支持 durable nonce 的子命令的通用操作流程。第 1 步创建账户。先准备 Alice、Alice 的 nonce 账户和 Bob 三个 keypair$ solana-keygen new -o alice.json $ solana-keygen new -o nonce.json $ solana-keygen new -o bob.json第 2 步给 Alice 充值。她需要资金来创建 nonce 账户并给 Bob 打款$ solana airdrop -k alice.json 1 1 SOL第 3 步创建 Alice 的 nonce 账户。注意这里没有使用单独的 nonce authority因此alice.json拥有 nonce 账户的完全权限$ solana create-nonce-account -k alice.json nonce.json 0.1 3KPZr96BTsL3hqera9up82KAU462Gz31xjqJ6eHUAjF935Yf8i1kmfEbo6SVbNaACKE5z6gySrNjVRvmS8DcPuwV第 4 步首次尝试失败。Alice 签名耗时过长指定的 blockhash 过期交易失败$ solana transfer -k alice.json --blockhash expiredDTaxfagttWjQweib42b6ZHADSx94Tw8gHx11 bob.json 0.01 [2020-01-02T18:48:28.462911000Z ERROR solana_cli::cli] Io(Custom { kind: Other, error: Transaction \33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\ failed: None }) Error: Io(Custom { kind: Other, error: Transaction \33gQQaoPc9jWePMvDAeyJpcnSPiGUAdtVg8zREWv4GiKjkcGNufgpcbFyRKRrA25NkgjZySEeKue5rawyeH5TzsV\ failed: None })第 5 步nonce 救场。Alice 重试这次指定 nonce 账户及其存储的 blockhash记住本例中alice.json就是 nonce authority$ solana nonce-account nonce.json balance: 0.1 SOL minimum balance required: 0.00136416 SOL nonce: F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7$ solana transfer -k alice.json --blockhash F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7 --nonce nonce.json bob.json 0.01 HR1368UKHVZyenmH7yVz5sBAijV6XAPeWbEiXEGVYQorRMcoijeNAbzZqEZiH8cDB8tk65ckqeegFjK8dHwNFgQ第 6 步成功。Bob 收到 0.01 SOL且 Alice 账户中存储的 nonce 已推进到新值$ solana balance -k bob.json 0.01 SOL$ solana nonce-account nonce.json balance: 0.1 SOL minimum balance required: 0.00136416 SOL nonce: 6bjroqDcZgTv6Vavhqf81oBHTv3aMnX19UTB51YhAZnN注意最后一步 nonce 值从F7vmkY3DTaxfagttWjQweib42b6ZHADSx94Tw8gHx3W7变为了6bjroqDcZgTv6Vavhqf81oBHTv3aMnX19UTB51YhAZnN——这正是AdvanceNonceAccount作为首指令生效的直接证据。九、运行时如何校验与防费用窃取仅靠 System Program 指令不足以完整实现该功能。提案的 Runtime Support 一节指出还必须修改运行时以强制交易使用现存 recent_blockhash并防止通过重放失败交易窃取费用。当运行时判定交易使用了 Durable Transaction Nonce首指令为AdvanceNonceAccount时会执行三项额外校验加载Nonce指令中指定的NonceAccount从账户 data 字段反序列化NonceState确认其处于Initialized状态对应 State 枚举校验账户中存储的 nonce 值与交易recent_blockhash字段一致。三项全部通过交易才继续后续校验。更微妙的是费用窃取fee theft防护以InstructionError失败的交易会扣费且状态回滚。如果AdvanceNonceAccount指令被回滚恶意验证者就可以反复重放该失败交易直到存储的 nonce 恰好被成功推进从而用受害者的 nonce 账户偷到费用。运行时的对策是当 durable nonce 交易因AdvanceNonceAccount以外的InstructionError失败时nonce 账户按常规回滚到执行前状态但运行时随后推进其 nonce 值并把推进后的账户当作成功一样持久化使重放永远无法命中旧 nonce。仓库中为这一机制提供了专门的数据结构NonceInfo trait 及 NoncePartial / NonceFull。其中NoncePartial持有交易检查阶段可用的有限 nonce 信息地址与账户快照NonceFull额外持有扣费后的 fee payer 账户快照NonceFull::from_partial会把 rent debit 补回 fee payer 以捕获费用前的账户状态且当 nonce 账户本身就是 fee payer 时fee_payer_address nonce_address不再重复持有 fee payer。这些快照正是失败回滚/推进逻辑需要执行前状态的载体。其测试用例 test_nonce_info 还验证了 nonce 账户作为 fee payer、以及账户列表中缺少 fee payer 时返回AccountNotFound等边界场景。十、DurableNonce 与 blockhash 的域隔离一个容易被忽视但非常重要的实现细节durable nonce 并不是直接复用链上 blockhash而是经过哈希域隔离domain separation。DurableNonce 的定义const DURABLE_NONCE_HASH_PREFIX: [u8] DURABLE_NONCE.as_bytes(); pub fn from_blockhash(blockhash: Hash) - Self { Self(hashv([DURABLE_NONCE_HASH_PREFIX, blockhash.as_ref()])) }即存储的 nonce 值 hash(DURABLE_NONCE || 最新blockhash)。Data 结构的注释 明确写道Named blockhash for legacy reasons, but durable nonce and blockhash have separate domains. 这意味着 nonce 值与实时 blockhash 处于不同哈希域即使 nonce 在链上长期可见也无法被反推为当前有效 blockhash。此外Data中的fee_calculator保存了与 nonce 对应的每签名费用get_lamports_per_signature保证交易使用暂存的 nonce 时费用也是确定的——这是离线提前构造、签名并计算成本的必要前提。CLI 侧同样依赖lamports_per_signaturecreate-nonce-account的账户创建金额需覆盖免租最低余额加提现/操作需求而提现校验由 resolve_spend_tx_and_check_account_balance 一类的余额检查函数统一处理区分 spend 与 fee、支持ALL关键字与 rent-exempt 金额模式。十一、要点速查与适用前提适用前提本文所有命令基于本仓库clicrate 的 clap 子命令定义cli/src/nonce.rs与 SDK 实现sdk/program/src/nonce/对应 Solana 官方 CLI--blockhash显式传值 --nonce的组合使用方式与文档示例一致。最小可用余额示例输出中免租最低余额为0.00136416 SOLnonce 状态序列化大小为 80 字节见 State::size创建时AMOUNT必须 ≥ 该值否则初始化报InsufficientFunds。命令清单create-nonce-account别名见 CLI 参考、nonce/get-nonce查询、new-nonce推进、nonce-account/show-nonce-account检视、withdraw-from-nonce-account提现/关闭、authorize-nonce-account转移权限。使用铁律交易必须把账户中存储的 nonce 值填入recent_blockhash并以AdvanceNonceAccount为首条指令否则会被当作普通交易走check_hash_age校验而失败。权限模型AdvanceNonceAccount、WithdrawNonceAccount、AuthorizeNonceAccount均要求当前 nonce authority 签名authority 可经authorize-nonce-account一次性转移并支持派生地址等非 keypair 持有形态。失败交易的处理durable nonce 交易因其他指令失败时nonce 仍会被推进并持久化防费用窃取设计因此签名方在重试前务必重新查询solana nonce-account获取新 nonce 值——示例第 6 步中 nonce 值的变化即此行为的体现。深入阅读可参考durable nonce CLI 文档、实现提案、system_instruction 指令构造、nonce 状态定义 与 NonceInfo 实现。【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考