写点什么

揭秘比特币和区块链(五):深入理解比特币交易的脚本

  • 2017-01-11
  • 本文字数:3040 字

    阅读完需:约 10 分钟

本文作者为火币区块链研究中心技术专家廖雪峰,他有十年软件开发经验,精通 Java/Python/Ruby/Visual Basic/Objective C/Lisp 等编程语言,对开源框架有深入研究,著有《Spring 2.0 核心技术与最佳实践》一书,并有多个业余开源项目托管在 GitHub。本文中廖雪峰详细地介绍了比特币及比特币区块链的相关知识。

另外,想快速了解并掌握区块链技术的同学,可以购买极客时间出品的『深入浅出区块链』专栏。专栏立足于区块链技术,带你形成完整的区块链知识体系。作者还会手把手教你构建自己的迷你区块链,把你真正带进区块链的世界。

在比特币区块链中,每一个区块都指向上一个区块,这些通过 SHA256 计算的区块哈希链就是比特币账本不可篡改的基础。

在一个区块中,比特币系统用交易(Transaction)来表示一笔比特币交易。一个区块包含至少一笔交易。这些 Transaction 的 Hash 通过 Merkle Tree 计算出所有交易的 Merkle Hash,并被包含至区块 Hash 中,从而实现交易的不可修改。

如果我们仔细观察每一笔交易,可以发现,除了第一笔交易是矿工的挖矿所得外,每一笔交易都拥有一个或多个输入(TxIn),以及一个或多个输出(TxOut):

第一笔矿工挖矿的收入交易通常被称为 Coinbase,它没有输入,所以 TxIn 的 Hash 总是被标记为 00000000…0000

其他的交易,任何一个 TxIn 都会唯一追溯到区块链上在本区块之前的某个交易 Hash,以及索引:

通过交易 Hash 和索引(从 0 开始),即可唯一确定一个未花费的交易输出——UTXO(Unspent Transaction Output)。这样,每一个 Tx Input 都和之前的某个 Tx Output 关联了起来。

我们假设在上一笔交易中,Bob 给 Alice 支付了 0.15 个 BTC。

由于比特币并没有账户的概念,这一笔交易的输出并没有写上 Alice 的名字,也没有写上 Alice 的公钥。

那么,Alice 想要花费这 0.15 个 BTC,她应该如何证明自己拥有这个 UTXO,并且,其他人无法假冒 Alice 来花费这个 UTXO 呢?

答案是比特币的交易创建的输出其实并非一个简单的公钥地址,而是一个脚本。在 Bob 给 Alice 支付 0.15 个 BTC 的这个交易中,Bob 创建的输出脚本类似:

复制代码
OP_DUP OP_HASH160 abcd1234...9876 OP_EQUALVERIFY OP_CHECKSIG

其中,abcd1234…9876 是 Alice 的公钥 Hash。整个脚本的意思是,谁能够提供一个签名和一个公钥,让这个脚本运行通过,谁就能花费这笔交易的 1.5 个 BTC。

由于创建签名只能使用 Alice 的私钥,非 Alice 的私钥创建的签名将无法通过这个脚本的验证,所以,其他人无法假冒 Alice 来花费这笔输出。

一旦 Alice 提供了一个签名和自己的公钥,她实际上已经创建了另一笔交易来花费这个输出。

所有人都可以验证 Alice 创建的这个新交易是否有效。如果有效,该交易就会被矿工打包进新的区块,从而成为区块链上不可更改的一部分。

我们以著名的 Pizza Transaction 为例,来验证一个交易是否是有效的。

在交易 cca75078…4d79 中,唯一的 TxIn 输入提供的 sigScript 是:

复制代码
8b4830450221009908144ca6539e09512b9295c8
a27050d478fbb96f8addbc3d075544dc41328702
201aa528be2b907d316d2da068dd9eb1e23243d9
7e444d59290d2fddf25269ee0e0141042e930f39
ba62c6534ee98ed20ca98959d34aa9e057cda01c
fd422c6bab3667b76426529382c23f42b9b08d78
32d4fee1d6b437a8526e59667ce9c4e9dcebcabb

