比特币地址有以1开头的地址,也有以3开头的地址。这两者有区别吗?
在这种情况下,地址上的比特币会被锁定?
谁拥有比特币的控制权,你?还是你的钱包?
如果你正在使用比特币钱包,却无法回答以上三个问题,那么这篇文章就是为你而写的。
SECBIT实验室在审计数字钱包的源代码时,发现一个名为pywallet的比特币钱包开源库存在严重缺陷。如果你把钱转到pywallet生成的OmniLayer收款地址,会造成资产永久损失。
SECBIT实验室的区块链技术专家zer0to0ne表示,OmniLayer协议允许在比特币区块链上发行自定义资产(如USDT)。全层资产交易的本质是比特币交易。比特币交易的代码库有很多,pywallet就是其中之一。它可以方便地构造OmniLayer格式的比特币交易。目前pywallet已经在一些数字钱包软件中使用。
但是开源库pywallet在生成OmniLayer的钱包地址时,错误地把地址的前缀写反了,部分资产被锁定在无效地址!
以下是pywallet相关错误代码截图:
文件地址:
插队:比特币网络上最常见的地址类型有三种:公共公钥地址(1-地址)、脚本哈希地址(3-地址)和隔离见证地址(bc1-地址)。地址类型由地址的前缀来区分。其中,1地址的前缀为0x00,3地址的前缀为0x05。
1-地址:这是最常见的比特币地址,通常用于普通转账收款。1-地址实际上是公钥散列的代码。1-验证地址签名后,可以解锁收据。
3-Address:这个地址是一个脚本散列地址。这种地址实际上对应的是一个比特币脚本哈希的代码。
BC1-Address: bech32编码地址,用于隔离见证事务。
开源库pywallet颠倒了地址前缀,错误地将1地址设置为3地址。因此,原本转移到1-地址的资产会被错误地转移到3-地址。当账户持有人以1地址验证即私钥签名的方式取出资产时,区块链网络以3地址执行脚本的方式执行验证,导致用户无法正常取出资产!
请谨慎使用pywallet开源库!
真相:比特币从未真正实现过转账功能。
这是很多人始料不及的,因为比特币的实现是基于UTXO模型,与我们直观的账户模型不同。Zer0to0ne解释说,实际上比特币从未真正实现通常意义上的转账功能。中本聪只是为比特币设计了一系列的比特币脚本操作器和比特币脚本执行器,而所谓的转账过程实际上是模拟了一个比特币脚本锁定和解锁的过程。这与日常生活中的总账(或账户模型)概念不同。
为了便于理解,我们可以将比特币区块链上的资产交易比作将资产锁入保险箱。只有持有保险箱钥匙的人(即收款人)才能取出保险箱内的资产进行交易。例如,如果Alice要向Bob支付一项资产,Alice将该资产锁在一个保险箱中,只有Bob拥有保险箱的钥匙,即只有Bob可以取出该资产。如果Bob想要取出资产,那么Bob需要同时花费资产(即,将它们锁在另一个保险箱中)。在鲍勃拿出资产之前,这些资产并不真正属于鲍勃。想象一下,如果Bob丢失了他的钥匙,他就不能取出他的资产。换句话说,当这个资产仍然保存在保险箱中时,它不属于爱丽丝或鲍勃。当然,爱丽丝也可以把她的资产放在一个任何人都可以打开的保险箱里,这也被称为任何人都可以花的交易。
由于比特币区块链上的支付地址不同,保险箱的类型也不同。不同类型的保险箱需要不同类型的钥匙才能打开。付款方为收款方定制一个保险箱,将资产放入保险箱并上锁,然后将保险箱扔在公共场所。有两种方法可以打开保险箱:
如果收款人是1地址,我们称之为1类保险箱。并且对应的密钥必须是对应于指定收集地址的私钥。保险箱解锁的过程就是验证1地址公钥和公钥对应的数字签名,也就是我们通常理解的转账到普通账户地址的验证过程。
如果收款人是3地址,我们称之为3型保险箱。密钥必须是可执行的比特币脚本。打开保险箱的过程是比特币脚本的哈希值对应3-地址,运行脚本后比特币脚本执行器成功返回。也就是说,只有拥有原始脚本并能成功执行的人,才能提取这个保险箱里的资产。
让我们回到这一节的问题:为什么比特币从来没有实现真正的转账功能?答案很简单,因为比特币系统中没有账户的概念,账户之间的转账无从谈起。未来一个人能开多少个保险柜也是未知数。
从上面的解释中我们知道,当pywallet开源库错误地将1地址识别为3地址时,就像是将原来的1型保险箱改造成了3型保险箱,但是账户持有人仍然持有1型保险箱的钥匙来解锁,那么自然是无法打开保险箱的。那么,zer0to0ne发现的错误锁定的OmniLayer数字资产还能追回吗?
可以用1地址的钥匙打开3保险箱吗?
Zer0to0ne随后向我们详细解释了两个重要概念P2PKH(付费公钥哈希)和P2SH(付费脚本哈希)的来龙去脉。这两个名词分别代表了两种不同类型的比特币交易。
以下是zer0to0ne的精彩技术细节分析:
P2PKH——中本聪的伟大发明
Pay to Public Key Hash,顾名思义就是把比特币放在保险箱里,而keyhole Hash(公钥哈希。我们看到的最常见的单地址本质上是一个公钥散列码。1- address的生成过程也很简单。公钥哈希由Hash160获得,前缀0x00加在公钥哈希的头,校验和加在哈希的尾。Base58后,获得以1开头的比特币地址。
Base58(0x00校验和)
我们来看看P2PKH交易类型的安全构建流程。以爱丽丝给鲍勃发送比特币为例:
付款人Alice在构造保险箱时需要设置一个锁脚本:
OP_DUP
OP_HASH160
(包含在Bob的收集地址中的公钥散列)
OP _ equal验证
OP_CHECKSIG
注:我们可以把这一步理解为爱丽丝为鲍勃定制了一个保险箱,把比特币放进保险箱,用鲍勃的公钥PubKey Hash锁起来。现在这个锁除了持有私钥的Bob,任何人都打不开。
当Bob需要花费Alice给他的比特币时,他需要提供必要的参数:交易签名公钥(技术俚语:scriptSig)来打开保险箱,这样锁脚本执行后就会返回True。这一步通常由钱包自动完成。
我们来看看比特币节点如何验证scriptSig的合法性。
(图片来自掌握比特币)
脚本执行过程如图所示。Bob将签署事务并获得数据(实际上包括数据长度信息)。真正的scriptSig应该是,比特币脚本执行器从推送数据开始,推送操作会读取第一个字节,得到将要放入堆栈的数据长度信息,然后继续执行比特币脚本,直到最后执行完毕,检查执行结果。
首先,然后,它将被放入堆栈。一个DUP操作将被复制到堆栈的顶部。HASH160弹出栈顶,计算散列,将结果推回栈中。然后用EQUALVERIFY弹出hash比较和是否相等。如果它们相等,它将返回True。如果它们不相等,它会将事务标记为无效。这一步公开了公钥,保证了签名人身份的正确性。但是黑客或矿工可以通过暴露的公钥构造一个新的交易来代替原来的交易,这不能保证安全性,所以需要下一步来保证交易不能被伪造。此时,堆栈上有和。执行CHECKSIG将验证数字签名的正确性,并确保签名者拥有与地址对应的私钥。
除了持有私钥的人之外,任何人都不能伪造签名。至此,一笔比特币P2PKH交易安全完成。
再解释一遍:当鲍勃想要花掉爱丽丝给他的比特币时,鲍勃只能用正确的钥匙打开爱丽丝留给他的保险箱,把钱放进鲍勃新建造的保险箱。
这时,一些聪明的读者会注意到一个细节:如果鲍勃拿出钥匙,区块链的任何一个矿工都能在打开保险箱之前看到钥匙的形状。理论上,他们可以立即复制一把钥匙,打开爱丽丝留给鲍勃的保险箱并花掉(俗称前跑攻击)。
你真的能做到吗?显然,中本聪已经考虑到了这个问题。该密钥中的交易签名是Bob发起的交易的完整签名。假设Bob想把Alice建的保险柜里的比特币放到一个新的保险柜里(给Charlie)。这时,Bob给出的密钥包含了Charlie的公钥Hash。虽然矿工可以复制Bob的钥匙,但是这个钥匙已经隐藏了下一个新保险箱的钥匙信息,所以矿工无法使用这个复制的钥匙完成其他动作(数字签名不能被盗用)。
P2SH——:后中本聪时代的伟大创新
中本聪设计了这么强大的脚本系统,只构造转会交易似乎太浪费了。我们试着用其他指令构造一些特殊的加锁脚本,用其他方式解锁。
例如,我们可以构建一个使用哈希前映像解锁事务的脚本:
OP_HASH160
OP_EQUAL
这个脚本的意思是,当满足Hash160(前像)==的条件时,可以成功解锁脚本。
我们继续通过保险箱的例子来解释,将这类保险箱命名为三级保险箱。现在爱丽丝给鲍勃的比特币被锁在一个由上面的Hash160保护的保险箱里。让我们称之为散列锁。
锁仍然需要正确的形状才能打开,但它的安全性要弱得多。由于缺少数字签名机制而被钥匙隐藏的钥匙信息不会随着Bob新建的保险箱而改变。任何一个矿工只要鲍勃一露出来,就可以复制出确切的钥匙,并抢着打开爱丽丝留给鲍勃的前跑保险柜,把硬币转移到另一个人伊芙身上,那么原本属于鲍勃的比特币就会被洗劫一空。
虽然这个脚本非常不安全,但是它有两个非常神奇的功能:
1.交易结构的输出足够短,意味着比特币节点维护的UTXO缓存空间会大大减少。
2.前像总是在交易花完的时候作为输入被引用,不会出现在交易的输出端。UTXO保持精简,同时可以把手续费的负担转移给接收方。
既然所描述的输出脚本有很多好处,那么我们有没有办法让这个事务安全呢?有必要说说P2SH是什么。
比特币的核心开发者加文艾德森(Gavin Adresen)提出了一项名为支付脚本哈希(P2SH)的技术。
P2SH的事务输出仍然是判断Hash160(脚本)==