picture

2023-12-06

EIP-1559

EIP-1559

什麼是 Ethereum Improvement proposals - 1559

Vitalik Buterin: A transaction pricing mechanism that includes fixed-per-block network fee that is burned and dynamically expands/contracts block sizes to deal with transient congestion.

在 2021 年 8 月 5 日 EIP-1559 出現以前, 瓦斯費是由市場競標而來,每一筆交易可以設定一個用戶願意 給付的瓦斯費,當網絡變得繁忙時,也就是有很多交易需要處理的時候,礦工(負責處理交易的節點)會優先處理 那些支付更高瓦斯價格的交易。然而,這種競標系統導致不穩定的瓦斯價格,因為用戶需要在每次交易時猜 測應該支付多少瓦斯費,取決於其他用戶的出價。

EIP-1559(為五個倫敦網路升級提案中其一)宗旨就在於改變現有的瓦斯費計算模式使其可以調度離尖峰交易 量,並且取代競標模式,在 EIP-1559 下,瓦斯費將由兩個組成部分組成 - 基礎費(Base Fee)小費 (Tips)。基礎費將是所有用戶都必須支付的標準費用,它將根據網絡流量由網絡計算(但這種計算是可預測的)而 得。小費將是用戶可以支付的可選額外費用,以加快他們的交易速度。

    如果上一個區塊剛好為50%滿,基礎費用將保持不變。
    如果上一個區塊為100%滿,基礎費用將在下一個區塊中最多增加12.5%。
    如果上一個區塊超過50%但不滿100%滿,基礎費用將以不到12.5%的幅度增加。
    如果上一個區塊為0%滿 - 也就是空的 - 基礎費用將在下一個區塊中最多減少12.5%。
    如果上一個區塊超過0%但不滿50%滿,基礎費用將以不到12.5%的幅度減少。

EIP-1559 還要求網絡燒毀用於支付基礎費用的以太幣代幣(Ether Bruning)。這個程序將減少以太幣代幣的總 供應量,使以太幣變得更加稀缺,因此更有價值,也能利用這個手段對以太幣的通膨進行控制。而且在 EIP-1559 之前的礦工由高到低排好了要打包的交易之後,他可以把出價最低的幾筆交易換掉,故意自己製造一些高手續費的 無用交易,反正手續費最後都會回到礦工身上,而且墊高最低手續費後,排在前面的交易也要付更多錢給礦工。

所以一旦以太坊實施 EIP-1559,希望正常交易處理速度的用戶可以支付基礎費(將其銷毀),而希望更快交易處 理速度的用戶可以添加小費(礦工費用),就不用迫使用戶猜測所需費瓦斯費了。

交易格式

用了一種新型的 EIP-2718 交易格式,這些參數使用 RLP(Recursive Length Prefix)格式進行序列化, 格式如 下:

0x02 ||
  rlp([
    chain_id,
    nonce,
    max_priority_fee_per_gas,
    max_fee_per_gas,
    gas_limit,
    destination,
    amount,
    data,
    access_list,
    signature_y_parity,
    signature_r,
    signature_s,
  ]);
  • Gas 價格已替換為「每種 Gas 的最大優先費」和「每種 Gas 的最大費用」。
  • chain ID 是單獨編碼的,而不是包含在簽名 v 值中。這實質上以更簡單的實作取代了 EIP-155(v = recid+ chainID*2+ 35)。
  • 簽名 v 值現在是一個簡單的奇偶校驗位元(「簽名 Y 奇偶校驗」),它是 0 或 1,取決於應使用橢圓曲線上 的哪個點。

每單位瓦斯的基本費用,它每個區塊都可以根據一個公式上下調整,該公式是父區塊中使用的瓦斯和父區塊的瓦斯 目標(瓦斯限制(gas limit)乘以以彈性倍增器(elastic multiplier))的函數。

該算法導致基本每單位瓦斯的費用在區塊超過瓦斯目標時增加,而在區塊低於瓦斯目標時減少。基本每單位瓦斯費 用會被銷毀。交易會指定他們願意支付給礦工的每單位瓦斯的最高費用,以鼓勵他們包括他們的交易(也就是優先 費用)。交易還會指定他們願意支付的每單位瓦斯的最高總費用(即:最大費用),該費用包括優先費用和區塊的 每單位瓦斯網絡費用(即:基本費用)。該交易將始終支付包括在其所在區塊中的基本每單位瓦斯費用,並且只要 兩種費用的總額不超過交易的最高每單位瓦斯費用,它們將支付在交易中設置的優先費用每單位瓦斯。若交易總費 用大於最高每單位瓦斯費用的話,交易則將無法被區塊鏈網絡接受和處理。

