写点什么

漫话比特币(三):匿名性,公钥的再加工

2021 年 3 月 03 日

漫话比特币(三):匿名性,公钥的再加工

在前两篇,我们介绍了比特币基于交易的记账系统,人们通过手中的公私钥来持有 BTC 资产、以及行使支付权利。为了强调 UTXO、公私钥和支付权利这些重要概念,我们在交易记录的内容上做了简化,三笔交易示例的借方地址中,记录的通通都是接收者的公钥。

 


初识比特币交易 – 简化版

 

有了非对称加密的背景知识,我们知道公钥可以随意发布给任何人。尽管如此,比特币系统在其代码实现中,还是放弃了直接在交易记录中存储公钥,转而用公钥哈希来记录借方地址,从而提供更好的匿名性。毕竟,比特币是一种现金系统,毫无疑问,匿名性是现金最重要的特性之一。

 

PKH:公钥哈希

 

说起公钥哈希,你可能比较陌生,不过对于比特币地址,想必你早已耳熟能详。当你需要接受别人的 BTC 转账时,只要把你的比特币地址、或是对应的二维码告诉对方就 OK 了。那么,比特币地址是怎么来的呢?

 


 从公钥计算比特币地址

 

从公钥到比特币地址的计算,尽管整个过程看上去比较复杂,但如果仔细观察的话,不难发现这个流程分为两个阶段。第一个阶段是,公钥经过两次哈希运算,得到一个叫作 PKH 的东西;第二阶段,就是从 PKH 到比特币地址的转换。我们先来关注第一个阶段:从公钥到 PKH。

 


从公钥计算 PKH

 

在这个阶段,公钥(33 字节)先经过 SHA256 哈希运算,得到 32 字节的哈希值,然后再经过 RIPEMD160 哈希算法计算,得到 20 字节的哈希值。公钥经过这两次哈希计算得到的 20 字节哈希值,在比特币的术语中就叫作 PKH(Public Key Hash,公钥哈希值)。

 

你可能会好奇:“一次哈希计算还不够吗?为什么需要连续两次哈希计算呢?”这是个好问题。通常来说,一次哈希计算已经能够很好地保证匿名性了。哈希算法不可逆,公钥经过 SHA256 计算得到的哈希值,是无法拿来反向计算得到公钥的。中本聪先生早已隐匿于江湖,所以这个问题一直是个谜。

 

不过,我们不妨这样推断:两次哈希计算的初衷,是为了更好地保护公钥的匿名性和安全性,第二次 RIPEMD160 哈希计算,相当于又加了一层保险,从而保证尝试通过 PKH 破解得到公钥的努力都是徒劳。尽管哈希算法对输入敏感,但还是有极低的概率花较少的时间和成本成功破解哈希值,采用两次哈希,那么两个近乎于 0 的概率相乘,得到的概率会更趋近于 0,从而让 PKH 破解成为事实上的不可能。

 

另外,一个有趣的事情是,SHA256 哈希算法是由美国国家安全局(NSA,National Security Agency)发明的,而 RIPEMD160 哈希算法是由欧洲的密码学家们提出并实现的,也许中本聪先生担心算法发明者有所保留,因此采取制衡的方式来加强比特币的安全性和匿名性。



 两个哈希算法的出处不同

 

说到这里,你可能会问:“PKH 看上去是一堆字符串,比特币地址,看上去也是一堆字符串。为什么不能把 PKH 当作是比特币地址,从而省略掉后面的第二阶段呢?”咱们不妨来做个思想实验,一起分析分析,如果拿 PKH 当作比特币地址,会有哪些问题和隐患?

 

烧钱

 

假设 PKH 就是比特币地址,那么在收款的时候,你需要把你的 PKH 告诉对方。我们看到,20 个字节的 PKH,是一个由数字和字母组成的字符串。那么显然,手写这么长的字符串,有很大的概率出现拼写错误,比如 1 被当作 l,c 错写成了 x(键盘上位置相近),或者干脆多写或者漏写了某个字符。想象一下,在某网站进行注册的时候,如果它要求你设置不少于 20 个字符的密码,你的内心作何感受?

 

