IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 区块链 -> 以太坊从公钥生成地址的具体过程 -> 正文阅读

[区块链]以太坊从公钥生成地址的具体过程

作者:commentBox

网上有大量的文章介绍了ECDSA(椭圆曲线加密)算法来生成以太坊公私钥对,进而生成一个唯一的以太坊地址。其中绝大部分都提到了非压缩公钥生成地址时先进行哈希运算,然后取后40位就是地址了。然而绝知此事要躬为。到底怎么哈希的,公钥和私钥的格式是什么,相信很多人和我一样不清楚!

笔者因为在研究一个东西,需要顺带弄明白以太坊公钥生成地址的细节流程。笔者首先百度了一下,参考了知乎上面的一篇文章《以太坊的私钥、公钥、地址、账户》 。然而摸索半天之后,得不到文章中列出的结果。最后笔者将文章中的私钥导入到MetaMask之后,让人大跌眼镜的是,最后得到的地址竟然不是文章中的示例地址。看来一定是哪出问题了,于是笔者决定弄清楚这个公钥到地址的清晰生成流程,不能再模模糊糊了。

知乎中这篇文章中有个流程图:
在这里插入图片描述
这个流程图很正确,只有简单三步,然而结果却不正确,最有可能出错的地方应该就是 从公钥经keccak-256算法到压缩公钥了。具体的Keccak-256函数是什么呢?不同的语言有不同的实现,这里笔者还是以最常用的Node.jsethers框架来进行这个问题的验证。

很幸运,ethers框架中有一个直接使用公钥计算地址的函数,下面是该函数的定义及说明:

ethers.utils.computeAddress( publicOrPrivateKey ) ? string< Address >source
Returns the address for publicOrPrivateKey. A public key may be compressed or uncompressed, 
and a private key will be converted automatically to a public key for the derivation.

这是已经封装好的计算库,我们欲知道详细过程参考它的源码实现就能得到。这里具体的参考过程我不讲了,下面直接贴出验证成功的脚本(假定叫test.sol):

//根据公钥生成地址实例详细流程
const eccrypto = require("eccrypto");
const sha3 = require("js-sha3");
const {ethers,utils} = require("ethers")

const private_key  = "18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725";
const my_wallet = new ethers.Wallet(private_key)
const HexCharacters = "0123456789abcdef";
const public_key = my_wallet.publicKey
printPublicKey(public_key)

//第一步: 移除公钥前两位04,如果包含0x就是移除四位了,再重新加上0x构造
let new_key = "0x" + public_key.substring(4)
//第二步:对上面的结果转化成bytesLike(不能漏)
let new_bytes = utils.arrayify(new_key)
//第三步,keccak_256,得到一个长度为64的哈希值
new_key = sha3.keccak_256(new_bytes)
//第四步,取上面结果的最后40位,就得到了全小写的地址。
let result = "0x" + new_key.substring(24)
//最后,将地址转换成检验后的地址
result = utils.getAddress(result)
console.log("")
console.log(result)
console.log(result === my_wallet.address)

function printPublicKey(public_key) {
    console.log(public_key.substring(2,4))
    let half = (public_key.length - 4)/2
    console.log(public_key.substring(4, 4+half))
    console.log(public_key.substring(4+half))
}

//unused
function convertBytesToHexString(value) {
    let result = "0x";
    for (let i = 0; i < value.length; i++) {
            let v = value[i];
            result += HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f];
    }
    return result;
}

从上面的代码中可以看出,使用公钥生成地址有这么几步:

  1. 移除公钥的前缀04,这个知乎那篇文章中提到:

采用椭圆曲线数字签名算法ECDSA-secp256k1将私钥(32字节)映射成公钥(65字节)(前缀04+X公钥+Y公钥):

但是它却并没有提到计算Keccak-256时要移除前缀。

  1. 将上面移除04后的16进制字符串再次转化为字节数组
  2. 对上一步得到的字节数组进行keccak_256运算,得到一个长度为64的哈希值(压缩公钥,32字节)。
  3. 取上面结果的最后40位,就得到了全小写的地址。
  4. 可选项。将全小写地址转化为校验后的地址。

我们直接node test.js 会得到以下输出:

04
50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352
2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6

0x3E9003153d9A39D3f57B126b0c38513D5e289c3E
true

这样格式的输出是为了和原知乎文章相比较。

至于原文章为什么最后得到的地址是错的,这里笔者重现了一下,原因在于它直接对移除04后的公钥字符串进行了Keccak-256运算,而并未将其转化为字节数组。

  区块链 最新文章
盘点具备盈利潜力的几大加密板块,以及潜在
阅读笔记|让区块空间成为商品,打造Web3云
区块链1.0-比特币的数据结构
Team Finance被黑分析|黑客自建Token“瞒天
区块链≠绿色?波卡或成 Web3“生态环保”标
期货从入门到高深之手动交易系列D1课
以太坊基础---区块验证
进入以太坊合并的五个数字
经典同态加密算法Paillier解读 - 原理、实现
IPFS/Filecoin学习知识科普(四)
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 11:20:47  更:2021-07-16 11:21:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:47:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码