Specification

1.As of FORK_BLOCK_NUMBER, a new EIP-2718 transaction is introduced with TransactionType 2.

2.新交易的固有成本是從 EIP-2930 繼承而來的:

    21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count

3.此交易的 EIP-2718 TransactionPayload 為:

rlp([
  chain_id,
  nonce,
  max_priority_fee_per_gas,
  max_fee_per_gas,
  gas_limit,
  destination,
  amount,
  data,
  access_list,
  signature_y_parity,
  signature_r,
  signature_s,
]);

4.此交易中的 signature_y_parity、signature_r 和 signature_s 元素表示對:

keccak256(
  0x02 ||
    rlp([
      chain_id,
      nonce,
      max_priority_fee_per_gas,
      max_fee_per_gas,
      gas_limit,
      destination,
      amount,
      data,
      access_list,
    ])
);

的 secp256k1 簽名。

5.交易的 EIP-2718 ReceiptPayload 包括以下參數:

rlp([status, cumulative_transaction_gas_used, logs_bloom, logs]);

向後兼容性

傳統的以太坊交易仍然可以正常運行並被包含在區塊中,但它們將無法直接從新的價格系統中受益。這是因為從傳 統交易升級到新交易將導致傳統交易的 gas_price 完全被 base_fee_per_gas 和 priority_fee_per_gas 消耗。

  • Block Hash Changing

Since Block Hash is Changing 所有驗證區塊或使用區塊哈希驗證區塊內容的應用程序將需要適應新的數據結構 (增加了一個額外的項目)。如果只取區塊標頭 byte 並對其進行哈希運算,仍然可以正確獲得哈希值,但如果從 其組成元素構建區塊標頭,則需要在末尾添加新的元素。

  • Gas Price

在此變更之前,GASPRICE 同時代表了簽名者每個 gas 單位支付的以太幣,以及礦工每個 gas 單位獲得的以太幣 。從這次變更開始,GASPRICE 現在僅代表了簽名者每個 gas 單位支付的以太幣,礦工獲得的交易費用不再可以直 接在 EVM 中訪問,這麼做可以使用戶所需的費用變得更清晰。

Security Considerations

增加最大區塊大小/複雜性

EIP-1559 將增加最大區塊大小,這可能會引起問題,如果礦工無法快速處理一個區塊,那麼他們將被迫挖掘一個 空區塊。隨著時間的推移,平均區塊大小應該保持與沒有這個提案相同,所以這只是一個短期區塊大小暴增的問題 。有可能一個或多個客戶端無法很好地處理短期區塊大小暴增,導致錯誤(例如內存不足或類似的錯誤),客戶端 實現應確保它們的客戶端能夠適當地處理個別區塊,直到達到最大大小。

交易排序

由於大多數人不再基於優先費用競爭,而是使用基準費用來確保被包含在內,交易的排序現在取決於個別客戶端的 內部實現細節,例如它們如何在內存中存儲交易。建議將具有相同優先費用的交易按照接收交易的時間進行排序, 以保護網絡免受垃圾攻擊,其中攻擊者將大量交易放入待處理池,以確保至少有一個交易處於有利位置。礦工仍應 根據自私挖掘的角度選擇高優先費用的交易,而不是低優先費用的交易。

礦工挖掘空區塊

有可能礦工會挖掘空區塊,直到基礎費用非常低,然後開始挖掘半滿區塊,並重新按優先費用排序交易。雖然這種 攻擊是可能的,但它並不是一個特別穩定的均衡狀態,只要挖礦是去中心化的。任何從這種策略中叛逃的礦工將比 參與攻擊的礦工更有利可圖,即使在基礎費用達到 0 之後也是如此。由於任何礦工都可以匿名叛逃,而且無法證 明特定礦工叛逃,所以唯一可行的執行這種攻擊的方式是控制 50%或更多的算力。如果攻擊者擁有確切的 50%算力 ,他們將不會從優先費用中獲得以太幣,而叛逃者將從優先費用中獲得兩倍的以太幣。對於攻擊者要實現盈利,他 們需要擁有 50%以上的算力,這意味著他們可以執行雙重支付攻擊,或者簡單地忽略任何其他礦工,這是一種更有 利可圖的策略。

