1

为什么以太坊的交易数据中没有from地址

 2 years ago
source link: https://learnblockchain.cn/article/3540
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
为什么以太坊的交易数据中没有from地址 | 登链社区 | 深入浅出区块链技术

为什么以太坊的交易数据中没有from地址

以太坊的交易数据中之所以没有from地址,是因为他可以通过签名推导出公钥,而公钥可以推导出地址。推导公钥的过程,实际也是做验签的工作。

前几天有个小伙伴问了一个问题,为什么以太坊的交易数据中没有from地址,那怎么知道这笔交易是谁发起的?这个问题之前一直没留意,然后回想了一下,之前做验签的时候用到了一个函数 Ecrecover,它可以从签名中恢复出公钥。随口答曰:可能是签名信息里面携带了公钥吧。

回去研究了一下,前面的回答有些肤浅了。

以太坊的交易数据是这样的,确实没有from

// LegacyTx is the transaction data of regular Ethereum transactions.
type LegacyTx struct {
	Nonce    uint64          // nonce of sender account
	GasPrice *big.Int        // wei per gas
	Gas      uint64          // gas limit
	To       *common.Address `rlp:"nil"` // nil means contract creation
	Value    *big.Int        // wei amount
	Data     []byte          // contract invocation input data
	V, R, S  *big.Int        // signature values
}

然后 Ecrecover函数是这样的:

// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) {
	return secp256k1.RecoverPubkey(hash, sig)
}

它的返回值是公钥,传入的两个参数分别是:

  • hash:原始数据的hash值。
  • sig :签名结果

而sig是交易数据中的r, s, v三个整数的序列化,代码如下:

func decodeSignature(sig []byte) (r, s, v *big.Int) {
	r = new(big.Int).SetBytes(sig[:32])
	s = new(big.Int).SetBytes(sig[32:64])
	v = new(big.Int).SetBytes([]byte{sig[64] + 27})
	return r, s, v
}

怎么跟想像的不一样呢,这里面并没有任何公钥的信息。没办法只能去恶补一下加密理论知识了。

椭圆曲线公私钥

我们知道以太坊账号使用的椭圆曲线方程是secp256k1,它长这样

y² = x³ + 7

Ecc加密理论中私钥其实是一个随机大整数,假设是d,公钥是椭圆曲线上的一个点,假设是Q,椭圆曲线上还有一个点叫生成元G。Q = d*G mod n, n是椭圆曲线群的阶(不理解G和n也没关系,他们都是常量)。所以公钥是私钥推导出来的,公钥不能反推出私钥。以太坊的地址,实际是公钥的哈希,再进行编码(几乎所有区块链都是这么玩的)。

注意G和Q都是坐标点,所以有x和y两个值,本文中用大写字母表示坐标点,小写表示整数

假设待签名的数据T,对T进行hash运算之后得到M,M是一个大整数,所谓签名实际是对M做运算。

现在我们看一下签名过程:

    1. 随机选择一个整数k, 且 0 < k < n
    1. 计算 R = k * G =(x, y)
    1. r = x mod n, 如果r = 0,则返回步骤1
    1. 计算 H = Hash(M),这步是将M映射到椭圆曲线上
    1. s = (k^−1) * (H + r * d) mod n, 若s = 0, 则返回步骤1

上面公式中 k^−1表示对n取模的逆元

r和s就是签名结果,我们看到在第5步中用到了私钥d。

我们看一下怎么对上面的签名进行验签

    1. 计算 H = Hash(M)
    1. 计算 u1 = H * (s^-1) mod n, u2 = r * (s ^ -1) mod n
    1. R = u1* G + u2*Q = (x, y) mod n
    1. 如果x = r则签名成功,证明过程此处省略

上面验签的过程中的第三步用到了公钥, R = u1*G + u2*Q 。结合第二步,可以推导出公钥
Q = (R - u1* G) * (u2^-1) = (R - H*(s^-1)*G) * (r^-1)*s = (s *R - H *G) * (r^-1) mod n

在上的式子中,除了R之外都是已知数。其实R(x,y), 通过验签的第4步,可以假设x=r,将x带入椭圆曲线方程中可以技术算出y。根据上面的方程y应该有2个值,

比如下面这个方程 y的值可以是1,4

y² = 1 mod 5

到底用哪个呢。我看回头看一下上面,以太坊交易数据结构体中的签名有3个变量(v,r,s),但是我们前面签名步骤只计算出了(r,s),那v用来干嘛的。对了,它刚好可以在这里指定哪个y才是我们需要的。所以在前面,我们的签名过程还少了一步,即生成v。

我们继续思考,这个方程有没有可能无解呢?当然有这种可能,那就是前面的假设x=r错了,也就是验签失败。比如下面这个方程就没有解

y² = 2 mod 5

这个v是不是必须的呢,其实也可以不要。因为y只有两个可能的值,将他们依次带入前面的验签过程中去验证,只要通过了,那就是合法的那个。因为v只占一个字节,还能减少计算量,携带一个v是可以接受的。

以太坊的交易数据中之所以没有from地址,是因为他可以通过签名推导出公钥,而公钥可以推导出地址。推导公钥的过程,实际也是做验签的工作。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK