以太坊交易中的Nonce詳解

nonce在區塊鏈中是一個非常重要的概念,從比特幣到以太坊都有nonce的身影。

在比特幣中,nonce主要用于調整pow挖礦的難度,而在以太坊中,除了調整挖礦難度外,在外部賬戶的每筆交易中也都存在一個nonce。這個nonce是一個連續的整數,在每個賬戶發送交易時所產生,其主要設計目的是為防止雙花。

web3中的sendTransaction方法,官方文檔是這樣寫的:

https://web3js.readthedocs.io/en/1.0/web3-eth.html#sendtransaction

image.png

可以看到,在構建交易時,有一個可選的nonce參數,可以覆蓋在交易池中的,pending列表中相同nonce的交易。

接下來我會在Geth搭建的私有鏈來進行以下實驗,來看看不同情況下nonce對應的交易會怎樣:

  • 相同nonce下的兩筆交易
  • 不連續nonce下的交易
  • 不具體指定nonce的交易

說明:使用Geth搭建私有鏈詳見:http://www.lxweimin.com/p/4c3efd23a427

首先下方命令看到地址0xfa8d4ded7fe1fec96c1b10443bea261195f233bb的總交易數是2,也就是說,nonce數值已累加到1(nonce值從0開始),新的交易nonce值將是2。

> eth.getTransactionCount("0xfa8d4ded7fe1fec96c1b10443bea261195f233bb")
2

接下來指定nonce為2,創建一筆交易,如下可看到,交易成功,并被打包到了1980區塊中。

> web3.eth.sendTransaction({from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb", to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53", value: "1000000000000000000", nonce: "2"})
"0x8a16b9a887928125071469332425896d33a1fa8c4e5a5deb50545f637ea1bf5d"

> eth.getTransaction("0x8a16b9a887928125071469332425896d33a1fa8c4e5a5deb50545f637ea1bf5d")
{
  blockHash: "0x9ba0aad04c9b52ef9bc543d274ce1718e32e9fdbf4bff7f88904f14c03f41855",
  blockNumber: 1980,
  from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb",
  gas: 90000,
  gasPrice: 1000000000,
  hash: "0x8a16b9a887928125071469332425896d33a1fa8c4e5a5deb50545f637ea1bf5d",
  input: "0x",
  nonce: 2,
  r: "0xd530dc31288ff48f796763ce398a21cca9e9ab640f80890478b259dc247709e0",
  s: "0x275aa77f51cb157e2088e90cc3859bd2e53dbcc5bf752a5380a8736796a2a383",
  to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53",
  transactionIndex: 0,
  v: "0x557",
  value: 1000000000000000000
}

如果此時我在發送一筆交易,并指定nonce還是2,運行結果如下所示。

會看到此時交易報錯,提示“nonce too low”

> web3.eth.sendTransaction({from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb", to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53", value: "1000000000000000000", nonce: "2"})
Error: nonce too low
    at web3.js:3143:20
    at web3.js:6347:15
    at web3.js:5081:36
    at <anonymous>:1:1

按照nonce的規則,接下來的新交易nonce值應該是3,我現在跳過3,直接指定nonce值為4,會發生什么,如下所示。

> web3.eth.sendTransaction({from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb", to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53", value: "1000000000000000000", nonce:"4"})
"0xcbd473fb1bcc396dfd98e2f1807dea5b78c77f713592c134e7249b3786025d6a"

可以看到,返回了交易hash,我們查看該筆交易,發現blockNumber為null,并沒有加入到區塊中,如下所示:

> eth.getTransaction("0xcbd473fb1bcc396dfd98e2f1807dea5b78c77f713592c134e7249b3786025d6a")
{
  blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  blockNumber: null,
  from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb",
  gas: 90000,
  gasPrice: 1000000000,
  hash: "0xcbd473fb1bcc396dfd98e2f1807dea5b78c77f713592c134e7249b3786025d6a",
  input: "0x",
  nonce: 4,
  r: "0x657ceabd6907d50d1af9046ea58352d0804cc3812b290ff103bc32514bc491c5",
  s: "0x2bc6b80ef7de0ed9a737ba78f404f825d58ab345c446019fbd67644c2f2b2a36",
  to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53",
  transactionIndex: 0,
  v: "0x558",
  value: 1000000000000000000
}

暫未被加入到區塊中的交易,會被放入到交易池中(txpool),交易池里會維護兩個列表,一個是待被打包的pending列表,一個是當前無法執行的交易queued列表。

從下方請求可看到0xcbd473fb1bcc396dfd98e2f1807dea5b78c77f713592c134e7249b3786025d6a交易被放到了queued列表中。這是由于交易池中沒有找到地址0xfa8d4DEd7fE1feC96c1B10443bEa261195f233Bb為3的nonce。