ETH 銷毀將排除固定供應

通過銷毀基礎費用,我們無法再保證以太幣供應的固定性。這可能導致經濟不穩定,因為長期以來以太幣的供應將 不再保持恆定。儘管這是一個有效的擔憂,但很難量化其影響。如果銷毀的基礎費用多於挖礦獎勵所產生的,那麼 ETH 將是通縮的;如果挖礦獎勵多於銷毀的,那麼 ETH 將是通膨的。由於我們無法控制用戶對區塊空間的需求, 所以我們現在無法斷言 ETH 最終會是通膨還是通縮,因此這個變化使核心開發者對以太的長期供應失去了一些控 制。

可能集中化

Sergio Demian Lerner 提出擔憂,EIP-1559 可能導致礦池在網絡中佔主導地位,形成集中化現象。在減少礦工收 入模型下,小型礦工可能難以競爭,進一步集中化可能威脅到網絡的去中心化性質。

News about EIP-1559

  1. Ether Burn Hits $1.1B After EIP-1559 Activation, by JACQUELYN MELINEK / SEPTEMBER 29, 2021 根據超 音波貨幣追蹤器的數據,截至本文發佈時,大約 386,466 ETH(約 11 億美元)已被燒毀,8 月 5 日實施 EIP-1559 以來,已燒毀價值超過 10 億美元的以太幣,達到了加密貨幣的另一個里程碑。以太坊稍微處於通縮 期,但一旦以太坊區塊鏈在 2022 年轉變為權益證明系統,成為以太坊 2.0 的一部分,預計將更頻繁地面臨更 大的通縮期。
  2. EIP-1559 實施 365 天:NFT 協議燒幣量大於 DeFi, by Elponcho / August 5, 2022 根據 ultrasound.money,365 天以來燒毀了約 260 萬個以太幣,基於 PoW 共識發行了 550 萬個以太幣,整體來說 ,為以太幣總流通量帶來 2.4% 的年增幅,365 天以來,每分鐘燒毀 4.89 個以太幣被燒毀,抵銷發行 0.47 x。在不同的類型交易中,NFT 活動所銷毀的以太幣,勝過 DeFi 活動,也彰顯了過去一年 NFT 的興盛與 DeFi 的降溫,其次為套利活動 MEV,以及其他活動。

EIP-1559 raw transaction sample and algorithm

演算法

RLP(recurrsivep-length prefix):

RLP 標準化了在節點之間以節省空間的方式傳輸數據。RLP 的目的是將任意嵌套的二進制數據數組編碼,正整數的 RLP 必須以大端二進制形式表示,不帶前導零(從而使整數值零等同於空 byte 數組),帶前導零的反序列化正整 數被視為無效。

Definition

  • RLP 函數接受一個項目,項目的定義:字串是一個項目,列表(list)是一個項目
  • 在以太坊中,RLP 是資料序列化/反序列化的主要方法,區塊、交易等資料結構在永久化時會先經過 RLP 編碼後 再存儲到數據庫中

要使用 RLP 編碼字典,兩種規範形式如下:

  1. 使用[[k1,v1],[k2,v2]...],其中鍵按字典序排列
  2. 使用以太坊所採用的高級帕特里夏樹編碼方法

