Skip to content
matrix edited this page Dec 30, 2019 · 3 revisions

nonce

定义

nonce (number used just once) 是密码学上只用一次的数字。nonce 在区块链中主要用于工作量证明和交易中,本文主要介绍交易中的nonce。

nonce 在交易中的作用

从同一个账户发出的所有交易必须有序,nonce从0开始,依次递增。 nonce 在交易中起到如下几个作用:

1.确定交易执行的顺序。某账户当前链上的nonce为n,则链执行交易时,依次按顺序执行nonce为n+1,n+2 ...的交易。交易的执行顺序不同,产生的结果也不同,通过nonce确定交易的执行顺序。

2.防止双花: 即双重支付,指的是在数字系统中,由于数据的可复制性,使得系统可能存在同一笔数字资产因不当操作被重复使用的情况。如果没有nonce,账户A转账户B数字资产n,由于交易的可复制性,节点可以多次从A账户转数字资产n到B。 有了nonce ,节点如果需要再次从A转同样的数字资产到B,必须要有客户端用账户A,用新的nonce,重新生成一笔从A转数字资产n到B的新的交易,并用账户A的私钥进行签名(nonce作为交易签名对象的一部分,不可修改),解决了双花问题。

AnnChain nonce处理机制

AnnChain链上保存每个账户的nonce。假设某账户A的nonce为n: 正常情况下,链会收到账户A发送的交易,nonce为n,n+1,n+2..的交易,然后按nonce排序。
等出块时间到了,会依次执行nonce为n,n+1,n+2的交易,在执行nonce为n+1交易之前绝不会执行nonce为n+2交易。出块时,如果最终执行到nonce位n+m的交易,则链上的账户nonce更新为n+m+1,所以在两个出块时间之内,链上获取账号的Nonce都是一样的,都是n。 非正常情况下,如果链收到nonce小于n的交易,进行合法性验证时会被直接丢弃,原因nonce太小。 如果没有收到nonce为n+1的交易,而收到了nonce为n+2的交易,一直等待nonce为n+1的前序交易,等待时间过长会返回超时。

nonce的使用

发送交易前,从链上获取nonce, 同一个账户发送交易nonce依次递增, 若果交易发送失败或者网络错误等问题,重新从链上获取nonce进行修正,再发送交易。

一个账户,一个线程,连续发送交易:

 //第一步,初始化, 从链上获取nonce,
 nonce :=sdk.GetNonce(accountAddress)
 for {
      //第二步,用nonce生成交易
      tx.Nonce = nonce 
      //第三步 发送交易
    _,err = sdk.TransactionAsync(tx)
     //发送成功,nonce递增
      nonce ++
     //如果发送失败,在有些情况下(比如交易重复)需要重新从链上获取nonce 进行修正
 }

一个账户,多线程并发问题: 交易并发发送,核心的问题是控制好nonce,nonce依次递增,每个nonce只用一次。所以在同一个客户端,一个账户 多线程发交易时, 本地nonce递增可以通过原子变量或者线程锁来保证.

//多线程nonce控制器
type NonceController struct {
	nonce  uint64
	mu sync.Mutex    
}
//多线程发送交易,调用该方法获取本地nonce,并递增
func (n*NonceController)GetNonce()(nonce uint64){
        //加线程锁
	n.mu.Lock()
       //退出时释放线程锁
	defer n.mu.Unlock()
	nonce = n.nonce
	n.nonce++
	return nonce
}

如果需要用多个客户端,同一个账户并发交易,需要用到分布式锁等其他机制来保证共享的nonce有序递增,而且每个nonce被用于一次。

Clone this wiki locally