2021 04 15 Sensible 3 Fungible Token

Sensible Fungible Token 方案 - 陈诚 (cc) #

2021 Bitcoin SV 1st BootCamp - Sensible Contract (3/5)

1 2

主要介绍两部分:

  1. token的实现方案
  2. 基于token方案我们能做的扩展之一swap

一、Token实现方案 #

3

我们的token方案是基于utxo模型,逻辑全部由比特币脚本实现。因此继承了utxo的特点高并发以及零确认。同时合约的逻辑会被矿工执行验证,因此有着非常高的安全性。

7

6

我们在设计token合约时遵循两个原则:代码精简和可扩展性

保持代码精简能够减少合约出现bug的几率。

首先token要支持的功能越多,代码就会越多,从而出现bug的几率就越大。而基于utxo的合约,每一个token的utxo都是一份完整的合约。随着token的使用,其utxo的数量会增长的越来越多。因此要修改合约的bug需要更新每一份token utxo,这个代价是非常大的。此外,代码少使得编译出来的token合约size也比较小,能够节约手续费。

在保证合约代码精简的同时,我们还希望token合约能够是可扩展的。对于第三方的开发者来说,他能够无许可的在token基础上开发新的应用,这样才能支持生态的发展。

8

为了支持token合约的可扩展性,我们引进了 Pay To Contract Hash (p2ch)。一般来说,token是被私钥控制的,用户通过私钥签名对token utxo进行解锁。但是我们通p2ch,把token的控制权从用户手中移交给合约,而合约根据自己的代码逻辑来决定token的行为。这里的合约本质就是一段代码和数据的集合,理论上我们可以实现任意的逻辑在里面。

9

p2ch有点类似于btc上的pay to script hash (p2sh)。这里简单介绍下p2sh的原理。首先生成锁定脚本,然后对锁定脚本进行hash,得到一个20字节的hash数据。解锁的时候传入锁定脚本和此锁定脚本的解锁参数。矿工会先验证锁定脚本和脚本hash是否匹配,然后再执行锁定脚本里面的逻辑进行解锁。

10

p2ch也是先生成一个锁定脚本,得到20字节的锁定脚本hash,然后把token从比特币的地址上转移到这个锁定脚本的hash上,我们称为contract hash。

11

当此token utxo解锁时,首先构造一个交易,把之前生成的锁定脚本,放入到一个输出的脚本中。

12

然后再构造一个交易,把contract和token分别作为输入。

在token的逻辑中,他会去检查此交易的所有输入中是否存在一个输入,它的锁定脚本是跟自己的contract hash匹配,如果匹配,则token就可以解锁

而contract的解锁条件则是由开发者编码来决定,因此可以支持任意的逻辑。

13

这是token合约(锁定脚本)的结构,有代码段和数据段组成。中间由OP_RETURN操作符隔开。

14

数据部分组成:

  • flag:sensible
  • type:4字节unsigned int
  • Type specific data:不同的类型有不同的定义。

token的数据类型定义如下:

  • Token name,名字,20字节。
  • Token Symbol, 简称,10字节。
  • Genesis flag是一个1字节的unsigned int,若等于1表示其为genesis合约,用于创建token。等于0则为普通token合约。后面介绍genesis的作用。
  • Decimal num,是1字节的unsigned int,用于表示token amount的小数位。
  • Token address使用和bsv legacal address一样的数据格式,保持和bsv转账的一致性。同时也支持script hash。
  • Token amount是一个8字节的小端存储的unsigned int, 代表token的数量。
  • TokenID 长度为36字节,tokenGenesis的outputpoint。

15

token合约里面实现了两个功能。转账和从合约解锁。

16

转账的逻辑需要检查输入的数量和输出的数量是否相等。

为了能够检查数量,我们需要对输入和输出进行遍历。代码实现上需要循环遍历所有的输入输出。但是在bitcoin的脚本里面,遍历是比较特殊的,由于不存在jump的指令,loop在脚本里面会展开,这就意味着:

  1. 你需要在合约里面就指定好你的loop的次数。
  2. 你支持的loop次数越多,展开后的代码越大,你的合约就越大,手续费就越多。

17

这是token合约里面用到的某个合约的例子。因为逻辑主要都在loop里面,因此size跟输入输出数量是一个线性的关系。

因此如果要支持更大的输入输出,合约就会变大,转账就会付出更多的手续费 但是大规模的输入输出只有很少的情况下才会用到,这会是的平时在使用较少的输入输出时也需要付出很大一笔手续费,对用户来说有点不太合理。

18

19

20

21

为了解决上面的问题,我们引入了合约拆分的技术。将数量检查这个功能独立出来做成合约。并且Token能够支持多种输入输出的组合。使用对contract hash检查的技术,能够在较少的代价下支持多种组合。

22

23

24

转账交易时,先根据具体的输入和输出token的数量,选择合适的数据检查合约,然后加入到转账交易的输入中。输入的token合约会检查此交易的输入中是否存在数量检查的合约,如果不存在,则无法解锁。

25

26

合约解锁功能也需要对输入输出的数量进行检查,检查的方法和转账类似。Token合约会在输入中检查是否存在数量检查的合约,同时还会检查是否有匹配自己的Contract hash的合约存在,如果不存在则无法解锁。

27

token合约在发行时能够选择支持增发和禁止增发。我们引入了TokenGenesis合约用来做token的增发。

28

如果发行者需要保留增发的能力,在第一次发行token的时候,在交易的输出中加入新的tokenGenesis合约。此合约解锁时需要发行者的私钥进行签名,因此只有发行者能够进行增发。

29

如果需要禁止增发,则在发行token的时候选择不输出tokenGenesis。由于tokenGenesis合约会对自己做溯源检查,因此一旦发行者选择不输出tokenGenesish合约,后面再也无法对token进行增发,能够在程序逻辑上保证发行者无法增发。

二、基于token的扩展:swap #

30

第二部分主要介绍下我们基于之前提到的token方案,在上面做的扩展开发,实现了一个类似uniswap功能的swap合约。此合约支持四种功能。

31

32

33

34

上面是测试网上部署的合约四种功能的交易。

35

同样的,我们根据这四种功能对合约进行了拆分,除了一个主合约swapMain,还有分别实现四种功能的合约。每当需要调用某个功能时,就生成对应功能的合约。

36

37

42

增加流动性的交易会需要先生成addLiquidity和amountCheck的合约,然后根据增加的流动性增发出一定数量的lp token。而转入到交换池里面的合约是用fetchToken合约控制的。

43

FetchToken合约的解锁条件是只要输入中有三种合约中的任意一中即可。

45

46

52

提取流动性的交易构成如上所示。

54

55

56

这是使用bsv换取token合约的功能。

58

59

60

这是token换取bsv的交易。

61

以上的swap合约只是token扩展的一个实例,我们还可以使用这种技术实现更多的基于token的扩展。