RLP encoding defination

  1. 對於一個單 byte,其值在 [0x00, 0x7f](十進制 [0, 127])範圍內,該 byte 本身就是它自己的 RLP 編碼 。
  2. 否則,如果一個字符串的長度為 0 到 55bytes,則 RLP 編碼包括一個值為 0x80(十進制 128)的 byte,接 著是字符串的長度,然後是字符串本身。因此,第一個 byte 的範圍是 [0x80, 0xb7](十進制 [128, 183]) 。
  3. 如果一個字符串的長度超過 55bytes,那麼其 RLP 編碼由以下部分組成第一個 byte 的數值是 0xb7(十進制 183)加上字符串長度的長度(即 byte 數的二進制表示的長度)的 byte 數。接著是字符串的長度。最後是 字符串本身。
  4. 如果一個列表的總有效載荷(即其所有項目的組合長度,經過 RLP 編碼後)長度在 0 到 55byte 之間,那麼 RLP 編碼由以下部分組成:第一個 byte 的數值是 0xc0 加上列表的長度。接著是所有項目的 RLP 編碼的串 聯。在這種情況下,第一個 byte 的範圍是 [0xc0, 0xf7](十進制 [192, 247])。
  5. 如果一個列表的總有效載荷超過 55byte,那麼 RLP 編碼由以下部分組成:第一個 byte 的數值是 0xf7 加上 有效載荷長度的長度(即 byte 數的二進制表示的長度)。接著是有效載荷的長度。最後是所有項目的 RLP 編碼的串聯。在這種情況下,第一個 byte 的範圍是 [0xf8, 0xff](十進制 [248, 255])。
        def rlp_encode(input):
            if isinstance(input,str):
                if len(input) == 1 and ord(input) < 0x80:
                    return input
                return encode_length(len(input), 0x80) + input
            elif isinstance(input, list):
                output = ''
                for item in input:
                    output += rlp_encode(item)
                return encode_length(len(output), 0xc0) + output

        def encode_length(L, offset):
            if L < 56:
                 return chr(L + offset)
            elif L < 256**8:
                 BL = to_binary(L)
                 return chr(len(BL) + offset + 55) + BL
             raise Exception("input too long")

        def to_binary(x):
            if x == 0:
                return ''
            return to_binary(int(x / 256)) + chr(x % 256)

RLP decoding definition

  1. 如果前綴的範圍在 [0x00, 0x7f] 內,則該數據被視為字符串,並且該字符串就是第一個 byte 本身;
  2. 如果第一個 byte 的範圍在 [0x80, 0xb7] 內,則該數據被視為字符串,並且該字符串的長度等於第一個 byte 減去 0x80,然後跟隨在第一個 byte 之後; 3.如果第一個 byte 的範圍在 [0xb8, 0xbf] 內,則該數 據被視為字符串,並且字符串的長度(以 byte 為單位)等於第一個 byte 減去 0xb7,然後跟隨在第一個 byte 之後,接著是字符串的內容; 4.如果第一個 byte 的範圍在 [0xc0, 0xf7] 內,則該數據被視為列表, 其中所有項目的總有效載荷等於第一個 byte 減去 0xc0,然後跟隨在第一個 byte 之後,接著是列表中所有 項目的 RLP 編碼的串聯; 5.如果第一個 byte 的範圍在 [0xf8, 0xff] 內,則該數據被視為列表,列表的長 度等於第一個 byte 減去 0xf7,然後跟隨在第一個 byte 之後,接著是列表的總有效載荷,最後是列表中所 有項目的 RLP 編碼的串聯。
            def rlp_decode(input):
                if len(input) == 0:
                    return
                output = ''
                (offset, dataLen, type) = decode_length(input)
                if type is str:
                    output = instantiate_str(substr(input, offset, dataLen))
                elif type is list:
                    output = instantiate_list(substr(input, offset, dataLen))
                output += rlp_decode(substr(input, offset + dataLen))
                return output

            def decode_length(input):
                length = len(input)
                if length == 0:
                    raise Exception("input is null")
                prefix = ord(input[0])
                if prefix <= 0x7f:
                    return (0, 1, str)
                elif prefix <= 0xb7 and length > prefix - 0x80:
                    strLen = prefix - 0x80
                    return (1, strLen, str)
                elif prefix <= 0xbf and length > prefix - 0xb7 and length > prefix - 0xb7 + to_integer(substr(input, 1, prefix - 0xb7)):
                    lenOfStrLen = prefix - 0xb7
                    strLen = to_integer(substr(input, 1, lenOfStrLen))
                    return (1 + lenOfStrLen, strLen, str)
                elif prefix <= 0xf7 and length > prefix - 0xc0:
                    listLen = prefix - 0xc0;
                    return (1, listLen, list)
                elif prefix <= 0xff and length > prefix - 0xf7 and length > prefix - 0xf7 + to_integer(substr(input, 1, prefix - 0xf7)):
                    lenOfListLen = prefix - 0xf7
                    listLen = to_integer(substr(input, 1, lenOfListLen))
                    return (1 + lenOfListLen, listLen, list)
                raise Exception("input does not conform to RLP encoding form")

            def to_integer(b):
                length = len(b)
                if length == 0:
                    raise Exception("input is null")
                elif length == 1:
                    return ord(b[0])
                return ord(substr(b, -1)) + to_integer(substr(b, 0, -1)) * 256

