每個區塊鏈都必須要有唯一標示每筆交易 (TX) 的方法,否則它將很容易受到交易重放攻擊。簡單來說,重放攻擊指的是攻擊者通過使用賬本中已有的交易來欺騙系統。

原文標題:《關於唯鏈雷神區塊鏈,你可能還不知道那些事兒(第一部分)- 交易唯一性》
作者:Peter Zhou,唯鏈首席科學家,英國南安普頓大學計算機博士,於 2017 年加入唯鏈,主要負責唯鏈雷神區塊鏈的科研研發及知識產權保護等工作

自 2018 年 6 月開始,唯鏈雷神區塊鏈已經正式上線一年了。作爲其 白皮書 的作者之一,我仍然覺得有一些爲了區塊鏈技術的大規模應用而設計的重要特性,還沒有被大家所瞭解。本篇是「關於唯鏈雷神區塊鏈,你可能還不知道的那些事兒」系列的第一篇。在這個系列裏我將詳細闡述這些非常有用,但尚未普及的唯鏈雷神區塊鏈的特性。另外,如果你還沒有了解過唯鏈雷神區塊鏈的多方支付協議(MPP)和多任務交易(MTT)特性,我強烈建議你可以先讀一下相關的文章。

區塊鏈如何對抗重放攻擊?硬核詳解唯鏈 TXID 方案Peter Zhou,唯鏈首席科學家,英國南安普頓大學計算機博士,先後以研究員和資深研究員的身份任職於英國肯特大學和芬蘭奧盧大學,參與了歐盟和芬蘭科學院重要科研項目,擁有十年計算機科研研發以及在國際一流學術雜誌和會議上發表科研成果之經驗,於 2017 年加入唯鏈,主要負責唯鏈雷神區塊鏈的科研研發及知識產權保護等工作

交易唯一性

每個區塊鏈都必須要有唯一標示每筆交易 (TX) 的方法,否則它將很容易受到交易重放攻擊。簡單來說,重放攻擊指的是攻擊者通過使用賬本中已有的交易來欺騙系統。

對於一個類似比特幣的基於 UTXO 模型的區塊鏈, 交易之間是相互關聯的,我們可通過歷史花費記錄來驗證交易。然而,對於像太坊這種基於賬戶體系的區塊鏈來說,這種驗證唯一性地方法不再適用。對於這類系統,我們需要在交易中加入一些額外的信息來達到交易的唯一性。

以太坊的解決方案

在以太坊上交易的唯一性是這樣實現的,以太坊在每筆交易中加入變量 AccountNonce,並且規定只有在 AccounceNonce 和發起交易的賬戶裏的 Nonce 變量的最新值一致的情況下,交易纔會被處理。請注意,Nonce 的數值記錄了該賬戶目前已經發送的交易數量。這樣一來,一筆交易就能通過這個 Nonce 的值和發起方的賬戶地址唯一來標示。

不過這種設計存在不足之處,那就是從同一個賬戶發送的交易將被迫組成一個序列。在這個序列裏,任何一筆交易的失敗,都將會導致後續交易不能被以太坊系統接受。這是因爲後續的交易裏包含的 Nonce 的數值都比交易發起賬戶當前的 Nonce 數值要大,所以必然會被系統排除在外。大家可以想象一下,當你有多筆交易正在等待系統確認,而你又想發新的交易的時候,你將面臨這樣的風險:一旦之前的任一交易失敗,新交易將會被無限延遲。

唯鏈雷神區塊鏈解決方案

唯鏈雷神區塊鏈是一個基於賬戶的區塊鏈系統,它通過以下方法來實現交易的唯一性。首先,它重新定義了交易裏的 Nonce 變量,使它成爲一個完全由交易發起者決定的 64 位無符號整數。對於一筆交易,我們將計算兩個哈希值,一個是不帶簽名的交易數據 RLP 編碼的哈希值,然後我們把計算出來的第一個哈希值串聯上交易發起方的賬戶地址,並對其做哈希運算,得到第二個哈希值。第二個哈希值的長度爲 256 位,在唯鏈雷神區塊鏈上用來作爲這筆交易的唯一標示(TXID)。值得注意的是,TXID 的計算是不需要私鑰來爲交易簽名的。

爲什麼這個設計很重要?

首先,一筆交易的唯一性完全取決於它的內容。它是否會被節點處理完全取決於計算出來的 TXID 是否已經在區塊鏈上存在,而不是取決於發起方賬戶的狀態(例如它的 Nonce 值)以及由這個賬戶發起的其他待確認的交易。顯然這樣的改變對於 dApps 開發者是很大的利好,因爲當他們編寫涉及與區塊鏈交互以及處理交易失敗的代碼時,他們所要考慮的問題被大大簡化了。