要命的是,一旦你的 PKH 拼写错误,别人按照错误的 PKH 去进行转账,你收不到钱是小事,这笔误转账凭空烧掉了一定数额的 BTC,是大事

 

要知道,PKH 是公钥经过两次哈希(SHA256 和 RIPEMD160)计算得到的,哈希计算不可逆。你误写的 PKH,很可能对应的是一把“幽灵公钥”,而这把幽灵公钥不归任何人所有。换句话说,任何人都没有资格、没有权利去花费‘误转账’的这笔钱。这意味着什么?

 


 PKH 误拼写对应着“幽灵公钥”

 

意味着这笔钱凭空消失了!把钱转账给一把幽灵公钥,那么,这笔钱谁也花不出去,这就好比在现实生活中,你把 100 元现金烧了,被烧掉的这张 100 元现金,自然任由谁也花不出去。

 

现实生活中,即便你烧了现金,没人可以再花这 100 元钱,市场上也确实少了 100 元钱的现金流通,但是,国家可以根据经济运行状况,选择适量增发货币。与法币不同,比特币代币 BTC 总量固定、不能增发,上限 2100 万枚。目前已开采的比特币供应量为 1800 万枚左右,到 2140 年开采量将达到总量 2100 万枚,到那时,比特币系统将不再印发新的 BTC。

 

比特币代币总量固定、不能增发,所以你看,“烧钱”是不是一件大事?因此,为了避免因 PKH 误拼写而“烧钱”,中本聪先生设计了比特币地址。

 

比特币地址

 

话付前言,我们再说回第二阶段:从 PKH 到比特币地址的转换。转换的过程称为 Base58 Check,这个过程包含 3 个步骤:

o 添加版本号前缀

o 追加验证码

o Base58 编码

 


 PKH 到比特币地址

 

版本号的添加,主要是考虑到未来开发的可扩展性,比如将来为比特币扩展其他的新功能。验证码是比特币地址转换的关键,它用于检查拼写错误并确保 PKH 和比特币地址都是有效的。Base58 编码是中本聪先生发明的编解码算法,算法的码字集合基于大小写字母和数字,剔除了其中容易导致混淆的字符,最终保留了 58 个基本字符,因而命名 Base58。

 

Base58 Check 过程展开

 

具体来说,添加了版本号 0x00 前缀的 PKH,经过两次 SHA256 哈希计算得到哈希值,取前 4 个字节当作验证码,如上图中的 0xfba5ec1a。验证码追加到 PKH 末尾,将带版本号前缀和验证码后缀的 PKH 经过 Base58 编码后得到比特币地址。在上图的例子中,韩梅梅的公钥经过两次哈希计算,得到 PKH:0x3eb554f2eeb524af270b051ebaeabc0560245fb6(16 进制,20 字节),该 PKH 经由 Base58 Check 编码最终得到比特币地址:16ia83uLfpF3frGBD5DDuVyDD1a5aYoBzm。

 

如果仔细观察 Base58 Check,会发现它的转换过程也是双向、可逆的。在 Base58 Check 的反向运算中,比特币钱包将接收到的比特币地址先进行 Base58 解码,得到加了 0x00 前缀和验证码后缀的 PKH,对该字节序列“掐头去尾”(掐掉头部的 0x00,去掉尾部的 4 字节验证码),从而得到原始的 PKH。

 

将此 PKH 加上 0x00 前缀,连续做两次 SHA256 哈希运算,得到 32 字节哈希值,取该哈希值的前 4 个字节与“去尾”的验证码进行对比,如果两者一致,说明比特币地址拼写无误、且 PKH 有效,否则该地址无效。由此可见,验证码是比特币地址有效性自检的关键所在。

 