private key 產生:

產生金鑰的第一步也是最關鍵的一步是找到安全的熵來源或隨機性來源。產生以太坊私鑰需要選擇 1 到 2²⁵⁶ 之 間的數字。以太坊軟體使用作業系統的隨機數產生器來產生 256 個隨機位元。

public key 產生:

公鑰是橢圓曲線上滿足橢圓曲線方程式的一組 x 和 y 座標。它是透過使用橢圓曲線乘法從私鑰產生的兩個數字得 出的。這個過程是不可逆的,這意味著私鑰不能從公鑰推導出來。

    K = k * G

其中 K 為公鑰,k 為私鑰,G 為常數點(生成點)

以太坊使用 secp256k1 產生橢圓曲線,定義如下:

    y ² = ( x ³ + 7 ) 超過 ( 𝔽 p )
    或者:
    y ² |p| = ( x3 + 7 ) |p|

表示 mod p 該曲線位於素數階有限域上 p,其中 p = 2²⁵⁶–2³²–2⁹–2⁸–2⁷–2⁶–2⁴–1。這是一個非常大的質數,這 使得這條曲線非常可靠。

我們可以使用 npm 中的 helpeth 來生產公私鑰

image

  • (補充)ICAP(互換客戶端地址協議)是一種客戶端地址協議,它是與 IBAN(國際銀行帳號)兼容的系統, 旨在 引用和處理客戶帳戶,以簡化資金轉移流程,使交易無縫進行,最終讓 KYC(認識您的客戶)和 AML( 反洗錢 監管)成為過去時的事情。

hash 產生:

Keccak-256

關於這部分內容請參閱:我們的同 事royal0721那裡 有著詳盡的解釋。

實際運行:

a

a

編碼與解碼原始交易數據演算法:

編碼

  1. 創建原始交易:使用提供的參數(如鏈 ID、交易序號、燃氣限制、接收地址、價值、數據、最大優先手續費 和最大手續費)創建一個原始交易
  2. 交易編碼:使用 TransactionEncoder 對象將原始交易進行編碼。這可以將交易數據轉換為一種特定的格式, 以便在區塊鏈上傳輸
  3. 哈希計算:使用哈希函數對編碼後的交易進行哈希計算。這可以生成一個唯一的數字摘要,用於後續簽名驗證
  4. 使用私鑰簽名哈希:使用您的私鑰對哈希進行簽名。這是一個數學運算,用於證明這筆交易是由私鑰擁有者創 建的
  5. 簽名轉換為 Sign.SignatureData:將簽名轉換為特定的數據結構,通常是 Sign.SignatureData,以便將其附 加到原始交易並在區塊鏈上進行廣播,從而完成交易。
            public String prepareRawTransactionWithSignature(RawTransaction transaction, Sign.SignatureData signatureData) {
            // encode signature data
            List<RlpType> values = transaction.getTransaction().asRlpValues(signatureData);
            RlpList rlpList = new RlpList(values);
            byte[] encoded = RlpEncoder.encode(rlpList);
            if (!transaction.getType().equals(TransactionType.LEGACY)) {
            encoded = ByteBuffer.allocate(encoded.length + 1)
            .put(transaction.getType().getRlpType())
            .put(encoded)
            .array();
            }
            String serializedTxHex = Numeric.toHexString(encoded);
            log.info("Serialized tx {}", serializedTxHex);
            return serializedTxHex;
            }

解碼

  1. 獲取原始交易數據:首先,需要獲取要解碼的原始交易數據。這通常以十六進制字符串的形式提供,例如 :0x02c4c3010203。將原始數據轉換為二進制
  2. 解碼過程始於將十六進制字符串轉換為二進制數據。這是因為 RLP 編碼的數據是以二進制形式存儲的。
  3. 開始解碼過程:解碼過程從數據的二進制表示開始。首先,它將二進制數據中的字節分組成更小的塊,這些塊 包含一個數據項的長度和實際數據。
  4. 遞歸解析數據:一旦檢測到具有長度前綴的數據項,解碼器將遞歸進入該數據項,以解析其內部結構。這意味著 RLP 可以處理複雜的數據結構,包括列表和嵌套數據。
  5. 完成解碼:解碼過程繼續遞歸解析數據項,直到整個數據結構被解碼為易於理解的形式,例如 JavaScript 的數 組和對象。解碼完成後,可以訪問解碼後的數據結構以獲取數據。

