ETH交易签名算法及各种Signer

签名算法

ETH Tx签名使用的是secp256k1签名算法。具体细节如下:

  • 签名算法代码
github.com/ethereum/go-ethereum/crypto
  • 私钥类型:secp256k1.PrivKeySecp256k1。
  • 私钥对应地址类型: common.Address。
  • 签名时使用私钥:ecdsa.PrivateKey。
//从secp256k1.PrivKeySecp256k1转化为ecdsa.PrivateKey。使用crypto包 
key.ToECDSA()
  • 签名。对输入私钥,哈希签名
sig, err := ethcrypto.Sign(txHash[:], priv) 
//进一步调用: 
secp256k1.Sign(digestHash, seckey)
  • 验证。输入pubkey,哈希,以及签名验证。
secp256k1.VerifySignature(pubkey, txHash, sig)

各种Signer

secp256k1签名算法是封装的独立接口,对传入的任意hash签名,而不在乎hash代表的意义。

当然我们这里说的hash代表的是tx哈希。

在ETH版本升级硬分叉过程中,tx的哈希也在变化,为了计算不同版本的哈希,出现了各种Signer:不同signer计算出来的tx哈希是不一样的。

看签名代码的实现,Signer的核心作用就是用于计算哈希:

// SignTx signs the transaction using the given signer and private key.
func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
    h := s.Hash(tx)
    sig, err := crypto.Sign(h[:], prv)
    if err != nil {
       return nil, err    }
    return tx.WithSignature(s, sig)
}

现在ETH中已经有很多Signer了:

// MakeSigner returns a Signer based on the given chain config and block number.func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
    var signer Signer    switch {
    case config.IsLondon(blockNumber):
       signer = NewLondonSigner(config.ChainID)
    case config.IsBerlin(blockNumber):
       signer = NewEIP2930Signer(config.ChainID)
    case config.IsEIP155(blockNumber):
       signer = NewEIP155Signer(config.ChainID)
    case config.IsHomestead(blockNumber):
       signer = HomesteadSigner{}
    default:
       signer = FrontierSigner{}
    }
    return signer}

我们按照ETH升级的时间线,整理下各个Signer哈希的区别。

  • Frontier。第一版以太坊
  • Homestead。升级高度:1,150,000
  • EIP155。EIP155是被包含在Spurious Dragon版本中。升级高度:2,675,000
  • EIP2930。EIP2030倍包含在Berlin版本中。升级高度:12,244,000
  • London。升级高度:12,965,000

FrontierSigner

这是最原始的哈希计算方式:

// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
    return rlpHash([]interface{}{
       tx.Nonce(),
       tx.GasPrice(),
       tx.Gas(),
       tx.To(),
       tx.Value(),
       tx.Data(),
    })
}

HomesteadSigner

这一版其实和Frontier一样。为啥一摸一样的要搞两份呢?可能当初开发者觉得,即然版本都硬分叉了,Signer也变变没毛病吧,可是又没有什么好变的,那就变个名字吧~(仅属个人臆测)。

// HomesteadSigner implements Signer interface using the 
// homestead rules. 
type HomesteadSigner struct{ FrontierSigner }

EIP155Signer

这一版在哈希中加入了chainid,目的是为了防止重返攻击,为啥不用版本名Spurious Dragon,而用EIP155作为Signer名字呢?显然一个字,任性。

// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
    return rlpHash([]interface{}{
       tx.Nonce(),
       tx.GasPrice(),
       tx.Gas(),
       tx.To(),
       tx.Value(),
       tx.Data(),
       s.chainId, uint(0), uint(0),
    })
}

eip2930Signer

这一版引入了accessList,可以看小弟关于eip2930的介绍,做了gas费读取的调整。

// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s eip2930Signer) Hash(tx *Transaction) common.Hash {
    switch tx.Type() {
    case LegacyTxType:
       return rlpHash([]interface{}{
          tx.Nonce(),
          tx.GasPrice(),
          tx.Gas(),
          tx.To(),
          tx.Value(),
          tx.Data(),
          s.chainId, uint(0), uint(0),
       })
    case AccessListTxType:
       return prefixedRlpHash(
          tx.Type(),
          []interface{}{
             s.chainId,
             tx.Nonce(),
             tx.GasPrice(),
             tx.Gas(),
             tx.To(),
             tx.Value(),
             tx.Data(),
             tx.AccessList(),
          })
    default:
       // This _should_ not happen, but in case someone sends in a bad       // json struct via RPC, it's probably more prudent to return an       // empty hash instead of killing the node with a panic       //panic("Unsupported transaction type: %d", tx.typ)       return common.Hash{}
    }
}

londonSigner

这一版升级是EIP1559。相当重要的调整,把gas划分为baseFee和tip,目标是调整网络拥堵。具体也可见小弟关于EIP的介绍。

对应的,在哈希中也引入了GasTipCap(最大小费的gas price)和GasFeeCap(最大gas price)。

// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s londonSigner) Hash(tx *Transaction) common.Hash {
    if tx.Type() != DynamicFeeTxType {
       return s.eip2930Signer.Hash(tx)
    }
    return prefixedRlpHash(
       tx.Type(),
       []interface{}{
          s.chainId,
          tx.Nonce(),
          tx.GasTipCap(),
          tx.GasFeeCap(),
          tx.Gas(),
          tx.To(),
          tx.Value(),
          tx.Data(),
          tx.AccessList(),
       })
}


《 “ETH交易签名算法及各种Signer” 》 有 5 条评论

  1. Antony EWPFMyIDLB 6 16 2022 how long does it take augmentin to work 149 Based on these studies, expert opinion now recommends this drug combination be utilized for unavoidable rapid ascents, such as rescue missions or military operations at high altitude

  2. buy priligy This treatment carries a risk of damage to the nipple and a loss of feeling

  3. pqf64cd8e32f5ac7553c150bd05d6f2252bb73f68dpq 的头像
    pqf64cd8e32f5ac7553c150bd05d6f2252bb73f68dpq

    pq118a9989815489c24b81b160782015890ed2085epq

  4. priligy near me We find that a state specific rewiring of the mitochondrial proteome by the i AAA peptidase YME1L is required to preserve NSPC self renewal

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

About Me

一位程序员,会弹吉他,喜欢读诗。
有一颗感恩的心,一位美丽的妻子,两个可爱的女儿
mail: geraldlee0825@gmail.com
github: https://github.com/lisuxiaoqi
medium: https://medium.com/@geraldlee0825