该 sigScript 实际上由两部分构成:

签名:30450221…ee0e01(71 字节 +1 字节签名类型),实际签名是去掉最后一个字节 01 的 30450221…ee0e,签名类型是 SIGHASH_ALL(0x01)。

公钥:042e930f…cabb(65 字节)

为了验证该交易是否有效,我们首先要根据 TxIn 所声明的 Previous Output Hash:a1075db5…d48d 和索引 0 找到上一笔交易的输出:

https://webbtc.com/tx/a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d

这笔交易输出的脚本是:

复制代码
1976a91446af3fb481837fadbb421727f9959c2d32a3682988ac

比特币的脚本由一系列指令和数据构成,每个指令占用一个字节,数据由数据头部的长度决定。上述二进制脚本翻译后的比特币指令如下:

复制代码
OP_DUP OP_HASH160 46af3fb481837fadbb421727f9959c2d32a36829 OP_EQUALVERIFY OP_CHECKSIG

现在,我们有了签名,公钥和脚本:

复制代码
sig: 30450221...ee0e01
pubkey: 042e930f...cabb
OP_DUP OP_HASH160 46af3fb46829 OP_EQUALVERIFY OP_CHECKSIG

就可以运行这个脚本来验证交易是否有效。

比特币脚本被设计成以栈来运行的虚拟机指令,它只有有限的几种指令,并且故意被设计成没有循环、条件跳转,所以,比特币脚本不是图灵完备的语言。

比特币脚本的执行非常简单。我们首先要准备一个空栈,然后把签名和公钥入栈:

紧接着,我们就可以执行 TxOut 的脚本:

复制代码
OP_DUP OP_HASH160 46af3fb481837fadbb421727f9959c2d32a36829 OP_EQUALVERIFY OP_CHECKSIG

首先执行 OP_DUP,这条指令把栈顶的元素复制一份,所以结果变成:

紧接着执行 OP_HASH160,它对栈顶元素计算 SHA256/RipeMD160,实际上是计算公钥 Hash,所以运行结果变成:

接下来的指令实际上是一个数据,我们直接把数据入栈:

然后,执行 OP_EQUALVERIFY,这条指令会比较栈顶的两个元素是否相等,如果不等,整个脚本就执行失败了,如果相等,脚本会继续执行,所以运行结果变成:

最后,执行指令 OP_CHECKSIG,这条指令会验证签名。首先,我们根据签名类型 SIGHASH_ALL(0x01)对整个交易进行验证。验证方法是:

把当前 Transaction 的所有 TxIn 的 scriptSig 去掉(红色部分),并把当前 TxIn 的 scriptSig 替换为 UTXO 的 script(蓝色部分),调整长度字段(绿色部分):

最后加上小端序 4 字节的签名类型 0x01(灰色部分),计算两次 SHA256,我们得到:

复制代码
c2d48f45…2669

现在,使用 ECDSA 算法对签名进行验证:

复制代码
boolean ecdsa_verify_signature(byte[] message, byte[] signature, byte[] pubkey)

根据签名的验证结果,我们即可确认该交易是否有效。

由于引入了脚本,我们可以看到,比特币实际上通过编程脚本实现了一个严格以计算机程序验证为基础的数字货币所有权的转移机制。由于计算机程序的可扩展性,比特币支付其实并不限定在必须支付给某一个公钥地址。利用脚本,我们可以构造出各种支付条件,例如,多重签名验证条件:

复制代码
2 <public-key1> <public-key2> <public-key3> 3 OP_CHECKMULTISIGN

这种提供多个公钥地址,并且需要多个签名验证的多重签名脚本,允许在 M 个签名种至少给出 N 个签名即可使用。上述脚本允许提供 3 个公钥地址中的任意两个有效签名。

当我们把比特币托管在某个第三方的在线钱包中时,就可以使用多重签名来保证只有自己和第三方钱包共同签名后才可动用输出,这样保证了黑客在攻击了第三方钱包后也无法花掉用户的比特币。

通过 OP_CHECKLOCKTIMEVERIFY,我们可以指定一个交易的锁定时间,在此之前,该交易输出无法被花掉。这个指令其实实现了支付宝的 7 天资金锁定然后再支付给卖家的功能。