ECDSA:

Svetlin Nakov: "The ECDSA signing algorithm (RFC 6979) takes as input a message msg ****+ a private key privKey ****and produces as output a signature, which consists of a pair of integers {r, s}. …The calculated signature {r, s} is a pair of integers, each in the range [1…n-1]. It encodes the random point R = k * G, along with a proof s, confirming that the signer knows the message h and the private key privKey. The proof s is by idea verifiable using the corresponding pubKey"

ECDSA)生成,該演算法由兩部分組成。第一部分是簽章創建演算法,第二部分是簽章驗證演算法。 ECDSA 簽章演 算法 ( RFC 6979 ) 將訊息 msg(hash)+ 私鑰 privKey 作為輸入,並加入簽章演算法產生簽章作為輸出,該簽章 由一對整數 { r , s } 組成。 計算出的簽章{ r , s } 是一對整數。它對隨機點 R = k * G 以及 s 進行編碼 ,確認簽署者知道訊息和私鑰。s 可以使用相應的 pubKey 進行驗證。

簽章創建法

當私鑰簽署已經序列化的交易時,就會建立數位簽章。實際上,這裡的序列化交易是 RLP 編碼訊息的 Keccak256 雜湊值。簽名交易的數學函數為:

        S ig = Fsig(Fkeccak256(m), k)

其中: k = 簽署私鑰 m= RLP 編碼訊息 Fkeccak256 = Keccak256 雜湊函數 Fsig = 簽名演算法 Sig = 產生的簽 名函數 Fsig 產生一個簽章 ( S ig ),該簽章產生兩個值 r 和 s,這對於簽章驗證很有幫助。Sig = r, s ECDSA 算法使用 secp256k1 曲線上的密鑰對來實現數字簽名和驗證,

a

簽章驗證法

要驗證交易,必須擁有簽章(r 和 s)、序列化交易和公鑰該公鑰必須與最初簽署交易時使用的私鑰相對應。簽名 驗證演算法採用這些列出的元件,並根據簽名是否有效傳回一個布林值;true 表示有效簽名, false 表示無效驗 證過程:

  1. 計算簽章證明的模逆,即簽章產生函數的逆:確定模數 N:需要知道要在其下執行模逆計算的模數 N。這是一 個正整數。選擇要計算逆的數字 a:要找到其逆的數字。通常,a 必須是模數 N 的正整數。計算逆元素 x:要 計算 a 的逆元素 x,需要找到一個數字 x,使得以下條件成立:(a * x) % N = 1。這表示 a 和 x 的乘積在 模 N 下等於 1。

  2. 從 x 座標恢復簽名期間產生的隨機點 r

  3. 從 R'產生其 x 座標:r' = R' .x

  4. 透過比較是否 r' == r 計算簽章驗證結果

    Sveltin Nakov:"簽章簽章使用私鑰 privKey 和訊息雜湊 h 透過橢圓曲線變換將隨機點 R (僅由其 x 座標表 示)編碼為數位 s,這是訊息簽署者知道證明私鑰。簽章驗證使用公鑰 pubKey 和訊息雜湊 h 將簽章中的證明 編號 s 解碼回其原始點 R ,並將恢復的 R 的 x 座標與簽章中的 r 值進行比較" 簽章驗證函數中加入了 v ,以協助偵測兩個可能產生值 R 和 R' 之間 r 的正確值。如果 v 是偶數,則 R 是正確值,如果 v 是奇數, 則 R',它有助於確定簽名是由哪個公鑰創建的。存在的目的是確保驗證者能夠正確地恢復公鑰,以驗證簽名的 有效性。

ECrecover

ECrecover 是 Solidity 中用於簽章驗證的 global function。它採用訊息哈希值和簽名驗證變數 v、r、s,並傳 回一個位址。然後可以將返回的地址與其他地址進行比較以找到匹配項。

