智能合约与逆波兰表达式

>![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/aa79eebd-a4ba-8d2a-5822-3f59b5d48026.jpeg)

想必大家对 `1+1，3-4，5x6，8/4，(1+6)x9，1+6x7`，这些简单的数学四则运算都不陌生，大家耳熟能详的四则运算，有个高级的称呼是**中缀表达式**。

为什么是中缀，而不是前缀、后缀、上缀、下缀？
中缀因为操作符都在中间位置，那能放到后面去么？当然可以了。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/aacc5837-a222-35d9-3803-6bdf866da378.gif)

**这种把操作符置后的方式叫逆波兰表达式，即 RPN，更通俗的叫法是后缀表达式。**

中缀表达式怎么和后缀表达式对应呢？

![中缀表达式](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/e7ffe57a-25ed-dc85-8ea6-6ad8a9f1faad.jpeg)

>![后缀表达式](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/f8e444cc-4e2f-22b1-2950-0a64afb37ce6.jpeg)

后缀表达式有什么优点？

- 相较于前缀表达式更易于转换，最左边一定为运算资源。

- 不用括号，依靠运算顺序就能确定运算符的优先级，运算更简洁。

- **更符合计算机的计算方式** 。计算机通过从左至右读取后缀表达式，就可以将遇到的运算对象压入栈，在遇到运算符时就弹出运算资源，完成计算，再将结果压入栈。最后留在栈中的就是计算结果。

来点小例子，展示后缀表达式的神奇与方便。

计算 `1 2 +`，即为中缀表达式的 `1+2`。
![1 2 +](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/376237d8-179e-a10a-535f-8ba765c8419a.gif)

计算`1 2 + 4 *`，即为中缀表达式的 `(1+2)*4`，这里不需要括号哦。
![1 2 + 4 *](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/9a98076e-7948-56c4-d3da-e0da01cff76c.gif)


开始上正菜了，先从比较简单的比特币解锁脚本说起。

先看一笔交易，从地址 [1K87TMWzsQLzdZb4kUmKAcrCWnQqdVW5QJ](https://btc.com/4221da291901809dd62abe338eeebf14114d3c672bb90496c5d2c339ae89da4e) 转到
[1B3C5xnvS9ijJRiCEMcrZsBS7FerCQwp9x](https://btc.com/4221da291901809dd62abe338eeebf14114d3c672bb90496c5d2c339ae89da4e) 0.08269623 个比特币，假设前一个地址的用户叫 Neko，后一个地址的用户叫 Sara。大家看它的输出脚本。

>![输出脚本](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/3665d5f2-cb21-dc7b-dba4-6056a41f39d7.jpeg)

Niko 给 Sara 的地址打钱就写这样的脚本：`OP_DUP OP_HASH160 <Sara公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG`。

Sara 怎么花出去呢？ta 需要构造好这样的脚本`<Sara签名> <Sara私钥> OP_DUP OP_HASH160 <Sara公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG`。

Sara 构造好的脚本就是后缀表达式，怎么会呢？请看大 B 站视频。

[智能合约雏形 - 比特币脚本](https://www.bilibili.com/video/av22693233)
[智能合约雏形 - 比特币脚本](https://www.bilibili.com/video/av22693233)
[智能合约雏形 - 比特币脚本](https://www.bilibili.com/video/av22693233)

最后的输出结果是 True 时，说明 Sara 构建一个有效的交易，新的交易将被发送到网络上，转账成功。

比特币的转账，不像银行一样，打入银行账户，而是一个个的输出脚本：

```
OP_DUP OP_HASH160 <Sara公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG
OP_DUP OP_HASH160 <Niko公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG
OP_DUP OP_HASH160 <Bob公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG
OP_DUP OP_HASH160 <Alice公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG
OP_DUP OP_HASH160 <Sara公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG

# 1个地址可以有很多个输出脚本
```

比特币的地址也不像银行一样有余额的概念，一个地址有多少余额，是看这个地址有多少个输出脚本，把每个输出脚本的余额汇总而成。
**这就是 UTXO 模型，Unspent Transaction Output，未花费交易输出。**

转了这么大圈，回到以太坊智能合约上，以太坊的智能合约语言是 Solidity，跟后缀表达式的画风差异很大。我们在使用 remix.ethereum.org 把合约部署到区块链上时，部署的是**Solidity 代码编译后的结果**，是 object 的内容，而 object 与 opcodes 是一一对应的。

![Solidity 合约](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/88940124-45c4-1b96-ba96-815c2021c79f.gif)


咱可以定义`OP_DUP`为`0x76`，`OP_HASH160` 为 `0xa9`，`OP_EQUALVERIFY`为`0x88`，`OP_CHECKSIG` 为 `0xac`，那 `OP_DUP OP_HASH160 <Sara公钥的HASH160值> OP_EQUALVERIFY OP_CHECKSIG `，就转为了 `76 a9 <Sara公钥的HASH160值> 88 ac`。

而以太坊为了支持智能合约在比特币的基础上做了如下改进：

- 支持图灵完备的后缀表达式 (支持循环)，可以做更多的事情，同时也释放出了恶魔。

- 通过 Solidity 与后缀表达式转换

- 还有最重要的一点，不在使用 UTXO 模型，而是使用 Account 模型，每次执行合约时，记录当前的数据状态。

比特币验证了区块链的可行性，以太坊引入了智能合约，强烈的冲击传统的经济观念。Bitcoin 是伟大的创举，Ethereum 是天才的设计，下一个引领风骚会是谁呢？

---

参考：

https://en.bitcoin.it/wiki/Script
https://en.wikipedia.org/wiki/Reverse_Polish_notation
https://www.computerhope.com/unix/udc.htm
https://github.com/ChristopherA/Learning-Bitcoin-from-the-Command-Line/blob/master/07_2_Running_a_Bitcoin_Script.md