概述
什么是 UTXO_Compiler
UTXO_Compiler(可执行文件名 utxo_compiler)是一个面向比特币 UTXO 模型的智能合约编译器。它接受以 .ct 为扩展名的高级合约源文件,将其编译为可嵌入比特币交易锁定脚本的 BVM 字节码。
合约语言的语法脱胎于 Python——使用缩进划分代码块、def 定义函数——但加入了强制类型声明和一套编译期所有权检查机制,帮助开发者在部署前发现数据误用问题。
比特币智能合约的运作方式
UTXO 模型
与以太坊的账户模型不同,比特币采用 UTXO(Unspent Transaction Output,未花费交易输出) 模型。每一笔比特币交易消耗若干旧的 UTXO,同时产生新的 UTXO。
每个 UTXO 携带两样东西:
- 聪(satoshis):这个输出锁定了多少比特币
- 锁定脚本(locking script):一段字节码,规定了"什么条件下才能花费这笔钱"
花费 UTXO 时,发起方需要在交易输入中提供解锁脚本(unlocking script)。只有当解锁脚本与锁定脚本联合执行后结果为真,花费才被网络认可。
上一笔交易的输出 当前交易
┌──────────────────┐ ┌──────────────────────┐
│ value: 100000聪 │ ─────────▶│ 输入:解锁脚本(钥匙) │
│ 锁定脚本(密码锁) │ │ 输出:新的锁定脚本 │
└──────────────────┘ └──────────────────────┘
锁 钥匙 + 新锁智能合约就是在锁定脚本里写入自定义逻辑:不只是验证签名,还可以验证时间、数据格式、多方条件等任意可编程条件。
BVM 是什么
BVM(Bitcoin Virtual Machine)是执行锁定脚本/解锁脚本的底层虚拟机,是一台基于栈的状态机。它没有堆,没有全局变量,只有两个栈(主栈和副栈)和一组操作码。
UTXO_Compiler 将你的高级合约代码翻译成 BVM 能直接执行的操作码序列。理解 BVM 的栈模型,是读懂编译器行为和所有权系统的关键。更多内容见 比特币基础。
你可以用它写什么
.ct 合约不只是能写单个签名校验,也适合表达多种常见的 UTXO 花费条件:
| 场景 | 合约主要验证什么 |
|---|---|
| 签名与身份锁 | 签名、公钥、预期公钥哈希 |
| 哈希锁与时间锁 | 哈希原像、截止时间、退款路径和分支花费规则 |
| 多方条件 | 多公钥、阈值逻辑、承诺数据和定长参数数组 |
| 有状态 UTXO 流程 | 被花费 UTXO 中的旧状态,以及新输出中的下一状态 |
| 预言机结算 | 外部签名消息、字段解析和收款方选择 |
| 交易级约束 | 输入承诺、输出布局、手续费、退款和资产流向规则 |
这些场景共享同一个模型:合约不会修改隐藏存储;它只证明“当前这次花费是否合法”,必要时再证明“当前交易是否生成了正确的下一枚 UTXO”。
UTXO_Compiler 是如何工作的
合约文件 (.ct)
│
▼
编译
│
▼
JSON 输出
- 锁定脚本字节码
- 函数参数信息
- self.X 占位符
- 可选调试信息
│
▼
用真实数据实例化
│
▼
比特币锁定脚本
│
▼
创建并花费 UTXO从用户视角看,编译器会把带类型的 .ct 合约转换成可用于实例化锁定脚本的 JSON。编译过程中会检查语法、类型、变量所有权、函数参数,以及脚本中数据传递的形状。
导出的 JSON 是部署流程的桥梁:它告诉你要使用哪段字节码、解锁脚本需要提供哪些参数,以及哪些 self.X 值需要在上链前填入真实数据。
语言的核心设计取舍
显式类型声明
所有函数参数、结构体字段都必须声明类型。没有自动类型推断,没有 var / auto。这看起来有些繁琐,但在 BVM 这种零容错环境里,"你在代码里看到什么,栈上就发生什么"是比简洁更重要的属性。
编译期所有权检查
局部变量在被传入内置函数或赋值给其他变量后,原变量即被"消耗",之后不能再次引用。这个机制直接对应 BVM 的栈弹出语义——在底层,变量被使用就意味着栈上的值被弹走了。所有权系统让编译器在编译期就捕获这类错误,而不是等到链上执行时出错。
合约状态固化在字节码中
合约成员变量会被直接编译进字节码本身,需要用户在字节码中用不同数据替换以生成不同的锁定脚本。这是 UTXO 模型"状态即代码"特性的直接体现,与以太坊那种"合约地址 + 链上存储"的模型有根本区别。
公有函数按顺序串行执行
不以下划线开头的函数会进入 ABI,但它们会按声明顺序编译成一条连续的锁定脚本流程。互斥路径不应写成多个 public 方法;通常用一个 main(path, ...) 公有入口,在内部用私有函数分发不同路径。
单合约文件
每个 .ct 文件包含且仅包含一个合约。这与"一个 UTXO 对应一段锁定脚本"的模型天然对齐。
标准库与模板复用
合约文件可以通过 import std.p2pkh 复用标准模板,解析路径来自标准库根目录。导入的库函数会在编译期对当前合约可用,并按私有辅助函数处理,不会作为 ABI 花费入口导出。实例数据通常由宿主合约显式传参,例如 verifyP2PKH(sig, pubKey, self.pubKeyHash)。
推荐开发流程
- 编写一个
.ct文件,只定义一个Contract,并为需要读取的交易片段声明清晰的Struct。 - 用
self.X占位符表达实例数据,例如公钥哈希、截止时间、预言机公钥或协议常量。 - 使用
utxo_compiler your_contract.ct编译;需要逐步排查时打开调试输出。 - 检查导出的 JSON、字节码、函数参数和占位符。
- 用真实数据替换
self.X,实例化锁定脚本,并用该脚本创建 UTXO。 - 构造花费交易,让解锁脚本提供函数参数,同时让交易输出满足
BVM.outputsHash等约束。
如果是有状态合约,常见模式是:用 Struct 建模父交易,重建被花费的 preTXID,从 SuffixData 读取旧状态,再验证新输出延续了相同合约代码并写入了正确的新状态。
与其他方案的横向对比
UTXO_Compiler .ct | sCrypt (BSV) | Bitcoin Script(原生) | |
|---|---|---|---|
| 抽象层级 | 高级语言 | 高级语言 | 汇编级 |
| 语法风格 | Python-like | TypeScript-like | 操作码序列 |
| 类型系统 | 强类型 | 强类型 | 无类型 |
| 所有权检查 | ✓ 编译期 | ✗ | — |
| 调试工具 | 内置交互式调试器 | 插件支持 | 无 |
| 实例数据 | self.X / 后缀数据 | 构造函数 / 属性 | 手动压栈 |
| 多路径入口 | 串行流程;用 path 分发 | 多个 public method | 手写分支脚本 |
初级开发者易踩坑
- 把 UTXO 合约当成以太坊合约:UTXO 合约没有可随时读写的链上存储,状态通常写在下一枚 UTXO 的脚本里。
- 忽略
self.X的实例化步骤:编译输出只是模板,上链前还要填入真实数据。 - 把多个 public 函数当成“任选一个调用”:它们会按声明顺序连续执行。
- 只看合约代码,不看交易输出:有状态合约必须同时检查当前交易是否创建了正确的新输出。
快速回顾
- UTXO_Compiler 把
.ct合约编译成可放进交易输出的锁定脚本字节码。 - 合约不是修改隐藏存储,而是验证“这次花费是否符合条件”。
self.X表示部署时写入锁定脚本的实例数据。- 公有函数会按声明顺序串行执行;互斥路径通常用一个入口加
path参数分发。 - 写有状态合约时,要同时验证旧 UTXO 和当前交易的新输出。
下一步
- 安装 — 配置编译环境