总结下来,使用 Base58 Check 编解码算法,比特币系统可以灵活地在比特币地址和 PKH 之间转换,用户在收款时提供比特币地址来避免误拼写导致的“烧钱”问题,交易记录(Transactions)在借方地址记下 PKH 来保护匿名性并提高存储效率,用户与系统各取所需。

 

重新审视支付过程

 

到目前为止,我们介绍了 PKH 和比特币地址的生成流程,并强调,比特币系统在其代码实现中,用 PKH 来记录借方地址,从而提供更好的匿名性。在上一篇《非对称加密》中,我们介绍了支付权利的行使过程。还记得“派尔伊特”咒语吗?你挥一挥魔杖,用你的 DNA 召唤一个只能用你的指纹才能打开的宝箱。你的 DNA 就是私钥,指纹就是公钥,而宝箱,就是你的数字签名。

 

数字签名需要与公钥配合,才能完成身份验证,从而行使的支付权利。PKH 是公钥经过两次哈希计算得到的哈希值,公钥的原始信息已被擦除,PKH 无法解密由私钥生成的数字签名,自然也就不能完成身份验证。那么问题来了,交易记录的借方地址,存储的是 PKH,而不是公钥,在这种情况下,支付权利要怎么行使呢?



 比特币系统中交易记录的实际构成

 

为了阐明支付权利的行使过程,我们在前两篇简化了交易记录的数据构成。现在有了 PKH 的概念,咱们来对“初识交易”中的交易记录进行适当的“修正”。在之前的交易记录中,借方信息存储的是公钥,贷方存储数字签名;修正后的交易记录的借方信息替换为 PKH,那么在需要花费 UTXO 的时候,在贷方部分仅仅提供数字签名自然是不够的。

 

正如刚才所说,PKH 是公钥经过两次哈希计算的哈希值,早已丢失公钥中的有效信息,自然不能解密由私钥加密的交易信息(数字签名)。因此,在进行支付时,需要同时提供数字签名和公钥。相应地,判断是否具有支付权利的流程与之前相比,变更为:

o 对于在交易输入(贷方信息)中提供的公钥,采用 SHA256 + RIPEMD160 进行双哈希计算,得到哈希值 PKH^;将 PKH^与 UTXO 中的 PKH 比对,二者需完全一致

o 对于交易输入中提供的数字签名,用公钥验证其身份。换句话说,派尔伊特咒语能够成功施法,用公钥“指纹”,可以成功打开数字签名“宝箱”。

 

对于支付权利的判定,这两个步骤缺一不可。以图中高亮显示的部分为例,韩梅梅想要成功完成支付,必须同时满足两个条件:

o 交易 tx-00693 贷方信息中提供的公钥在经过双哈希运算后得到的哈希值与前两个交易(tx-00825 和 tx-00901)借方信息中的 PKH 保持一致

o 贷方信息中提供的公钥能够成功验证韩梅梅的数字签名

 

对于交易记录的修正,你可能会说:“看上去无非是多了个 PKH,然后把公钥从上一笔交易的 Output,挪到了下一笔交易的 Input,一个半斤、一个八两,也没什么本质区别啊!”

 

其实还是有所区别的,在基于 PKH 的记账系统里,BTC 持有者在支付、转账的时候,才需要提供公钥、数字签名这些个人敏感信息。换句话说,BTC 持有者在行使支付权利的时候,才需要提供这些信息。但在之前的存储方式中,BTC 持有人在接收对方的转账时,就得暴露公钥信息。对于那些只想囤币、积累 BTC 资产的人来说,从匿名性的角度出发,自然是 PKH 的存储方式更友好。

 

Postscript

 

本篇是《漫话比特币》科普系列的第三篇,该科普系列面向所有具备计算机基础知识的读者,尽可能地采用生活化类比和示例来帮助读者更好地理解、消化技术内幕中涉及的关键知识点,如哈希算法、非对称加密、交易的结构、区块链的构成,等等。

 