> web3.txpool.content.queued
{
  0xfa8d4DEd7fE1feC96c1B10443bEa261195f233Bb: {
    4: {
      blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
      blockNumber: null,
      from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb",
      gas: "0x15f90",
      gasPrice: "0x3b9aca00",
      hash: "0xcbd473fb1bcc396dfd98e2f1807dea5b78c77f713592c134e7249b3786025d6a",
      input: "0x",
      nonce: "0x4",
      r: "0x657ceabd6907d50d1af9046ea58352d0804cc3812b290ff103bc32514bc491c5",
      s: "0x2bc6b80ef7de0ed9a737ba78f404f825d58ab345c446019fbd67644c2f2b2a36",
      to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53",
      transactionIndex: "0x0",
      v: "0x558",
      value: "0xde0b6b3a7640000"
    }
}

新建一筆交易,設置nonce為3,如下所示。

會看到提交交易后,queued列表中nonce為4的交易也被移出,同時待打包的pending列表中,有了nonce值為3和4的交易信息。挖礦后,這兩筆交易將會被寫入到區塊中。

> web3.eth.sendTransaction({from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb", to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53", value: "1000000000000000000", nonce:"3"})
"0xf9ed5af220997d8278e075ed87b391e6a28354b0c45726bb41ebae22fe5817b1"

> web3.txpool.content.pending

{
  0xfa8d4DEd7fE1feC96c1B10443bEa261195f233Bb: {
    3: {
      blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
      blockNumber: null,
      from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb",
      gas: "0x15f90",
      gasPrice: "0x3b9aca00",
      hash: "0xf9ed5af220997d8278e075ed87b391e6a28354b0c45726bb41ebae22fe5817b1",
      input: "0x",
      nonce: "0x7",
      r: "0x258f9f382c3cd8c595cd610ad7df09cc5b0d6b7a6cd68a67a0d151a1d72d8c72",
      s: "0x7c4fe98cbeb967f8a6d7ebb4c848c264521ddf98fc992f99571d4e2380e50a9a",
      to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53",
      transactionIndex: "0x0",
      v: "0x557",
      value: "0xde0b6b3a7640000"
    },
    4: {
      blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
      blockNumber: null,
      from: "0xfa8d4ded7fe1fec96c1b10443bea261195f233bb",
      gas: "0x15f90",
      gasPrice: "0x3b9aca00",
      hash: "0xcbd473fb1bcc396dfd98e2f1807dea5b78c77f713592c134e7249b3786025d6a",
      input: "0x",
      nonce: "0x7",
      r: "0x258f9f382c3cd8c595cd610ad7df09cc5b0d6b7a6cd68a67a0d151a1d72d8c72",
      s: "0x7c4fe98cbeb967f8a6d7ebb4c848c264521ddf98fc992f99571d4e2380e50a9a",
      to: "0xf3756e74c9c409fdf4fa6d44c492fbb1edf36f53",
      transactionIndex: "0x0",
      v: "0x557",
      value: "0xde0b6b3a7640000"
    },
  }
}

總結一下:

  1. 以太坊中有兩種nonce,一種是在區塊中的nonce,主要是調整挖礦難度;一種是每筆交易中nonce。
  2. 每個外部賬戶(私鑰控制的賬戶)都有一個nonce值,從0開始連續累加,每累加一次,代表一筆交易。
  3. 某一地址的某一交易的nonce值如果大于當前的nonce,該交易會被放到交易池的queued列表中,直到缺失的nonce被提交到交易池中。
  4. 地址的nonce值是一個連續的整數,起設計的主要目的是防止雙花。
  5. 在發生一筆交易時,如果不指定nonce值時,節點會根據當前交易池的交易自動計算該筆交易的nonce。有可能會出現節點A和節點B計算的nonce值不一樣的情況。

參考代碼:

https://github.com/ethereum/go-ethereum/blob/86e77900c53ebce3309099a39cbca38eb4d62fdf/core/tx_pool.go

一筆交易調用add(ctx context.Context, tx *types.Transaction)方法將交易信息加入到交易池的pending列表中,需要對交易信息進行驗證,驗證方法是validateTx,如下所示:

// validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
    // Heuristic limit, reject transactions over 32KB to prevent DOS attacks
    if tx.Size() > 32*1024 {
        return ErrOversizedData
    }
    ...
    // Ensure the transaction adheres to nonce ordering
    if pool.currentState.GetNonce(from) > tx.Nonce() {
        return ErrNonceTooLow
    }
    ...
}

使用GetNonce方法獲取當前地址在交易池中的nonce值,如果當前交易的nonce比交易池中的nonce值小,就會報“nonce too low”的錯誤。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • 問題起源 在使用同一個地址連續發送交易時,每筆交易往往不可能立即到賬, 當前交易還未到賬的情況下,下一筆交易無論是...
    糙米薏仁湯閱讀 5,194評論 1 6
  • 原文地址 https://mbinary.coding.me/introduction-to-bitcoin.ht...
    mbinary閱讀 5,214評論 0 4
  • 8.1 簡介 挖礦是增加比特幣貨幣供應的一個過程。挖礦同時還保護著比特幣系統的安全,防止欺詐交易,避免“雙重支付”...
    冉冉升起的沙丁魚閱讀 1,634評論 0 1
  • 長相普通的宋念念21歲了,仍然單著,但是她有喜歡的人了。而她喜歡的人就是她多年的好友丁杭,他不算帥,但180的身高...
    徐漾閱讀 388評論 0 5
  • 等你 天晴的日子 我在陽光下等你 下雨的時候 我在云層里等你 我站成了一棵樹 一半在風中 一半在土里 等你 從晨曦...
    鳴鷗閱讀 383評論 11 5