Liquid的Covenants:处理比特币脚本中的金额

1. 引言

Covenants契约是一种允许introspection自省的结构:

  • 交易output可以对花费其的交易施加条件(超出特定的"必须提供自身的有效签名和特定的公钥")。

Rusty Russell 之前研究过Covenants: Examining ScriptPubkeys in Bitcoin Script,但契约想要强化的另一个有用的事情是金额。很容易实现相等,但需考虑允许合并输入的情况:

  • 也许第一个output必须是第一个和第二个输入的总和。

问题是比特币脚本处理的是带符号的补码值,而 31 位限制了最多 21.47483648 个比特币。但是,使用OP_MULTISHA256OP_CAT,可以处理全额。

2. 令人烦恼的金额问题

使用OP_TXHASH,可在stack上获得 SHA256(输入金额)SHA256(输出金额)。由于这涉及哈希,因此除了相等之外,无法评估数字,因此在没有完全完整契约Covenants in Bitcoin: A Useful Review Taxonomy的其他情况下:

  • 需要让用户提供实际值到witness stack上,
  • 然后测试这些值来确定想要的条件,
  • 然后确保它们与OP_TXHASH交易中的内容相符。

Rusty Russell通常反对这种向后的形式(只需给stack上的值!),但无论如何都无法原生使用源自 OP_TX 的64 位值(建议push两个值,尽管其有点ugly)。

3. 比特币脚本可以处理的价值形式

2100万个 BTC 略低于 2^51 聪。

将这些位分成一组堆栈值:

  • lower:低24位
  • upper:高位(27,但可最多允许到 31)

将此元组称为"脚本友好对"(Script-friendly pair, SFP) 形式。请注意,stack上的所有脚本数字均以little-endian表示,并带有符号位(最后一个字节为 0x80)。不幸的是,这是一种令人讨厌的格式。

4. 将SFP转换为 8 字节 Little-Endian 值

下面的代码采用positive CScriptNum,并生成2个stack值,可以将这2个stack连接起来形成一个 4 字节无符号值:

# !UNTESTED CODE!
# Stack (top to bottom): lower, upper
OP_SWAP

# Generate required prefix to append to stack value to make it 4 bytes long.
OP_SIZE
OP_DUP
OP_NOTIF
	# 0 -> 00000000
	OP_DROP
	4 OP_PUSHDATA1 0x00 0x00 0x00 0x00
OP_ELSE
	OP_DUP
	1 OP_EQUAL OP_IF
		# Single byte: prepend 0x00 0x00 0x00
		OP_DROP
		3 OP_PUSHDATA1 0x00 0x00 0x00
	OP_ELSE
		OP_DUP
		2 OP_EQUAL OP_IF
		# Two bytes: prepend 0x00 0x00
		2 OP_PUSHDATA1 0x00 0x00
		OP_ELSE
			3 OP_EQUAL OP_IF
				# Three bytes: prepend 0x00
				1 OP_PUSHDATA1 0x00
			OP_ELSE
				# Prepend nothing.
				0
			OP_ENDIF
		OP_ENDIF
	OP_ENDIF
OP_ENDIF
OP_SWAP

# Stack (top to bottom): upper, pad, lower

这46个字节处理upper。现在lower是 0 到 16777215 之间的 CScriptNum,想要生成两个stack值,可将它们连接起来形成一个 3 字节无符号值。此时必须删除四字节情况下的零填充:

# !UNTESTED CODE!
# Stack (top to bottom): upper, pad, lower
OP_ROT

# Generate required prefix to append to stack value to make it 3 bytes long.
OP_SIZE
OP_DUP
OP_NOTIF
	# 0 -> 000000
	OP_DROP
	3 OP_PUSHDATA1 0x00 0x00 0x00
OP_ELSE
	OP_DUP
	1 OP_EQUAL OP_IF
		# Single byte: prepend 0x00 0x00
		OP_DROP
		2 OP_PUSHDATA1 0x00 0x00
	OP_ELSE
		OP_DUP
		2 OP_EQUAL OP_IF
		# Two bytes.  Now maybe final byte is 0x00 simply so it doesn't
		# appear negative, but we don't care.
		1 OP_PUSHDATA1 0x00
		OP_ELSE
			# Three bytes: empty append below
			3 OP_EQUAL OP_NOTIF
				# Four bytes, e.g. 0xff 0xff 0xff 0x00 
				# Convert to three byte version: negate and add 2^23
				# => 0xff 0xff 0xff 
				OP_NEG
				4 OP_PUSHDATA1 0x00 0x00 0x80 0x00 
				OP_ADD
			OP_ENDIF
			# Prepend nothing.
			0
		OP_ENDIF
	OP_ENDIF
OP_ENDIF

OP_SWAP
# Stack (top to bottom): lower, pad, upper, pad

可稍微优化这 47 个字节。

