跳到主要内容

Alkanes 元协议

Alkanes 是构建在比特币之上的智能合约元协议。它让开发者可以直接在比特币的基础层上部署和运行可编程合约,不需要侧链,也不需要独立的验证者集合。frBTC、DIESEL 和 FIRE 都是 Alkanes 代币,SUBFROST 的托管逻辑正是通过 Alkanes 索引器来协调的。

它处于什么位置

Alkanes 构建在 Protorunes 之上,而 Protorunes 又在 Runes 协议的基础上扩展出了可编程执行能力。分层关系如下:

Bitcoin  →  Runes  →  Protorunes  →  Alkanes

由于一切都锚定在比特币区块上,并由比特币共识来验证,Alkanes 合约继承了比特币的安全性。合约使用 Rust 编写并编译为 WebAssembly(WASM),由索引器确定性地执行,因此每一个运行该协议的节点都会计算出相同的状态。

Protostone 与 cellpack

一笔比特币交易通过 protostone 来携带 Alkanes 指令:这是编码在交易 OP_RETURN 中的一条协议消息。一个 protostone 可以在输出之间转移代币,也可以调用某个合约。

要调用合约,protostone 会携带一个 cellpack。cellpack 是一组经过 LEB128 编码的整数列表,形式如下:

[block, tx, opcode, ...args]
  • 前两个值 [block, tx] 是你所调用合约的 AlkaneId(见下文)。
  • 接下来的值是 opcode,用来选择合约的方法。
  • 剩余的值是该方法的参数。

这取代了早期扁平的“calldata”模型:cellpack 是一个带长度前缀的变宽整数列表,而不是固定大小的字节数组。

AlkaneId

每一个 alkane 都由它被铸刻(etch)的位置来标识:

[block, tx]

也就是区块高度以及该区块内的交易索引。一些较低的范围被保留给系统合约使用。在文本中,AlkaneId 写作 block:tx

合约接口(ABI)

合约通过 MessageDispatch 枚举来声明自己的方法,并为每个方法打上 #[opcode(n)] 标签。在构建时,这会生成一个 __meta 导出项,描述该合约的方法、opcode、参数和返回类型。你可以通过 meta 视图函数从任何已部署的合约中读取这份 ABI,这是发现某个合约能做什么的标准方式。参见读取 Alkane 元数据

在一笔交易中组合多个操作

一笔交易可以携带多个按顺序执行的 protostone。一种常见的复杂交互模式是使用三个 protostone:

  1. 转移(Transfer):把合约将要消费的代币转移到它会读取的输出(以及影子输出)上。
  2. 铸造(Mint):例如在这次交互中一并铸造 DIESEL。
  3. 调用(Call):调用该合约,由它接收被转移的代币并执行相应操作。

第 1 步中被路由的代币会落在**影子输出(shadow outputs)**上,这是一种协议层面的输出,并非真实的比特币输出,但可以被协议寻址。这使得一个合约能够精确地接收它应得的资产,从而让每个合约的执行保持隔离。构建这些内容的细节见在 SUBFROST 上构建

读取合约状态

你不能直接读取合约的存储。你需要通过索引器调用它的视图函数(view functions)

  • simulate 只读地评估一次调用并返回结果。
  • trace 返回某次调用的执行轨迹。
  • 更新的 simulateprotostonessimulatetransactionsimulateblock 视图会返回完整的执行轨迹(按 protostone 划分,包含所消耗的 fuel 以及被触碰的存储),让你可以在广播之前预览一笔交易或一个区块究竟会做什么。

这些内容记录在 JSON-RPC 参考文档中。

Fuel

合约执行有一个叫做 fuel 的计算预算。就其所衡量的内容而言,它类似于以太坊上的 gas;但在最关键的一点上与 gas 不同:fuel 是免费的

不存在 fuel 价格,不存在 fuel 市场,也没有需要充值的余额。每个区块自带一份固定的 fuel 预算,而这份预算只在真正调用合约的交易之间分配:普通转账既不会分得 fuel,也不会稀释其他交易的份额。在这些调用合约的交易之间,预算按字节大小的比例分配,并设有下限,使得即使是体积很小的交易也能完成有意义的工作。因此,如果一个区块中只有一笔合约调用,它可以使用全部预算,而不是其中很小的一部分。交易未用完的 fuel 会归还给区块,供下一笔交易使用。整个过程中不会以任何资产收取任何费用。

要让一笔交易进入区块,你所支付的是普通的 Bitcoin 矿工费:以原生 BTC 计价,并按字节收费,与一笔完全不运行合约的交易完全相同。Alkanes 上的网络手续费始终是 BTC,因为它们就是 Bitcoin 的交易手续费。你不需要先获取某种单独的 gas 代币才能进行交易。

因此,决定你计算预算的杠杆是交易的字节大小,而这本来就是你用 sats 支付的部分。让一次调用在计算上更昂贵,并不会让它在发送时更昂贵。

有一点需要如实说明:如果一次调用所需的 fuel 超过了它分得的份额,它就会失败,而补救办法是发送一笔体积更大的交易,这确实会带来更高的矿工费。在这种情况下,字节确实可以换取算力。但这是个别情形而非常态,因为预算只在调用合约的交易之间分配,在目前的密度下相当充裕。

目前在主网上,每个区块的预算是 1,000,000,000 fuel,每笔交易的下限是 3,500,000。两者都是共识常量,可能在协议升级时改变。

一个操作如果耗尽了 fuel 就会失败;它不会悄悄地多收费用。视图函数运行在很高的 fuel 上限下,因此只读查询很少会触及这个限制。

协议手续费与网络手续费是两回事:目前包装与解包 BTC 收取 0.1%,交换则收取资金池手续费。它们以所涉及的资产计费,与 fuel 无关。

接下来看什么