前言
水龙头是什么
水龙头这个名字总会让我想起生活中把水龙头开关拧到无限接近但又不达到关闭状态的大妈们,这样可以让水一滴一滴缓慢滴出,但又不会触发水表计费,彰显出她们丰富的生活经验和生存智慧。
水龙头是赠送小额比特币的服务。
为了让用户可以快速尝试Bitcoin SV网络,会有人搭建水龙头服务,给用户赠送给小额比特币,这样用户就可以用这些币尝试使用Bitcoin SV网络,如:转账、测试、写入数据等。
——wiki.bsv.info
合约需求
本文介绍如何通过智能合约直接在链上提供水龙头服务。该服务满足如下需求:
- 任何人都可以向合约中充值。
- 每隔一段时间,任何人都可以从合约中提取一定额度的BSV。
完整的代码已经合入了sCrypt的样板项目中。
准备知识
阅读本文前需要先了解OP_PUSH_TX的相关知识,推荐阅读如下文章:
代码分析
总体结构
该合约共有三个部分:
- 充值合约
FaucetDeposit
- 提现合约
FaucetWithdraw
- 水龙头合约
Faucet
对外可见的是水龙头合约,其他两个合约不可见,只是帮助水龙头合约实现具体功能。顾名思义,充值合约实现充值功能,提现合约实现提现功能。虽然定义了多个合约,但最终编译出来的脚本还是放在一个UTXO中。这种多合约模式的使用方法和特点,可以参见sCrypt的说明文档多合约。
水龙头合约对外提供两个函数:
- 充值
deposit
- 提现
drop
充值功能分析
函数参数
SigHashPreimage preImage
:当前tx的签名哈希原像。如果你不知道这个参数的含义,请阅读文章开始部分推荐的文章。int depositAmount
:充值聪数。Ripemd160 changePKH
:找零用的公钥Hash。int changeAmount
: 找零聪数。
参数检查
require(Tx.checkPreimage(preImage));
require(depositAmount > 0);
对参数进行取值范围的检查。
因为sCrypt目前还不支持unsigned int
类型,所以需要检查depositAmount
参数是正数,避免出现利用充值函数从合约中取走币的漏洞。
构造合约输出
合约规定充值tx最多会有两个有先后顺序的输出:
- 充值后的合约
- 找零(可选)
bytes output0 = this.composeOutput0(preImage, depositAmount