现在使用OP_MULTISHA256(或OP_CAT 3 次 和OP_SHA256) 将它们连接起来形成一个 8 字节的little-endian数,以便与OP_TXHASH所使用的格式进行比较。

基本上,95 个字节用于将元组与哈希值进行比较。

5. 添加两个SFP

编写一些代码来添加两个格式良好的SFP!

# !UNTESTED CODE!
# Stack (top to bottom): a_lower, a_upper, b_lower, b_upper
OP_ROT

OP_ADD
OP_DUP
4 OP_PUSHDATA1 0x00 0x00 0x00 0x01
OP_GREATERTHANOREQUAL
OP_IF
	# lower overflow, bump upper.
	# FIXME: We can OP_TUCK this constant above!
	4 OP_PUSHDATA1 0x00 0x00 0x00 0x01
	OP_SUB
	OP_SWAP
	OP_1ADD
OP_ELSE
	OP_SWAP
OP_ENDIF

# Stack now: a_upper(w/carry), lower_sum, b_upper.
OP_ROT
OP_ADD
OP_SWAP
# Stack now: lower_sum, upper_sum

请注意,这 26 个字节不会检查upper是否溢出:若处理已验证的金额,甚至可以在可能之前添加 16 次(当然,对于不同的金额,这是不可能的)。不过,可在最后的OP_SWAP前,添加OP_DUP 0 OP_GREATERTHANOREQUAL OP_VERIFY

6. 检查SFP

上面的代码假设格式良好的pairs,但由于这些pairs源自witness stack,因此需要有一个routine来检查某pair是否格式良好:

# !UNTESTED CODE!
# Stack: lower, upper
OP_DUP
# lower must be 0 - 0xFFFFFF inclusive
0
4 OP_PUSHDATA1 0xFF 0xFF 0xFF 0x00
OP_WITHIN
OP_VERIFY

OP_OVER
# upper must be 0 - 0x7FFFFFF inclusive
0
4 OP_PUSHDATA1 0xFF 0xFF 0xFF 0x07
OP_WITHIN
OP_VERIFY

这确保了范围都在规格范围内:没有负数,没有巨大的数字。

7. 总结

虽然这表明OP_CAT/OP_MULTISHA256足以处理脚本中的比特币金额,但其大小(约 250 字节以验证两个输入等于一个输出)为优化提供了相当引人注目的案例。

值得注意的是,这就是 Liquid 选择在比特币脚本中添加以下 64 位操作码的原因:

  • OP_ADD64 ,
  • OP_SUB64,
  • OP_MUL64,
  • OP_DIV64,
  • OP_NEG64,
  • OP_LESSTHAN64,
  • OP_LESSTHANOREQUAL64,
  • OP_GREATERTHAN64,
  • OP_GREATERTHANOREQUAL64。

【Liquid团队还重新启用了bitwise操作码(OP_XOR等)以使其能够很好地工作。还实现了OP_SCRIPTNUMTOLE64、OP_LE64TOSCRIPTNUM和OP_LE32TOLE64进行转换。】

在Rusty Russell之前的文章中,建议OP_LESS适用于任意值,但不适用于这些值,因为endian字节序是错误的!至少,需要添加OP_LESSTHAN64、OP_ADD64和OP_NEG64来允许 64 位比较、加法和减法。

但是,仅使用OP_CAT或OP_MULTISHA256,就可以处理金额。只是不够优美!

参考资料

[1] Blockstream团队 Rusty Russell 2023年10月22日博客 Covenants: Dealing with Amounts in Bitcoin Script

相关推荐
区块链文博10 天前
比特币国家与企业机构“战略储备”潮起
大数据·人工智能·web3·区块链·比特币
Daniel_1872 个月前
区块链技术与应用-PKU 学习笔记
区块链·以太坊·比特币
编程老船长2 个月前
第4章 揭秘区块链:从创世区块到链式结构
算法·区块链·比特币
我是前端小学生3 个月前
手把手教你实现HD钱包助记词生成算法
比特币
Mindfulness code4 个月前
比特币详解
区块链·比特币
Z3r4y5 个月前
【区块链】浅谈面向小白的关于BlockChain那些事
区块链·智能合约·it·以太坊·比特币
我想学LINUX5 个月前
【常见开源库的二次开发】基于openssl的加密与解密——SHA算法源码解析(六)
算法·开源·openssl·比特币·sha-1·sha-2·比特币挖矿
我想学LINUX5 个月前
【常见开源库的二次开发】基于openssl的加密与解密——Base58比特币钱包地址——算法分析(三)
算法·嵌入式·加密·openssl·比特币·base58·密钥交换
林冠宏_指尖下的幽灵7 个月前
Ton 区块链的官方 类ERC20-Token 智能合约代码-Transfer部分解析
区块链·以太坊·比特币
二当家的素材网7 个月前
零基础10 天入门 Web3之第3天
web3·密码学·以太坊·比特币·加密货币·区块链技术