https://bitcoin.org
比特币官网
https://bitcointalk.org
比特币知名论坛
https://www.blockchain.com/explorer
英国加密货币公司,前身是blockchain.info, 其主要产品是其加密货币钱包,交易所,区块浏览器和机构市场产品。
通过这个网站可以查询到比特币实时大盘,以及所有历史区块和其中的交易记录。除了比特币,还支持市场上流行的大多数加密货币。
https://bitinfocharts.com
加密货币数据网站,展示了各种加密货币当前的已发行货币数量、币价、市场总价、24h交易量、全网难度、全网算力(Hashrate)、挖矿收益、top富人榜、区块链账本大小,
以及项目代码的迭代信息等。还支持查询某个地址、区块、交易的详细信息。
https://www.coinwarz.com/mining/bitcoin/hashrate-chart
CoinWarz 为用户提供有关可用于采矿或交易的各种加密货币的盈利能力的信息。站内还提供了比特币全网难度、全网算力的历史变化记录图表。
它是指区块链加密货币的第一个区块,英文叫做genesis block。 通过blockchain交易所网站可以查看到创世区块详细 ,下图截取自该网站
在图中可以看到一个区块的所有详细信息,如区块hash、区块高度、区块大小(285B)和区块产生时间等。
也可以叫做数字货币钱包,它可以管理你的账户私钥,主要用途是用来接收或向他人转账比特币。数字货币已经发展多年,钱包技术经过多次迭代也发展出不同的版本, 如硬件钱包(离线钱包)、在线钱包、冷钱包/热钱包、桌面/移动钱包。需要明确的是,冷钱包/热钱包才是大分类,而硬件钱包和在线钱包、以及桌面/移动钱包只是大分类下的一种形式。 另外,还有浏览器插件钱包(属于热钱包),一般在电脑浏览器端使用。
此外,还有轻钱包(Simplified Payment Verification,SPV)的概念,它的相对概念是全节点钱包、中心化钱包。
是用于存储加密货币私钥地址的离线钱包。其功能是将私钥存储在不联网的硬件设备中,因此很安全。不联网的硬件设备可以是硬件钱包, 也可以是装有钱包软件但不联网的电子设备,如电脑、手机、U盘等。
硬件钱包
是用于存储加密资产私钥地址的专用电子设备,其功能就是将私钥存储在内部的芯片内,永远不会将设备直接联网,安全性很高。
目前市面上的多数硬件钱包在对外转账时仍旧需要通过蓝牙/USB、二维码、TF卡和NFC等方式连接到手机或电脑中的配套app。其中二维码、TF卡和NFC的连接方式更好,因为使用时它们建立的是一次性连接, 可以将攻击面降到最小。而对于蓝牙和USB,虽然连接时间短暂,但这在这段「间接联网」的过程中也是非常危险的。 硬件钱包对外转账的过程本质上是生成交易信息后再使用私钥签名,然后将交易明文和签名广播到网络中等待矿工确认。
对于收款场景,只需通过硬件钱包显示屏出示钱包地址或二维码即可,过程无需连接联网app。
目前市面上支持比特币的硬件钱包较多,这里列出几个用户较多的品牌,如Ledger、Trezor、KeepKey、OPendime、库神钱包(中国)。
和冷钱包相反,热钱包是通过联网存储的钱包。
热钱包是指互联网能够访问你私钥的钱包,热钱包往往是在线钱包的形式,使用热钱包时,最好在不同平台设置不同的密码。且开启二次认证以确保资产安全。
一般而言,普通散户用得比较多的是热钱包,因为使用方便。而大户才会用冷钱包,因为资产庞大,预防黑客入侵的风险。
使用交易所账户交易是一种常见的交易方式,而它的危险之处就在于交易所掌握了用户的私钥和助记词,用户钱包安全完全依赖交易所。 近年来交易所被攻击或一夜间卷款跑路导致大量用户损失资产的新闻不绝于耳。
安全方面,热钱包不如冷钱包,联网功能使得热钱包在安全性方面稍显逊色。但如果从便携方面考虑,联网状态下的热钱包可以随时进行资产交易,使用体验更加方便快捷。
其实就是在电脑上/手机上运行的客户端钱包,也属于在线钱包的一种。
- 主流的桌面钱包有:Bitcoin Core,Atomic Wallet,BitPay,Electrum和Exodus等;
- 主流的移动钱包有:欧易OKx、Tokenall、Blockchain、Coinbase、imtoken等。
轻钱包(SPV)是“Simplified Payment Verification”(简单支付验证)的缩写。指的是不加载全部账本的钱包,这类钱包只加载所有的block header, 然后验证某一笔交易的hash是否在其中即可验证某一笔支付是否完成。它的优点是轻量,占用空间小,但依赖网络中的其他全节点,属于去中心化钱包。
轻钱包只具有「支付验证」的功能,而没有「交易验证」的功能。「交易验证」是一个矿工节点的作用,矿工挖矿本质上是先下载整条最长链上的账本信息到本地, 然后从网络中收集交易并验证和打包它们到区块的过程,其中的验证指的就是交易验证,这个验证过程包含遍历整个账本以确认转账方确实包含交易所需的货币金额。
如上所述,包含了完整账本信息的钱包就是全节点钱包,它和矿工的区别是它不挖矿。优点是交易确认最快,也最安全。全节点钱包也是最早出现的钱包版本。
在交易所创建的在线钱包就是中心化钱包,它不依赖去中心化网络,而是通过交易所服务器接口查询账户和交易信息。
- 中心化钱包的优点是使用方便,一个账户管理多个币种,即使忘记密码也能通过注册信息找回。
- 中心化钱包的缺点是资产所有权不在用户手中,因为私钥管理在平台手中,平台随时可以限制账户的交易和提现。(不过部分交易所也将私钥告知用户,但即便如此,平台也掌握了用户私钥)
对于钱包的安全管理,一定要做好私钥或助记词的离线备份及存储,忘记它你将永远无法找回这个账户。切记助记词和私钥一定不要截图存在手机里或者在线保存在云盘中,不要复制粘贴收藏在社交平台上,也不要通过网络传输“私钥,助记词,密码。
不要随便下载来历不明的软件,特别是加密货币资产相关的,下载软件一定要通过官网跳转下载。假如你在这些途径输入了私钥,从技术上来说,很容易就能获取。
若你使用在线钱包,请尽量不要连接公共WiFi,否则也可能造成你的数据泄露。
有几种途径可以获取:
- 接受其他人的转账
- 挖矿:购买专业矿机,加入矿池,挖矿成功后获得比特币奖励。但是主流币的挖矿门槛越来越高
- 从比特币交易所购买。如火币、币安、Coinbase等
这里在 https://electrum.org/#download 下载 electrum 轻钱包进行演示,笔者电脑是macOS,Windows系统请参考此贴 。
在mac上双击.dmg安装包将app拖到应用程序目录,然后通过终端执行命令:open -n /Applications/Electrum.app --args --testnet
如图所示,点击下一步(名字不重要,可以命名为钱包1),选择标准钱包,下一步, 选择创建新的密语种子(助记词),下一步,复制密语种子到你的记事本,下一步, 粘贴密语种子通过验证,下一步,输入密码用来加密存储本地钱包(重点是助记词),即可创建钱包,然后看到下图
首先,通过钱包获取收款地址,点击【接收】,输入请求的金额,大于1就行,点击创建请求,选择右侧【地址】,右下角左键复制地址
接下来,访问免费发币的测试网 https://coinfaucet.eu/en/btc-testnet/ ,输入框右键粘贴地址,点击按钮【Get bitcoins!】
不出意外的话,你就可以看到下图所示的效果,表示交易发起成功,等待网络确认。注意,这里的金额并不是由我们决定的
等几分钟后,交易确认完成,在【历史】菜单栏下可见交易记录,左下角余额从开始的红色变成绿色,红色表示未确认, 这里显示单位是mBTC,1BTC=1000mBTC,1mBTC = 100,000 satoshi,后者是BTC最小单位
需要注意的是:该站点对IP进行限制,每12h仅能使用一次转账功能。
首先我们需要获取收款方地址,这里我们直接将BTC转回给原地址。右键【历史】中的交易记录,点击【查看交易】,如下图
图中输出标记的位置就是原转账方地址,这里是根据地址后面的金额判断出来的,显然金额18前面是我们的地址,那剩余的钱必然转回原地址。 当然,这里也可以看到右侧提示绿色标记为收款地址,根据这一点也能看出来。那么复制该地址作为我们的收款方,然后在【发送】栏中输入交易相关信息。
注意:千万不要复制【输入】框中的字符串作为收款地址,因为它并不是一个收款地址,而是一个UTXO指针(由上一笔交易ID+引用的输出索引号构成,下文会讲到)。 转账到一个无效收款地址会导致BTC损失,无法找回。
点击支付,输入密码,发起转账。几分钟后,交易被确认,下图展示笔者的两笔转出交易
其中第二笔是支付到一个无效地址,但交易仍然能被确认,通过右键该笔交易,选择【使用区块链浏览器查看】,如下图
在比特币系统中,比特币归属权完全绑定私钥,通过私钥可以计算出公钥和对外收款地址(比特币地址)。而比特币交易过程的交易验证则通过公钥+数字签名算法来进行。
用户可以通过钱包软件生成和管理自己的私钥,这些操作完全在本地,不经过网络。所以比特币的资产所有权以及去中心化信任都是通过密码学算法来实现的。
私钥
用来对账户拥有者发起的交易信息进行签名,交易信息和账户拥有者的公钥被广播至网络后,矿工使用公钥对交易信息进行验证,验证过程分两步:
一是通过验证签名来确认交易就是账户拥有者发起的交易;二是通过全账本UTXO集合找出该公钥地址确有足够比特币可以扣除。
私钥永远私密保存,不会传播至网络,一旦暴露,如同将银行卡号和密码一同泄露。
公钥
如上所述,公钥的使用者是网络中的矿工,被用于对账户拥有者发起的交易信息进行验证。
公钥会随交易信息一起传播至比特币网络,所以是可以公开的。私钥可以推出公钥,但反之则不行。
账户(比特币)地址
作为一般的对外公开收款地址,是将公钥通过多次哈希计算+Base58Check得来,通常也叫做公钥哈希地址。
注意:公钥也可以直接作为对外收款地址,不直接使用公钥的原因一般是不想暴露公钥地址。
libbitcoin-explorer 是cpp写的一款cli工具,通过它可以看到比特币中各种密钥以及交易信息再不同生命周期阶段编码的样子,对于学习和理解比特币原理有很大帮助。 下面以macOS为例进行安装演示:
# 首先需要安装brew,这里假设你已安装
# 1. 安装依赖库
$ brew install autoconf automake libtool pkgconfig wget boost zeromq
# 2. 下载安装脚本
$ wget https://raw.githubusercontent.com/libbitcoin/libbitcoin-explorer/version3/install.sh && chmod +x install.sh
# 3. 先设置好命令行http_proxy,过程需要下载文件
$ export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
# -- 注意raw.githubusercontent.com的域名劫持问题,如被解析到0.0.0.0或代理加速失效说明被劫持,需要修改dns,笔者使用的是114.114.114.114
# 4. 创建安装主目录,然后执行脚本(安装过程大概几分钟)
$ mkdir ~/libbitcoin-explorer
$ ./install.sh --prefix=~/libbitcoin-explorer/prefix --build-dir=~/libbitcoin-explorer/build \
--build-boost --build-zmq --disable-shared
# 5. 安装完成,得到可执行文件
$ lei@MacBook-Pro ~ % ./libbitcoin-explorer/prefix/bin/bx
Usage: bx COMMAND [--help]
Version: 3.7.0
Info: The bx commands are:
address-decode
address-embed
address-encode
...
# 6. 将bin目录添加到全局环境变量(可选)
sudo 修改/etc/paths(追加bin绝对路径然后新开shell窗口生效),或/etc/profile(追加 export PATH=$PATH:/path/to/libbitcoin-explorer/prefix/bin)
比特币私钥通过椭圆曲线签名算法secp256kl生成,该算法标准中定义私钥长度256bit,即32Byte。也就是说比特币系统中一共最多可以生成2256个私钥, 且对应公钥和比特币地址也有一样多。这个数字比🌏上的沙子还多。所以也不用担心别人生成和你一样的私钥地址,还把你的比特币花掉。
本质上来说,私钥就是一个随机数,即可以是0~2256之间的任意数字,算法实现一般通过调用操作系统底层随机数生成器来随机生成私钥。
私钥编码
因为私钥是一位256位整数,太长无法被人类记住。因此,比特币有一种对私钥进行编码的方式,即带校验的Base58编码,而且有非压缩和压缩两种编码格式,
也分别对应非压缩和压缩的公钥格式。由于公钥会附在交易信息中,为了减小账本空间占用,所以一般使用压缩格式公钥。
下面使用上个步骤中下载的工具来生成私钥
# 1. 生成一个256位随机大整数,默认以十六进制显示
$ lei@WilldeMacBook-Pro ~ % bx seed | bx ec-new // bx seed生成随机种子作为输入提供给ec-new命令
82e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b6 // 十六进制的64位字符
# 2. 给随机数加上网络标识前缀,0x80标识mainnet主网,0xef表示testnet测试网,这里以主网为例,加上80
8082e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b6
# 3. 添加公钥格式十六进制字符后缀:压缩公钥追加0x01,非压缩不追加,这里以压缩为例
8082e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b601
# 4. 对上个步骤结果进行两次SHA256哈希: sha256(sha256(x))
$ lei@WilldeMacBook-Pro ~ % bx sha256 8082e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b601
8ba3b4f90f95acce33661eee239ec02faf4f5b4337dad31dbd9ea6ce9e952a11
$ lei@WilldeMacBook-Pro ~ % bx sha256 8ba3b4f90f95acce33661eee239ec02faf4f5b4337dad31dbd9ea6ce9e952a11
611cef1d7ffeea5f4157cc5282c595ebd7a95bb9395bdd0605e409f9000a896c
# 5. 取上个步骤结果中前4字节,即0x611cef1d作为校验值,追加到步骤3结果的末尾
8082e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b601611cef1d
# 6. 对上个步骤结果进行base58编码
$ lei@WilldeMacBook-Pro ~ % bx base58-encode 8082e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b601611cef1d
L1c9kNdPw68cwn6WTE9wFpkYedeeQKxypGCjusYTgq4tc2oDjHJt
最后的结果L1c9kNdPw68cwn6WTE9wFpkYedeeQKxypGCjusYTgq4tc2oDjHJt
就是钱包客户端中常见的私钥格式,又称为钱包导入格式(WIF: wallet import format)。
使用非压缩格式的WIF是以5开头的字符串(目前已基本不使用),使用压缩格式的WIF是以K或L开头的字符串,这个字符串就是需要私密保存的私钥地址。
根据前述内容,我们知道比特币公钥有压缩和非压缩两种格式,对应两种格式的私钥。需要注意的是,非压缩公钥包含两部分,暂且叫做x,y(都是256位长度,共512位),
而且通过x能够推出y。首先取得上一节步骤1中的原始私钥为
8082e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b6
,下面进行演示:
# 使用原始十六进制私钥作为参数
$ lei@WilldeMacBook-Pro ~ % bx ec-to-public -u 82e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b6
04c42c3b35f18de5b6976b02eb80ef937117e064b6c77b570893ae5fba3fac41c9e1a2e1aef64038c8f222f14b6f111aedf8898956c6b3deefd345bf926c2b9ca2
# 共520位,1字节的0x04前缀表示这是非压缩公钥,之后的512位分别是x,y(各256位),即
x=c42c3b35f18de5b6976b02eb80ef937117e064b6c77b570893ae5fba3fac41c9
y=e1a2e1aef64038c8f222f14b6f111aedf8898956c6b3deefd345bf926c2b9ca2
# 同样使用原始十六进制私钥作为参数
$ lei@WilldeMacBook-Pro ~ % bx ec-to-public 82e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b6
02c42c3b35f18de5b6976b02eb80ef937117e064b6c77b570893ae5fba3fac41c9 // 264bit
上述结果为264bit(33Byte),其中1字节的前缀表示公钥中的y的正负,0x02为正,0x03为负。
账户地址通过公钥计算得来,取上文中压缩格式公钥为02c42c3b35f18de5b6976b02eb80ef937117e064b6c77b570893ae5fba3fac41c9
,计算过程如下:
# 1. 对公钥进行一次SHA256
$ lei@WilldeMacBook-Pro ~ % bx sha256 02c42c3b35f18de5b6976b02eb80ef937117e064b6c77b570893ae5fba3fac41c9
c0b0c1f4066e2886bfa5fb7844b7b123362200b0ff26f45b6522f70588aeb1c3
# 2. 对上个步骤结果进行ripemd160再次哈希
$ lei@WilldeMacBook-Pro ~ % bx ripemd160 c0b0c1f4066e2886bfa5fb7844b7b123362200b0ff26f45b6522f70588aeb1c3
c6f4a45a003d829d3696228c8d26906e33c5f50e
# 3. 对上个步骤结果添加网络标识前缀,0x00表示主网,0x6f表示测试网,这里选择主网,添加前缀后如下
00c6f4a45a003d829d3696228c8d26906e33c5f50e
# 4. 对上个步骤结果进行两次SHA256哈希
$ lei@WilldeMacBook-Pro ~ % bx sha256 00c6f4a45a003d829d3696228c8d26906e33c5f50e
e8c6bc3fa9ba524bdb49515db90ff451ffba17cb3222d7532f8f5adb0758e06d
lei@WilldeMacBook-Pro ~ % bx sha256 e8c6bc3fa9ba524bdb49515db90ff451ffba17cb3222d7532f8f5adb0758e06d
1891874ae85fccd526b1a16cb0c93ae72d82447990e8a29b206a634c7da658c5
# 5. 取上个步骤结果前4字节作为校验码,拼接到步骤3结尾,如下
00c6f4a45a003d829d3696228c8d26906e33c5f50e1891874a
# 6. 对上个步骤结果进行base58Check编码,得到账户地址
$ lei@WilldeMacBook-Pro ~ % bx base58-encode 00c6f4a45a003d829d3696228c8d26906e33c5f50e1891874a
1K8ys1NjZjL1gcc4n2KtB74fhsFxbBBDQu
如上所述,由私钥8082e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b6
最终生成的压缩公钥比特币地址是
1K8ys1NjZjL1gcc4n2KtB74fhsFxbBBDQu
,同理,使用非压缩公钥执行相同步骤会得到另一个比特币地址,这两个地址都对应同一个私钥,都是有效的。
上述步骤较为繁琐,是为了方便演示原理。实际中我们可以通过bx工具将私钥快速生成(压缩公钥对应的)比特币地址:
$ lei@WilldeMacBook-Pro ~ % echo 82e4dac3269c6ef55a3f25e5ef0265f73e25366f8cabba74d379ca1bac4398b6 |bx ec-to-public | bx ec-to-address
1K8ys1NjZjL1gcc4n2KtB74fhsFxbBBDQu // 与上面的结果一致
# 注意:对非压缩格式和压缩格式的公钥进行哈希计算得到的地址,都是以1开头的。因此,从地址本身并无法区分出使用的是压缩格式还是非压缩格式的公钥。
如A给B转账10BTC,就如同A签署一张只付给B的支票,如果想要支票生效,支票上就必须有A的亲笔签名。同时A的账户还必须有足够使用的BTC余额。在比特币系统中, 创建交易就是类似签署支票的一个数字化的过程,而交易的验证就是矿工验证交易签名和账户余额的过程,只要验证通过,交易就可以在某个时候被打包到区块中。
合法交易会被节点发送给相连的其他节点,其他节点再执行相同步骤。几秒钟后,一笔有效交易就会以指数级的扩散速度在比特币网络中传播开来,直到所有节点都收到它。
交易订单创建后,会被发送到至少一个与比特币网络相连的节点,节点会先验证交易是否合法。验证步骤主要包含:
- 交易语法和数据结果是否正确
- 交易总价值是否为0~2100万
- 交易输入是否合法
- 交易解锁脚本能否解锁引用的UTXO锁定脚本(下文介绍)
如果验证不合法,则交易被节点拒绝,不会广播到网络中。
验证合法的交易将被获得记账权的节点打包到区块中,区块会追加到节点本地的账本链条中,同时将区块广播到网络中其他节点。
注意:交易是先被第一个收到的节点验证后再广播到网络中,随后被获得记账权的节点进行打包。
其他节点收到区块后,会对区块进行验证,验证内容包括
- 记账权归属是否正确
- 所有交易是否合法
如果区块验证通过,则接入到节点本地区块链中,并广播给相连节点。
为了防止分叉和双花问题,这笔交易所在区块之后又接上一个新的区块,成为这笔交易的一次确认,当经过6次确认时,可以认为交易永久上链,不可篡改。
在比特币系统中,没有一个中心化的位置可以查询账户余额,而是基于UTXO(Unspent Transaction outputs)模型进行余额确认的。关于UTXO的解释,请参考比特币技术细节-UTXO小节 。 在这个模型中,所有交易由n个输入和n个输出构成(n>0,coinbase没有输入),一笔比特币交易的数据结构如下图
截取自 https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format
其中从上到下依次是软件版本号,交易中input的数量,交易中input明细列表,交易中output数量,output明细列表, 锁定时间(多意义字段,为0时表示立即生效;<5亿时表示区块高度,并且是该区块高度之前锁定;>5亿时表示时间戳,并且是该时刻之前锁定)。
在比特币系统中,除了挖矿奖励外,其他任何交易的输入都来自于前面某个区块中的某笔交易的某个输出,就像是无数条链将所有交易串联起来,链头节点是Coinbase。
每一笔交易都可以查到对应来源。
一笔交易的输入必须是从该用户的UTXO中创建而来。也就是说,在一笔交易中,输入是来自某一笔交易的UTXO,输出又可能作为后续某笔交易的输入,输入金额之和一定等于输出金额之和,
如此循环往复下去,使得比特币在不同私钥之间流转/交易。另外,在输出集合中,通常会包含一笔手续费,是支付给矿工作为酬劳的。
交易输出结构如下图
其中value是比特币数量,pk_script_bytes是锁定脚本大小,pk_script是锁定脚本(不定长)。锁定脚本好比是输出金额的一把锁,付款人将这笔输出金额锁住,
然后钥匙交给收款人,那就只有收款人能消费这笔金额。
交易输出的类型有两种,分别是Standard TxOut(标准交易输出)和Coinbase TxOut(挖矿交易输出)。
它是指向一个UTXO的指针,指针由是上一笔交易的哈希ID和UTXO索引号n组成(n表示交易输出的序号,序号从0开始)。想要成功消费这笔UTXO中的金额, 就需要解锁脚本来解锁UTXO中的锁定脚本,所以交易输入又包含解锁脚本,也叫签名脚本。总体结构如下图
表一中的previous_output就是引用的UTXO指针,signature script就是解锁脚本,sequence是保留字段,固定为0xffffffff。表二其实就是所引用的上一笔UTXO指针的构成(交易hash+所属输出序号)。
交易输入的类型有三种,分别是Standard TxIn、Spend Coinbase TxIn(花费挖矿交易输入)和Coinbase。
公式:手续费 = 所有输入之和 - 所有输出之和
在比特币交易中,交易费可以由用户来设置。需要注意的是,手续费与交易包含多少比特币无关,而是与交易大小(字节数)有关,也就是交易大小每一千字节所需的费用。 下图展示了区块#762953 中第2笔交易的手续费
满足上述公式。当然,任何一笔交易都会满足这个公式。
0.00825121 = 0.00800721 + 0.00024400 (1BTC = 1亿 satoshi)
相信看完上文的读者一定对这个脚本的具体内容是急不可耐的想要了解了,本节将对它进行完整剖析。
比特币交易脚本语言,叫作脚本,是一个与Forth类似的逆波兰式表示的基于堆栈的执行语言。如果听起来感觉乱七八糟,可能是因为你没有学习过20世纪60年代的编程语言。 脚本是一种非常简单的语言,只能执行限定的功能并且只能在一些特定的硬件上执行,它就像嵌入式设备,比如手持计算器,一样简单。它只要求最低的处理能力,也不能像其他现代编程语言一样可以做很多有意思的事情。在可编程货币的情境下,它其实是一种特别设计的安全特性。
比特币的脚本语言被称作基于堆栈的语言,因为它使用了一种叫作堆栈的数据结构。堆栈是一种非常简单的数据结构,你可以将它看作一堆卡片。堆栈只运行两种操作,入栈(push)和出栈(pop)。入栈是将一个项目添加到栈的顶部。出栈则从栈顶部移除一个项目。
脚本语言通过从左到右处理每个项目来执行脚本。数字(数据常量)被压入栈中。操作符将一个或多个参数压入栈中,或者从栈中移除,操作它们,并有可能将结果压入栈中。例如,OP_ADD从栈中移出两个项目,把它们相加,然后将求和结果压入栈中。
条件操作符评估一个条件,产生一个真(TRUE)或假(FALSE)的布尔结果。比如,OP_EQUAL从堆栈中移出两个项目,如果两个项目相等,则把TRUE(用数字1表示)压入栈中, 如果不相等,则压入FALSE(用数字0表示)。比特币交易脚本通常都会包含条件操作符,因此可以产生TRUE的结果来表示一个有效交易。
例如,脚本2 3 OP_ADD 5 OP_EQUAL
演示了算术加法操作符OP_ADD,将两个数相加,并把结果压入栈中;接着,条件操作符OP_EQUAL检查求和结果是否等于5。
以下是一个稍微复杂的脚本,用于计算2+7-3+1。注意,当脚本在一行中包含多个操作符时,堆栈允许上一个操作符的结果被下一个操作符使用。
2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL
这个脚本最终会让堆栈中只留下一个TRUE(1),读者可以自行计算一下。
其中比特币交易脚本用到的各类堆栈指令的用途解释参考比特币官网文档—交易操作符 。
锁定脚本(LockingScript),也叫scriptPubKey或者Pubkey Script,位于一个UTXO上,是这个UTXO的一把「锁」,锁定了这笔交易输出。通俗来讲,锁定脚本是交易时,由付款方创建的,内容包含了收款人的比特币地址信息。
解锁脚本(UnlockingScript),也叫scriptSig或者Signature Script,是一把能够解开一个UTXO锁定脚本的钥匙,位于一笔交易的一个输入中,每个输入都有一个解锁脚本,对应一个UTXO。解锁后方能使用UTXO中的金额。 既然锁定脚本中包含的收款人的比特币地址信息,那么想要解锁,也就是验证下一笔交易输入的发起人是上一笔交易输入的收款人,那就需要比特币地址信息对应的公钥和对应私钥签名。
这两个脚本,其本质上就是由多个参数和指令组成的一个脚本而已。
那么如何「解锁」呢? 很简单,矿工将解锁脚本和锁定脚本拼凑起来组成一段完整脚本,运行这个脚本,如果堆栈中的执行结果为TRUE说明则解锁成功。
下图展示了比特币常见交易类型P2PKH(对公钥哈希付款)的两个脚本的结构组成(其中省略了OP_
指令前缀)
矿工收到交易时,会根据每个交易输入的UTXO指针在UTXO内存池中找到对应的交易输出,然后执行每个交易输入和对应交易输出的拼接脚本,如果成功执行且执行结果为True, 则说明解锁脚本正确解锁该UTXO,输入有效,最后标记该UTXO为【已使用】,并从矿工本地的UTXO内存池剔除这个UTXO。
下图展示了P2PKH的的脚本具体执行过程:
执行过程的文字描述:
- 解锁脚本中的<sig>: 将本次交易发起人的签名sig入栈
- 解锁脚本中的<PubK>:将本次交易发起人的公钥即pubkey入栈(P2PKH中的这个公钥是本次交易发起人私钥对应的公钥,可作为收款地址)
- 锁定脚本中的DUP: 复制pubkey再入栈,此时栈中存在3个参数,从栈顶到栈底依次是:pubkey, pubkey, sig
- 锁定脚本中的HASH160: 弹出栈顶的pubkey并对其进行2次hash,即repemd160(sha256(pubkey)),然后将结果再入栈,此时栈中仍然3个元素
- 锁定脚本中的<PubKHash>:将锁定脚本中的pubkey的hash值入栈,此时栈中4个元素,从栈顶到栈底依次是:hash(pubkey)_2, hash160(pubkey)_1, pubkey, sig
- 锁定脚本中的EQUALVERIFY:该指令是
OP_EQUAL
和OP_VERIFY
的结合体,OP_EQUAL
的作用是将栈顶中的两个参数弹出进行对比是否相等, 若相等则入栈一个1(True),否则入栈一个0(False)。而OP_VERIFY
的作用是弹出此时的栈顶元素(1或0)进行判断,若是0直接终止运行并报错;否则继续运行。 此时栈中剩余2个元素:pubkey, sig。 - 锁定脚本中的CHECKSIG:该指令使用公钥来验证签名(签名是消费者使用自己的私钥对当前交易签名生成),签名算法是ECDSA。 具体过程是将当前交易明细数据进行哈希计算得到交易hash,然后使用公钥解密签名得到另一个哈希,对比两个哈希是否一致,一致就入栈1,否则入栈0。
笔者还找到一张P2PKH的脚本执行动态图,非常有助于理解脚本执行过程。
这个脚本验证的核心思想其实就是通过公钥和验签的方式来确定本次交易发起人就是上一笔UTXO交易的收款人,验证通过才有资格消费上一笔UTXO。
在比特币系统中,一共定义了五种类型的标准交易脚本,分别是P2PKH、P2PK、MS、P2SH和OP_RETURN,下面一一介绍。
它是Pay-to-public-key-hash的缩写,也是最常见的交易类型。这种交易类型中,锁定脚本内包含的主要内容就是收款人的公钥哈希,而解锁脚本中包含的是收款人的公钥和对应私钥签名。 解锁过程就是先验证公钥是否正确,再验证签名是否正确。P2PKH的锁定脚本格式如下:
OP_DUP OP_HASH160 <PubKey_HASH> OP_EQUALVERIFY OP_CHECKSIG
而P2PKH的解锁脚本格式如下:
<Sig> <PubKey>
Pay-to-public-key的缩写,是一种早期使用的、比P2PKH更简单的比特币交易形式。 使用这种脚本形式,公钥本身存储在锁定脚本中, 而不是像前面的P2PKH那样的公钥哈希,这要短得多。 P2PKH是Satoshi发明的,目的是使比特币地址更短,便于使用。但由于后来专家认为,在未来的量子计算中, ECDSA算法可能被破解,即存在通过公钥反推私钥的可能,那么P2PK这种公开暴露公钥的交易类型就不再安全。而SHA256则被证明是量子安全的密码算法, 在P2PKH的设计中,公钥只在消费时暴露,所以P2PKH逐渐代替了P2PK。
显然P2PKH也没有完全解决私钥可以被破解的问题。虽然用户在作为P2PKH交易的收款方时可以得到公钥哈希庇护安全,但在进行下次余额消费时, 用户公钥一定会出现在交易输入的解锁脚本中,这个时候如果本次交易没有完全将账户中的BTC转走,那么剩下的BTC就存在于风险之中。也就是说, 最佳做法是不要使用P2PK交易类型,也不要重用同一个比特币地址进行P2PKH交易。
P2PK现在最常见于coinbase交易中,由未更新为使用P2PKH的旧采矿软件生成。
P2PK的锁定脚本格式如下:
<Public Key A> OP_CHECKSIG
P2PK的解锁脚本格式如下:
<Signature from Private Key A>
Multiple-signature的缩写,译为多重签名交易。它允许多个公钥签署一笔交易,以实现多方共同管理资产的目的。MS交易的锁定脚本中记录了N个公钥, 而解锁脚本中至少有M个签名验证通过才算有效交易,这也被成为M~N交易。其验证思想是在N个公钥中,有任意M个私钥进行正确签名即验证通过。
MS的锁定脚本格式如下:
M <Public Key 1> <Public Key 2> ... <Public Key N> N OP_CHECKMULTISIG
以2~3交易为例的锁定脚本格式如下:
2 <Public Key A> <Public Key B> <Public Key C> 3 OP_CHECKMULTISIG
以2~3交易为例的解锁脚本格式如下:
OP_0 <Signature B> <Signature C>
最终组成的交易脚本如下:
OP_0 <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 OP_CHECKMULTISIG
其中开头的OP_0
其实是为了应对OP_CHECKMULTISIG
指令的一个bug而添加的。bug内容是该指令原本应该从堆栈中弹出M+N+2个参数。例如在本例中,
参数依次入栈,最后执行OP_CHECKMULTISIG
时,会先弹出3,再弹出3个参数,之后弹出2,再弹出2个参数,一共是7个参数。但实际上它却弹出了M+N+3也就是8个参数,
弹出最后一个参数时堆栈已经空了,就会报错。所以在创建解锁脚本时,会在前面随意加个参数,即OP_0
可以改为其他。
Pay-to-script-hash缩写,是2012年引入的一种强大的交易类型,它能够简化复杂交易类型的脚本程序。以MS作对比,MS的脚本会随着N的长度增加而增加, 这就会导致脚本变得很长,带来以下缺点:
- 脚本过长,付款人需要为此付出更多手续费;
- 直到这个UTXO被花费,它都会一直存在用户内存中,会占用用户过多内存空间。
P2SH的解法是:
- 将原锁定脚本进行hash,新的锁定脚本只包含旧脚本的hash值(如果这笔P2SH的UTXO一直不被消费,就永远不会暴露收款人的公钥信息);
- 花费这个UTXO时,解锁脚本中应包含原锁定脚本,也叫赎回脚本(redeem script);
- 矿工验证时,会先验证原锁定脚本的hash是否与新锁定脚本中的存储的hash值一致,再验证签名正确性。
如此,利用hash算法的固定长度特点再次巧妙解决了脚本过长占用空间的问题。
再次以2~3交易为例,则原锁定脚本RedeemScript格式如下:
2 <Public KeyA> <Public KeyB> <Public KeyC> 3 OP_CHECKMULTISIG
P2SH的新锁定脚本为:
OP_HASH160 OP_EQUAL
P2SH的解锁脚本如下(比较长):
OP_0 <signature B> <signature C> 2 <Public KeyA> <Public KeyB> <Public KeyC> 3 OP_CHECKMULTISIG
P2SH交易更重要的一个用途是可以锁定脚本的160位哈希值,然后添加一个比特币主网的前缀标识0x05,再按照base58格式编码得到一个地址,所以主网的P2SH地址都是3开头,测试网是2开头。
P2SH主网地址示例3N8MytPW2ih27LctLjn6LfLFZZb1PFSsqBr
,由RedeemScript hash生成该地址的过程参考BIP13-P2SH地址格式 。
P2SH的用途主要是多重签名地址脚本。
由于区块链本身的分布式和时间戳特性,使得它有着超越支付功能的用途。许多开发者尝试去充分利用比特币的脚本优势,将其应用在其他方面, 例如电子公证、股票认证和智能合约领域。早期的创新应用是将文件的hash值作为电子指纹存放在交易输出中,以便任何人都可以引用该交易在特定日期建立该文件的存在证明。 但是反对者们认为这样会导致账本臃肿,进一步消耗磁盘容量;而且这样的交易输出是伪UTXO,不能用于创建新交易,会使得这些UTXO一直存在用户内存中。
在Bitcoin v0.9中,社区对这些用户作出了妥协,增加OP_RETURN操作符。使用此操作符输出的记录,只能被记录在区块链上,不会存在用户的UTXO内存池中, 但会增加账本大小。 它的交易输出脚本格式如下(没有解锁脚本):
OP_RETURN <data>
data部分限制为80字节,通常表示hash,例如SHA256算法的输出(32字节)。 许多应用程序将前缀放在数据前面以帮助识别应用程序。 例如,数字公证服务使用8字节前缀"DOCPROOF",对应ASCII码的十六进制是44f4350524f4f46。
参考