PalletOne调色板跨链的BTC实现
之前已经讲到了PalletOne调色板跨链以太坊ETH和ERC20的技术原理,接下来我们来讲解PalletOne跨链比特币BTC的技术原理。
一、BTC充币
假如用户A持有一定数量的比特币BTC,他希望换一些PTN,那么他可以通过BTC充币合约,将BTC兑换成等值的PBTC(PalletOne上发行的与BTC1:1兑换的映射Token),然后用PBTC即可与持有PTN的用户进行互换。BTC充币合约是运行在一个选定的陪审团上的用户合约,陪审团由4个选出的陪审员组成,每个陪审员都部署了PalletOne的比特币适配器,通过BTC-Adaptor,合约可以对比特币网络进行访问和操作。
0.选定陪审团,每个陪审员节点生成比特币的私钥、公钥
在BTC充币合约部署到PalletOne网络时,会选定4个陪审员组成陪审团,每个陪审员因为都有比特币适配器,所以可以按比特币的规则,生成私钥和公钥,并将公钥公布到PalletOne网络(记录到状态数据库)。
1.生成锁定BTC的多签地址和赎回脚本
我们规定,4个陪审员,只要有3个同意将多签合约中的BTC转移,那么该交易就可以生效,所以我们需要构建一个3/4签名的多签脚本和地址。
该多签脚本又叫做赎回脚本 Redeem Script,格式为:
3 PubKey1 PubKey2 PubKey3 PubKey4 4 CHECKMULTISIG
对该赎回脚本计算Hash,即可得到P2SH的地址: 3XXXXXXX
这个赎回脚本和多签地址是固定不变,都需要保存到PalletOne的状态数据库中。
2.用户将自己的BTC转移到多签地址进行锁定,并通过OP_RETURN备注上映射的PalletOne地址
前面我们已经得到了多签地址3XXXXXX,将BTC转移到该地址即可实现BTC的锁定,但是比特币是不支持智能合约,怎么才能做PalletOne地址的映射呢?幸好比特币有OP_RETURN功能,这个特殊的OP CODE就是为了在交易时存放额外数据用的,这额外的数据可以是比特币转账时的备注信息,可以是存证一些信息,也可以做商业上的一些特殊扩展应用。OP_RETURN后面的数据限制为40字节(或者80字节,取决与Bitcoin core的版本),PalletOne地址为20字节,进行Base58编码后,显示的字符串长度最长为35位,满足40字节的限制要求,所以我们可以直接将PalletOne地址以字符串形式放到OP_RETURN中。
【扩展方案:由于PalletOne地址是和比特币地址的生成规则类似的,所以一个合法的PalletOne地址,在去掉第一个字母P后,就是一个合法的比特币P2PKH地址,于是我们还有第二种映射方法,那就是在把BTC转移到多签锁定地址的同时,也将1聪的BTC转移到去掉第一个字母P的PalletOne地址而产生的映射地址上,那么BTC充币合约在扫描交易的时候便可读取该地址,从而得到映射地址。】
3.定时任务扫描多签地址上的交易,发起PalletOne合约放币
用户在将BTC从自己的钱包转移到多签地址的交易被打包确认后,定时任务会扫描到该笔交易,然后将该交易的Hash作为参数,发起对BTC充币合约的“放币”方法的调用,而参数就是该锁定BTC交易的Hash。如果锁定比特币的交易未被打包,或者打包后的确认数不足(小于6个),则不发起放币方法的调用。
4.PalletOne合约确认多签地址收到BTC,释放对应数量的PBTC到映射地址。
合约的内部逻辑是这样的:
- 根据传入的Hash检查StateDB,确保之前没有处理过该笔交易。
- 通过BTC-Adaptor读取到该笔交易的详细信息,获得对应的映射地址、BTC金额、接收地址等。
- 确认接收地址为多签锁定地址所收到的BTC,生成从合约付款PBTC到映射地址的Payment。
- 记录下比特币锁定的Hash,防止重放攻击。
以上步骤最终以一个Transaction的形式完成,所以不存在部分完成的可能。
二、BTC提币
如果用户已经持有了PalletOne上面的BTC等值映射Token:PBTC,现在需要兑换成BTC(也就是提币),那么应该按如下方式实现:
1.将PBTC转移到PalletOne上部署的BTC提币合约(我们命名为PC1),同时在该交易上,以比特币收币地址作为参数。
用户B通过和用户A交易,拿到了1 PBTC,如果要转换为BTC,需要先准备一个BTC的钱包,并获得自己接收BTC的地址。接下来,在PalletOne中发起一笔交易,该交易包含了以下两条主要消息:
- 将1 PBTC转移到提币合约PC1上
- 调用了PC1合约的“提币申请”方法,传入的参数为用户B的比特币钱包地址。
2.陪审团从比特币多签地址上选取适当的UTXO,构造付款到提币地址的交易。
陪审团在接收到用户B发起的请求后,会通过BTC-Adaptor查询多签锁定地址的UTXO,在获得UTXO后,还需要进行过滤。因为之前可能有其他的提币申请,而且没有被签名或者没有被打包,但是这些提币申请在PalletOne提币合约中已经记录下来,所以我们需要将这些被“预定”了的UTXO排除掉,不然可能会造成两笔不同的提币交易双花同一个UTXO的情况。过滤完UTXO后,剩下的UTXO我们通过一定的算法,找出其中满足交易提币额,而且尽量接近提币额的UTXO,这些UTXO就是比特币交易的输入。已知用户提币的金额(=打入到PC1合约的PBTC金额-提币手续费)和提币地址,就可以构造比特币交易的输出。这样未签名的比特币提币交易就完成了。当然陪审团还会把本次提币交易所使用的UTXO记录的到状态数据库中,表示已经被占用,以防新的提币交易使用同一个UTXO。
在选取UTXO时,需要注意,如果有多人发起了提币,那么可能出现UTXO被暂时占用完的情况,也就是说本次提币交易无法获得足够的UTXO来构造提币交易,那么这次提币交易就会失败,用户转到合约的PBTC也会返回到用户手中。用户只有等一段时间,让之前被预定的UTXO被签名打包了,产生了新的找零UTXO,再次发起提币交易才能成功。
3.用户或者定时任务触发提币合约的签名方法调用,陪审员对比特币交易进行签名。
如果步骤2的提币交易构造顺利,那么用户就可以再次发起“提币签名”方法的调用了。当然如果用户不着急,也可以由定时任务来发起这个合约调用。本次合约调用,实现了以下逻辑:
- 陪审员根据请求Hash查询到未签名的比特币提币交易。
- 陪审员用自己的私钥,对提币交易进行签名。
- 4个陪审员进行协商,按一定规则筛选出3个陪审员签名。
- 陪审员用3个签名,还有之前已经生成并保存好的赎回脚本,构造完整的解锁脚本: Sig1 Sig2 Sig3 RedeemScript
- 将解锁脚本和未签名交易结合,形成可被比特币网络接收的已签名交易并保存到PalletOne状态数据库中。
4.比特币广播节点监测PalletOne网络,发现有签名后的比特币交易,将该交易广播到比特币网络。
在PalletOne网络中,我们还需要部署一些比特币交易广播节点,这些节点监测PalletOne网络,发现有已签名的交易保存到状态数据库时,就会将该交易取出,然后广播到比特币网络。该节点还负责查询已签名交易的打包状态,如果已经被打包和确认,那么就可以向提币合约发起一个“交易状态更新”的方法调用,将该提币交易状态改为已打包,陪审员也会通过BTC-Adaptor确认状态是否真实,如果真实,将UTXO状态和交易和状态进行更新。
三、总结
比特币的模型是UTXO,与以太坊的Account模型不同,所以在BTC提币的时候,步骤2需要预先独占用一部分UTXO,如果某次提币操作占用的UTXO过多,剩余的可用UTXO过少,可能会导致接下来一段时间BTC提币失败。
比特币的手续费是从转出的地址中收取,而不是像以太坊一样收取合约调用者的Gas,所以在BTC提币的时候,是没有比特币广播节点的补偿的。
在地址映射上,除了使用OP_RETURN或者使用以太坊的transfer函数进行地址映射,都需要将映射地址写入到原有的链上,如果用户是一个区块链开发者,或者有高级的钱包工具,完全可以用自己比特币/以太坊的私钥对映射地址进行签名,然后将公钥、映射地址、签名这3个信息提交到跨链合约,合约是可以通过验证签名和公钥,确保映射地址的正确性。这种映射方式的好处是不需要在原有区块链上发布消息(降低了手续费),但是缺点也很明显,大部分用户只会用钱包APP进行转账操作,一般钱包APP也不提供签名、导出公钥等操作。