为了让读者能够专注于比特币系统的整体实现,本系列隐去了一些技术细节,如哈希算法的工作流程、椭圆曲线加密的工作原理等,更多地从每一个技术点的特性、特点、用途出发,逐渐勾勒出比特币系统的全貌。笔者学浅才疏、疏漏难免,还请广大读者朋友批评、指正,更欢迎您一起参与探讨比特币、区块链的相关话题。


更多阅读:


漫话比特币(一):基于交易的记账系统


漫话比特币(二):非对称加密

2021 年 3 月 03 日 07:002504

评论

发布
暂无评论
发现更多内容

2020年高频Java面试题集锦(含答案),让你的面试之路畅通无阻!

Java成神之路

Java 程序员 架构 面试 编程语言

架构师训练营第八周作业

丁乐洪

推进工业互联网和区块链创新发展

CECBC区块链专委会

区块链 互联网

FFmpeg使用基础(音视频开发入门)

赖猫

架构师训练营第 12 周学习总结

netspecial

极客大学架构师训练营

刚参加完阿里P6面试归来(Offer已斩获),6点面试经验总结

Java架构之路

Java 程序员 架构 面试 编程语言

2020最新最全的Java架构面试复习指南,掌握10%阿里P7没问题

Java架构之路

Java 程序员 架构 面试 编程语言

Java开发者必读的〈Java开发手册(嵩山版)〉灵魂15问,深究Java规约背后的原理。

Java成神之路

Java 程序员 架构 面试 编程语言

数字人民币红包迎战“双十二” 六大行钱包全接入

CECBC区块链专委会

数字人民币

面试官:简单说一下RocketMQ整合SpringBoot吧

比伯

Java 编程 程序员 架构 计算机

架构训练营-week-12总结

于成龙

架构训练营

可能会重塑未来移动支付市场的格局

CECBC区块链专委会

货币

Java内存模型JMM详细解析

云流

程序员 并发编程 架构师 java面试

架构师训练营第 12 周作业

netspecial

极客大学架构师训练营

架构师训练营 1 期第 12 周:数据应用(一)- 作业

piercebn

极客大学架构师训练营

真的爱了!这份阿里P8整理的《Java核心技术整理》新版手抄本,简直把所有Java知识操作都写出来了

Java成神之路

Java 程序员 架构 面试 编程语言

2020的另一面:5G的斯普特尼克之年

脑极体

命令行搜索神器fzf

Rayjun

Linux

陪你手撕源码系列之 STL set 相关算法

herongwei

c++ 算法 set stl

阿里聚划算5轮面试题:GC收集器、多线程锁、海量数据技术考核

Java架构之路

Java 程序员 架构 面试 编程语言

什么?还不知道该如何学习微服务?这份Github上星标55.9k的微服务神仙笔记真的太香了!

Java成神之路

Java 程序员 架构 面试 编程语言

网络篇:朋友面试之TCP/IP,回去等通知吧

Crud的程序员

TCP 网络协议 IP

玛雅公约软件系统开发|玛雅公约APP开发

开發I852946OIIO

系统开发

Github上标星30K+的SpringBoot实战电商项目,简直不要太牛!

Java成神之路

Java 程序员 架构 面试 编程语言

一只支持凡尔赛文学创作的摄影手机

脑极体

架构训练营-week12-作业1

于成龙

LeetCode题解:22. 括号生成,BFS,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

架构师系列9: 找出单向链表合并节点

桃花原记

架构师训练营第三周”代码重构“作业

随秋

极客大学架构师训练营

spring2.5.6+java6升级到spring4+java8了

阿水

Java spring 升级

怎么保护自己的音乐作品不被盗用,用FL制作防盗水印片段

懒得勤快

版权保护 音乐 音乐制作 编曲

低代码的认知误区与落地实践

低代码的认知误区与落地实践

漫话比特币(三):匿名性,公钥的再加工-InfoQ