比特币价格的上上下下,始终撩动着每一个人无比关切的小心脏。从去年初的 800 美元左右,飞涨到去年底到 19783.21 美元最高点,不到1年,便有将近 25 倍的升值速度。尽管眼下又掉回 8000 多美元的价格,但价格差不多能搞出去年同期一个数量级,币圈人士“过去一年比以往 10 年挣的都多”,已经是不争的事实。
而对区块链开发者来说,据说也已经有拿到年新 500 万的天价。所以“跑步进入区块链”,已经成为不少程序员的共识。但是看过很多远离,我们如何才能迅速上手呢?国外网友 Ken Shirriff 在博客中分享了他在手动茶古剑比特币交易时的代码与对比特币协议的心得,区块链大本营编译如下。
近期,媒体行业对比特币表现出极大的热情,这鼓舞着我从网络底层的数据流开始,认真学习比特币的工作原理。通常人们会使用钱包软件来进行比特币交易,钱包软件在方便用户的同时,向用户隐藏了比特币的交易流程,而我想亲自动手来体验比特币交易,我的目标是用Python手动创建一笔比特币交易,以十六进制数据的形式将交易广播到比特币网络中,然后观察这笔交易是怎么被加入到区块链中的。事实证明,这个过程很有趣,希望你也对它感兴趣。
在本篇文章中,首先我会对比特币进行一个简单的概述,之后,我会从以下几个方面带领你们学习比特币:创建一个比特币地址(比特币中的账户),进行一笔比特币交易,签署交易,将交易广播到比特币网络中,最后等待交易的确认。
比特币简述:
首先,我会介绍一下比特币系统是怎么运转的,然后再深入探讨整个细节。比特币是一个基于点对点网络的电子货币,你可以用现金在网上购买比特币,用比特币向他人转账,在有些商家,你可以像使用支付宝一样使用比特币付款,当然,你也可以卖出所持有的比特币换回现金。
简而言之,在比特币网络中,分布式账本(区块链)记录并随时更新着每个比特币的所有权。与银行不同的是,比特币并没有与个人或个人的账户绑定,相反的,比特币只属于一个个比特币地址,比如:1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa。这里你可能已经绕晕了,难道这段字符中藏着比特币?当然不是,比特币地址是比特币网络中的一个身份,也可以通俗地说是你在比特币中开的一个“银行账户”,我们用这个“账户”来进行交易。在网站:blockchain.info中,你可以查到所有的交易信息:
比特币账户信息
但是怎么证明这个账户是我的呢,不急,先往下看,你的疑问我会为你一一解答。
比特币交易
如何像使用现金一样使用比特币呢?答案是创建一笔交易。在一笔交易中,比特币的所有者(上文提到过比特币的所有者是比特币地址)将所有权转移到一个新的比特币地址。比特币的一个颠覆性创新就是通过鼓励节点记账(也叫矿工挖矿),将交易记录放在一个分布式的数据库中。交易被集合在区块中,大概每十分钟比特币网络中产生一个新的区块,成为交易记录的一部分,称为区块链。加入到区块链中的交易可以被认为是一笔成功的交易。现在问题来了,谁来给你记账呢?是矿工,矿工的挖矿过程就是在往区块链中记账,矿工要核实每笔交易是否正确,核实完后,矿工们就开始算一道很难的数学题(密码学中的哈希函数),最早算出答案的人就能生成一个区块,也叫挖出了一个新的区块,这个区块将成为区块链的新一部分。
也许你会问了,明明是记账,干着会计的活,为什么要叫挖矿呢?和传统的在地下挖矿石一样,比特币挖矿也是会有收获的。挖矿是一种新发行比特币的过程,当前,每挖到一个矿,矿工会得到系统奖励的12.5个比特币,按目前一个比特币接近一万美元的市价,这就是一笔12.5万美元的巨款。此外,矿工还可以获得本区块中所有的交易费,举例来说,在高度为512587的区块中,幸运的矿工总共收获了12.829个比特币。正因如此,矿工之间的竞争十分激烈,采矿的难度与矿工间激烈的竞争是比特币安全的重要保证,因为这样可以保证没有坏人能操纵系统。
点对点网络
比特币并没有一个中央服务器,相反,比特币在一个点对点网络中运行。如果你运行一个比特币节点,那你就成了网络的一部分。比特币网络中的节点彼此交换自己存储的交易,区块,以及IP地址信息(用于节点间建立连接互相通信)。当你第一次连接到比特币网络,你的节点会从随机挑选的节点中下载区块链的信息。反过来,你的节点也会向后加入者提供信息。当你要创建一笔比特币交易时,你要把这笔交易发送给一些节点,这些节点会在比特币网络中广播这笔交易,直到全网都收到这笔交易。矿工们会收集你的交易信息,生成一个含有你这笔交易的区块,向全网广播,这时,你的节点也会收到这个区块信息,通过验证,这笔交易被加入到了区块链中,你就交易成功了。
加密技术
现在回到证明比特币账户是谁的这个问题。比特币使用数字签名技术以确保只有比特币账户的所有者才能使用账户中的比特币。比特币地址的所有者拥有与该地址相匹配的私钥,当花费比特币时,你用要这个私钥在交易上签名,证明自己是这个账户的所有者。这有点像现实生活中的盖章,盖章就意味着授权。怎么验证呢,公钥与比特币账户相关联,用公钥就可以验证签名是否正确。这样就解决了比特币账户是谁的这个问题。
怎么来区分不同的交易呢?交易和区块都使用密码学上的哈希值进行索引,是不是有点耳熟,对,在比特币协议中,多处使用到了哈希函数,矿工们刚才算的数学题就是在算哈希函数。
其实比特币并不长这个样
比特币协议探究
在接下来的文章里,我将逐步介绍我是怎样手动进行一次比特币交易的。首先,我生成了一个比特币账户以及对应的公钥,私钥。接下来我发起了一笔比特币交易,我向这个新生成的账户转了一小笔比特币。期间手动签署这笔交易很困难,它花费了我很多的时间。最后,我将这笔交易发送到比特币网络,等待它被加入区块链。本文的其余部分会详细地介绍这些步骤。
事实证明,手动进行比特币交易比我想象中的更加困难。正如你所看到的,比特币的协议有些许混乱:它使用了大端格式数字(高位编址,将高序字节存储在起始地址),小端格式数字(低位编址,将低序字节存储在起始位置),固定长度数字,可变长度数字,自定义编码格式,DER编码格式以及各种加密算法。因此,仅仅是将数据转换为正确的格式就浪费了很多时间。
我遇到的第二个难题就是加密,尝试一下手动加密,你就会发现密码学对人们多不友好,甚至可以说是无情。即使你只输错了一个字节,交易就会因出错被拒绝,而且它不会告诉你哪里出错了,你只能重来。
最后,手动签署交易的过程也比想象中难得多,签署交易时每个环节都必须零失误,要么又要退回重来。
比特币地址和密钥
第一步,我创建了一个比特币地址。通常情况下,人们都是使用比特币客户端软件来创建比特币地址和与之相关的密钥。本着学习的态度,我写了一些Python代码来生成比特币地址,从而揭示地址创建的机理。
比特币使用了一系列的密钥和地址,下图解释了它们的关系。首先你要创建一个随机的256位的私钥,这个私钥用于在花费比特币时签署交易。因此,私钥必须保密,否则你的比特币可能会被盗用。
椭圆曲线数字签名算法(Elliptic Curve Digital Signature Algorithm,ECDSA,美国政府的标准,接下来我们会讨论它)会从私钥中生成一个512位的公钥,这个公钥用于验证交易的签名。但不方便的是,比特币协议中需要在这个公钥上添加了前缀04,这个公钥在交易签署之前不会被泄露,不像其它系统中公钥就是为了公之于众的。
比特币地址与公钥的关系
下一步就是生成与他人交易时使用的比特币地址了。512位的公钥太长不方便使用,因此使用SHA-256和RIPEMD哈希算法将其缩小为160位。然后使用比特币定义的Base58Check 编码将密钥编码为ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)格式。得到的地址(例如上文中的:1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa)就是你接收别人比特币时要发布的地址。需要注意的是,你无法从比特币地址中复原出公钥或私钥。如果你丢失了你的私钥(比如说你把私钥存在你的硬盘上,但硬盘丢失),你的比特币将永远丢失。
最后,钱包交换格式密钥(WIF)用于将私钥添加到你的钱包软件中,这只是将私钥进行Base58Check编码转换为ASCII格式,这一步是可逆的,而且很容易经过逆变换恢复出256位的私钥。(图中有我的私钥,我很好奇是否有人会用我的私钥去偷(通过私钥签署交易,从而转走)我那价值80美分的比特币,然而真有人那么做了,可以在
https://blockchain.info/address/1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa 看到,也算是为教学做贡献了。)
总之,共有三种密钥:私钥,公钥,公钥的哈希值,经过使用Base58Check编码,它们对外都是以ASCII格式表示。私钥是其中最重要的密钥,因为花费比特币时需要私钥签署交易,而且其他的密钥都可以从私钥中产生。公钥的哈希值就是你们刚看的的比特币地址。
我使用下面的代码片段来生成WIF格式的私钥和地址。私钥只是一个随机的256位的数字,使用椭圆曲线数字签名算法从私钥中生成公钥,公钥使用SHA-256算法,RIPEMD-160算法进行哈希计算,再经Base58编码并进行校验后得到比特币地址。最后,私钥用Base58Check编码以生成用于将私钥输入钱包软件的WIF编码。注意,这段Python随机函数代码在密码学上安全性并不高,如果你想要尝试这一步骤,建议使用更安全的钱包软件来生成比特币地址和密钥。
代码语言:javascript复制def privateKeyToWif(key_hex):
return utils.base58CheckEncode(0x80, key_hex.decode('hex')) def privateKeyToPublicKey(s):
sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1)
vk = sk.verifying_key
return ('