這一設計對企業用戶來說具有同樣重要的意義。假設你是一個工廠的負責人,你想要在區塊鏈上註冊產品,從而實現產品的追溯以及物流信息的可被驗證性。如果是基於以太坊的交易唯一性方案,當你的產品被源源不斷地生產出來,並通過發送交易到區塊鏈來試圖註冊它們的時候,任何一個交易的失敗將導致後續所有交易被系統拒絕,產品註冊失敗。這對於你的工廠來說是災難性的,是不可接受的。如果是基於唯鏈雷神區塊鏈的方案,整個流程將會變的流暢而且方便管理。因爲你所需要做的僅僅是在發送一筆交易之前,確保它 TXID 沒有被使用過。其餘的就是單獨處理那些失敗的交易即可(比如重新發送它們),這是因爲失敗的交易不會影響到其他的交易。

一個簡單的演示

爲了大家能夠更好的理解,我編寫了一小段代碼來展示 TXID 是如何計算的。在展示代碼之前,我首先到類似 veforge 這樣的唯鏈雷神區塊鏈瀏覽器上隨機找一筆交易。我的代碼會在本地重構這筆交易並計算它的 TXID。最後,我會把計算出來的 TXID 值和瀏覽器上的值進行對比來驗證我得出的計算結果。

以下是我隨機選取的一筆交易的 TXID:
0x38fac25309ab5395f1725284af46af585988983945e67b77cf9916b1ddf13d4b

這裏有個問題:由於 veforge 會四捨五入交易轉賬值,我會需要用其他工具來找到精確的數值,來重構這筆交易。爲此我打開了 Sync(一個專爲唯鏈雷神區塊鏈打造的功能強大的 dApp 運行環境),點擊了 「Toggle Developer Tools」 以及 「Console」(如下圖所示)。

區塊鏈如何對抗重放攻擊?硬核詳解唯鏈 TXID 方案區塊鏈如何對抗重放攻擊?硬核詳解唯鏈 TXID 方案

然後在 console 裏輸入以下命令:

connex.thor.transaction('0x38fac25309ab5395f1725284af46af585988983945e67b77cf9916b1ddf13d4b').get().then(tx => console.log(tx))

下圖展示了我隨後得到的結果:

區塊鏈如何對抗重放攻擊?硬核詳解唯鏈 TXID 方案

爲了運行我的代碼,你需要安裝 thor-devkit.js,這是一個幫助開發者開發 dApp 的 Typescript 開源庫。你可以通過這個 鏈接,找到我的代碼。

好了,現在我來給大家講一下代碼。這裏我們第一步做的是構建一個交易子句。對於那些不熟悉唯鏈雷聲區塊鏈交易模型的同學,我簡單說一下。每個唯鏈雷神區塊鏈的交易都可以包含多個子句(Clause),每個子句包含變量 tovaluedata,可以做通常我們定義的交易的同樣的事情。有了多子句結構,我們可以讓一筆交易做跟多的事情,只要是這些事情都是由同一個地址發起的。這樣會大大提高效率,並且保證這些子句操作的原子性。

let clauses =  [{
    to: '0x564B08C9e249B563903E06D461824b5d6b7F2968',
    value: "0x2a7ee2750fca8ea00000",
    data: '0x'
}]

在構造完子句之後,我們需要給交易的其他變量賦值。這裏我們用之前在 console 中得到的系統返回值來賦值:

let body: Transaction.Body = {
    chainTag: 74,
    blockRef: '0x002e3040a9ade438',
    expiration: 720,
    clauses: clauses,
    gasPriceCoef: 0,
    gas: 21000,
    dependsOn: null,
    nonce: '0x73541be64e72817c'
}

之後,我們做的是構建交易:

let tx = new Transaction(body)

並且計算交易 TXID。這個計算過程分兩步:1)首先計算不帶簽名的交易數據 RLP 編碼的哈希值:

let signingHash = cry.blake2b256(tx.encode())

2)其次把步驟一中計算出的哈希值和發送賬戶地址串聯起來,然後再對其計算哈希值,得到最終的交易 TXID:

const origin = '0xa4d2050f24ed7EfF313B7E912D6e5BF96ce57B95'
let id = '0x' + cry.blake2b256(signingHash, Buffer.from(origin.slice(2), 'hex')).toString('hex')

好了,現在我們已經計算出了該筆交易的 TXID,可以把它打印出來,與之前在 veforge 瀏覽器上找到的 TXID 進行比較了。

最後,我要感謝 @rogake 幫助我一起翻譯了 我的英文原文

來源鏈接:bbs.vechainworld.io