如何为unsigned transaction实现递增nonce 解决offchain worker提交交易优先级报错
问题根因说明
你碰到的TooLowPriority报错本质和普通签名交易的nonce冲突不是一回事:Unsigned交易不需要依赖链上账户的nonce做去重,该报错核心是你当前validate_unsigned返回的ValidTransaction中,and_provides传入的全局唯一标识重复了,交易池判定两个交易的作用完全等价,在优先级数值相同的情况下就会直接拒绝后提交的交易。
解决方案
你需要让每个验证人在同一个era提交的该unsigned交易的provides标签唯一,也可以配合调整优先级规则避免冲突,具体修改方案如下:
- 调整
validate_unsigned的唯一标识生成规则,将era号、验证人ID这类唯一变量拼入provides标签,确保同个era下不同验证人提交的交易标识不会重复 - 可选:将优先级数值和era号、验证人权重等参数绑定,进一步降低同优先级冲突的概率
- 可选:如果业务侧不需要每个验证人都提交一次交易,可以在Offchain Worker逻辑中加分布式锁,每个era只让第一个触发OCW的验证人提交交易,从根源上避免重复提交问题
实现示例
第一步:修改Extrinsic入参
给unsigned交易增加唯一标识类的入参,方便后续做校验和生成唯一标签:
#[weight = 10000] pub fn submit_participant( origin: OriginFor<T>, validator: T::AccountId, current_era: EraIndex, _signature: T::Signature ) -> DispatchResult { // 原有业务逻辑前可增加校验,避免恶意构造的交易 ensure!(<Validators<T>>::get(current_era).contains(&validator), Error::<T>::InvalidValidator); // 你的原有存储逻辑 Ok(()) }
第二步:修改validate_unsigned逻辑
调整唯一标签和优先级规则:
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { match call { Call::submit_participant { validator, current_era, .. } => { // 拼接全局唯一的provides标签,era + 验证人ID确保同era不同验证人提交的交易标识不重复 let provide_tag = (b"participant_submit", current_era, validator).encode(); ValidTransaction::with_tag_prefix("pallet-participant") // 优先级可按需调整,比如era越新优先级越高,或者验证人权重越高优先级越高 .priority(100 + *current_era as u64) .and_provides(provide_tag) // longevity可以调整为era周期的一半,避免过期交易占用交易池 .longevity(T::EraDuration::get() / 2) .propagate(true) .build() } _ => InvalidTransaction::Call.into() } }
第三步:调整Offchain Worker提交逻辑
在OCW提交交易的代码中,传入当前节点的验证人账户、当前era号作为参数,再签名提交即可。
内容的提问来源于stack exchange,提问作者Jdawg287
相关产品推荐
相关产品推荐