还有一些交易并没有指定一个公钥 Hash,例如,这个交易的脚本如下:

复制代码
OP_HASH256 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 OP_EQUAL

它的意思是说,谁能够提供一个数据,它的 SHA256 是 6fe28c0a…0000,谁就可以花费这笔交易。

(注:该交易已经被花费了,有人找到了符合条件的数据)

从比特币的脚本,我们可以看到,基于区块链的数字货币支付实际上是数字货币所有权的安全转移。如果我们把金融资产或者实物资产以数字化的形式登记在区块链上,通过脚本就可以安全实现各种条件下的所有权转移,这正是智能合约在区块链上的应用。

出处:火币区块链研究中心。转载需注明来源!

2017-01-11 16:0217124

评论

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

Flume日志采集框架构成组件

编程江湖

flume

易用好用的云管平台看这里!行云管家杠杠的!

行云管家

云计算 企业上云 混合云 云管平台

网络安全kali渗透学习 web渗透入门 Kali系统的被动信息收集

学神来啦

技术干货 | NeCodeGen:基于 clang 的源到源转译工具

网易云信

前端 Clang

博文推荐|Apache Pulsar: 统一消息流平台

Apache Pulsar

开源 云原生 broker Apache Pulsar 消息中间件

详解 Flink 中 Time 与 Window

五分钟学大数据

flink 1月月更

前端开发之样式调试

@零度

前端开发

基于 PTS 压测轻松玩转问题诊断

阿里巴巴云原生

阿里云 云原生 压测 问题 PTS

老牌安全厂商海泰方圆加入龙蜥社区

OpenAnolis小助手

Linux 开源 社群运营

【云成本】降低云成本三大技巧你知道吗?

行云管家

云计算 企业上云 云成本

【行业云说】云数底座赋能基层治理现代化

浪潮云

云计算运维

Form 表单在数栈的应用(下):深入篇

袋鼠云数栈

前端

架构训练营 week5 课程总结

红莲疾风

「架构实战营」

研效优化实践:WeTest提效测试

WeTest

只有天空才是你的极限,我们热爱探索的过程并沉浸其中丨图数据库 TiMatch 团队访谈

PingCAP

在线XML转HTML工具

入门小站

工具

大数据开发之Flink SQL建设实时数仓实践

@零度

大数据 flink sql

web技术分享| 白板SDK之函数和方程式的运用

anyRTC开发者

前端 音视频 视频会议 白板 web技术分享

深入浅出Apache Pulsar(2):Pulsar消息机制

云智慧AIOps社区

视频智能生产及内容分析应用工具开源了!​

百度大脑

人工智能

Linux之date命令

入门小站

Linux

netty系列之:channel和channelGroup

程序那些事

Java Netty 程序那些事 1月日更

操作指南|最详尽文档翻译志愿指南

Apache Pulsar

开源 翻译 云原生 Apache Pulsar 社区

APP违法使用个人信息?不用怕,华为云VSS为你保驾护航

华为云开发者联盟

移动应用 安全 漏洞 隐私合规 华为云VSS漏洞扫描服务

Spring的底层实现机制

编程江湖

Spring JPA

带你读AI论文丨RAID2020 Cyber Threat Intelligence Modeling GCN

华为云开发者联盟

网络威胁情报 CTI 异构信息网络 GCN HINTI

Committer 郭吉伟专访:做开源不是搞慈善,用开源也不是薅羊毛

Apache Pulsar

开源 架构 云原生 中间件 Apache Pulsar

一文看懂:工程项目管理软件有哪些?怎么选?

优秀

项目管理软件

SPARK 应用如何快速应对 LOG4J 的系列安全漏洞

明哥的IT随笔

spark CDH

爆测一周!22年必看最细致代码托管工具测评

阿里云云效

阿里云 云原生 代码管理 代码托管 Codeup

2022AJAX常见面试题分享

编程江湖

ajax

揭秘比特币和区块链(五):深入理解比特币交易的脚本_语言 & 开发_廖雪峰_InfoQ精选文章