address signer = ecrecover(msgHash, v, r, s);

in solidity:

    pragma solidity ^0.5.0;
    contract SignatureVerifier {
        function verify(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
            bytes32 prefixedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash));
            address signer = ecrecover(prefixedHash, v, r, s);
            require(signer != address(0), "Invalid signature");
            return signer;
        }
    }

EIP-1559 raw transaction sample and algorithm

假設我們有一個 EIP-1559 raw transaction sample//可以在 etherescan 中查找:

0x02f902fa0181b483a6792e850244ddce8e83045ee9943fc91a3afd70395cd496c647d5a6cc9d4b2b7fad8802c68af0bb140000b902843593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006508457f00000000000000000000000000000000000000000000000000000000000000020b080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000002c68af0bb140000000000000000000000000000000000000000000000001886271c2fb90c220a4d00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000003850952491606a0e420eb929b1a2e1a450d013f1c001a08b50ee3e376edb0fc0719ecb19a38dd7558285ba61b15c30083369bb76bfb393a069dc859312719259201ffae0c78208abc1b4e93ed05fd782f754dbe9eec75231;

解析後得到 JSON object:

{
  "chainId": "1",
  "type": "EIP-1559",
  "valid": true,
  "hash": "0x9d0de6855390ff87e9fe9ca8a8e07e6a8819f29531ea69dc701f96268080440d",
  "nonce": "180",
  "gasLimit": "286441",
  "maxFeePerGas": "9745321614",
  "maxPriorityFeePerGas": "10909998",
  "from": "0x7C71e3C2dC48557336961e0adc390164C8b045b6",
  "to": "0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad",
  "publicKey": "0x042e53305a01238bbb049d4f4d4c06b9aabaf42297bb2368f9263fb2e155fd9fce02bdd0e0e1a97632e4da7e11605423563a70fe78c146ab4ee89f234204f60d0c", //
  "v": "01",
  "r": "8b50ee3e376edb0fc0719ecb19a38dd7558285ba61b15c30083369bb76bfb393",
  "s": "69dc859312719259201ffae0c78208abc1b4e93ed05fd782f754dbe9eec75231",
  "value": "200000000000000000",
  "data": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006508457f00000000000000000000000000000000000000000000000000000000000000020b080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000002c68af0bb1400000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000002c68af0bb140000000000000000000000000000000000000000000000001886271c2fb90c220a4d00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000003850952491606a0e420eb929b1a2e1a450d013f1", //
  "functionHash": "0x3593564c", //
  "possibleFunctions": [
    {
      "definition": "execute(bytes,bytes[],uint256)", //
      "decodedInputs": [
        "0b08", //
        [
          "000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000002c68af0bb140000",
          "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000002c68af0bb140000000000000000000000000000000000000000000000001886271c2fb90c220a4d00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000003850952491606a0e420eb929b1a2e1a450d013f1" //
        ],
        "1695040895" //
      ]
    }
  ]
}

解析內容

  1. "chianId:1" 代表以太坊主網(補充:"3"代表 Ropsten 使用 PoW 算法, "4"代表 Rinkeby 是以太坊的 PoA 的測試網路, "42"代表 Kovan 使用 Clique 算法, "100"代表 XDai 提供快速的交易, 其他 chainID 可以 在chainlist.org查找)
  2. "Type:EIP-1559": 代表交易類型
  3. "valid:true": 表示交易有效且可以被處理
  4. "hash": 是一個唯一標識符,16 進制字串,類似於收據,用作交易已驗證並添加到區塊鏈的證據。在許多情 況下,需要 hash 來定位資金。
  5. "nonce": 是發送者地址的計數,確保交易按正確的順序處理並防止雙重支付,此交易來說,這是使用者錢包 的第 180 次交易 //180 = b4
  6. "gaslimit": 用於此交易的最大 Gas 限制
  7. "maxFeePerGas": 告知以太坊區塊鏈用戶願意花費的最大金額
  8. "maxPriorityFeePerGas" 用戶願意支付的最高小費金額
  9. "from","to": 使用者與接收者的以太坊地址
  10. "publicKey": 以太坊公鑰是橢圓曲線上滿足橢圓曲線方程式的一組 x 和 y 座標。它是透過使用橢圓曲線乘 法從私鑰產生的兩個數字得出的。這個過程是不可逆的,這意味著私鑰不能從公鑰推導出來。請參見演算法章 節
  11. "v","r","s": r 和 s 是 ECDSA(Elliptic Curve Digital Signature Algorithm) 簽名的輸出, v 是恢復 ID。
  12. "value": 以 Wei 為單位表示的以太幣金額。在此交易中,它是 200,000,000,000,000,000 Wei,相當於 0.2 以太幣
  13. "data": 函數調用及其參數
  14. "functionHash": data 字段串調用的函數的 hash 值
  15. "possibleFunctions":呼叫了名為『execute(bytes,bytes[],uint256)』的函數,並提供了特定的解碼輸入 (decodedInputs)

解析詳細過程

Syntex Description
0x02 EIP-1559
F9 F9 = f7 + 2 (告訴你後面兩 bytes(4 個字),是宣告整篇的長度)
02fa 02fa = 762, 762*2 = 1524
01 chainID
81 b4(decimal:180) Nonce
83 a6792e maxPriorityFeePerGas
85 0244ddce8e maxFeePerGas
83 045ee9 gasLimit
94 3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad To
88 02c68af0bb140000 Value
b9 0284 b9>b7(185 >183),185-183 = 2,告訴你後面兩 bytes(4 個字),是宣告 data 的長度(0284=644)
3593564c000000...~C0 Data
84 3593564c Function hash
6508457f unit256
0b08 decode input[0]
0000000...0000000000000000002c68af0bb140000 decode input[1,0]
...2000000...0000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000...003850952491606a0e420eb929b1a2e1a450d013f1 decode input[1,1]
6508457f(1695040895) decode input [2]
c0 空列表,表示 data 結束進入 VRS 簽名部分
01 V
a0 分隔符
8b50ee3e376edb0fc0719ecb19a38dd7558285ba61b15c30083369bb76bfb393 R
a0 分隔符
69dc859312719259201ffae0c78208abc1b4e93ed05fd782f754dbe9eec75231 S

//

參考:

  1. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
  2. https://blockworks.co/news/ether-burn-hits-1-1b-after-eip-1559-activation
  3. https://abmedia.io/20220805-eip-1559-365-days
  4. https://consensys.net/blog/quorum/what-is-eip-1559-how-will-it-change-ethereum/
  5. https://morten.dev/posts/new-transaction-types-on-ethereum
  6. https://blog.bitmex.com/zh_cn-breaking-down-the-fee-market-eip-1559/
  7. https://notes.ethereum.org/@vbuterin/eip-1559-faq
  8. (公私鑰產生 /secp256k1)https://betterprogramming.pub/understanding-ethereum-cryptography-3ef7429eddce
  9. (RLP)https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
  10. (以太坊白皮書)https://ethereum.org/zh/whitepaper/
  11. (hash: keccak256)https://wiki.rugdoc.io/docs/introduction-to-ethereums-keccak-256-algorithm/
  12. (secp256k1)https://www.youtube.com/watch?v=vQ1-bQ4Jt5U
  13. (ecdsa)https://www.cs.miami.edu/home/burt/learning/Csc609.142/ecdsa-cert.pdf
  14. (ecdsa)https://www.youtube.com/watch?v=f9eitAS1nsY
  15. (ecdsa vs RSA)https://sectigostore.com/blog/ecdsa-vs-rsa-everything-you-need-to-know/
  16. (ecdsa vs rsa vs dsa)https://www.linkedin.com/advice/1/what-advantages-disadvantages-rsa-dsa-ecdsa-ssh
  17. (v,r,s)https://coinsbench.com/understanding-digital-signatures-the-role-of-v-r-s-in-cryptographic-security-and-signature-b9d2b89bbc0c
  18. (ecdsa)https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages
  19. (decode raw transactiondata)https://towardsdatascience.com/decoding-ethereum-smart-contract-data-eed513a65f76
  20. (ecrecover) https://soliditydeveloper.com/ecrecover
  21. (decode)https://snakecharmers.ethereum.org/web3-py-patterns-decoding-signed-transactions/
yang_avatar

CHEN PIN YANG

軟體工程師

一杯Ramos Gin Fizz,一點blues,還有無止盡的面容焦慮。喜歡到舞池跳舞,但又懶得運動。

查看作者的其他文章

分享到

回上頁