Bounty #293 — 对 Compact Standard Library 中约 30 个导出的全面介绍
语言版本: Compact 0.23+ | 编译器: 0.31.0 | 运行时: 0.16.0
- 概述
- 通用类型:Maybe 与 Either
- Merkle 树相关
- 椭圆曲线
- 内核类型:ContractAddress 与 UserAddress
- 加密和哈希
- 铸币/代币操作
- Coin/Box 操作
- 余额与区块时间
- 板载注册
Compact Standard Library (CompactStandardLibrary) 是 Midnight 区块链智能合约语言 Compact 的内置标准库。在使用 Compact 编写合约时,通过 import CompactStandardLibrary; 即可访问所有导出,无需额外安装依赖。
标准库提供了约 30 个导出,涵盖代数数据类型(Maybe、Either)、Merkle 证明验证、椭圆曲线运算、哈希与承诺原语、代币铸币与转账、以及区块时间函数等核心功能。本文档按类别逐一介绍每个导出,并提供可直接参考的 Compact 代码示例。
所有示例代码文件位于 contracts/ 目录下。
Maybe<T> 和 Either<A, B> 是 Compact 中最基本的代数数据类型,广泛用于表示可选值和二选一的场景。
封装一个可能存在或不存在的值。isSome 为 true 时 value 有效,否则 value 应视为 default<T>。
struct Maybe<T> {
isSome: Boolean;
value: T;
}
构造 Maybe<T> 的两个工厂函数。
circuit some<T>(value: T): Maybe<T>;
circuit none<T>(): Maybe<T>;
示例: 根据条件选择性地返回值。
export circuit demoMaybe(value: Uint<64>): Maybe<Uint<64>> {
if value > 0 {
return some(value);
} else {
return none<Uint<64>>();
}
}
不相交并集(disjoint union),表示要么是类型 A 的值,要么是类型 B 的值。
struct Either<A, B> {
isLeft: Boolean;
left: A;
right: B;
}
构造 Either<A, B> 的两个工厂函数。
circuit left<A, B>(value: A): Either<A, B>;
circuit right<A, B>(value: B): Either<A, B>;
示例: 在合约地址和用户地址间选择。
export circuit demoEither(isContract: Boolean): Either<ContractAddress, UserAddress> {
if isContract {
return left<ContractAddress, UserAddress>(kernel.self());
} else {
return right<ContractAddress, UserAddress>(kernel.selfUserAddress());
}
}
完整示例文件: contracts/generic-types.compact
Compact 内建对 Merkle 树路径验证的支持,适用于零知识证明中证明某个叶子属于某棵树而不泄露具体位置。
Merkle 树的根哈希值,由单个 Field 表示。
struct MerkleTreeDigest { field: Field; }
Merkle 路径中的一个条目,包含兄弟节点的哈希和方向指示。
struct MerkleTreePathEntry {
sibling: MerkleTreeDigest;
goesLeft: Boolean;
}
深度为 n 的 Merkle 树路径,指向类型为 T 的叶子。可以通过见证(witness)从编译器输出构造。
struct MerkleTreePath<#n, T> {
leaf: T;
path: Vector<n, MerkleTreePathEntry>;
}
从 MerkleTreePath 推导出 Merkle 树根,用于验证叶子是否属于某棵树。
circuit merkleTreePathRoot<#n, T>(path: MerkleTreePath<n, T>): MerkleTreeDigest;
merkleTreePathRoot 的变体,假设叶子已在外部哈希过。
circuit merkleTreePathRootNoLeafHash<#n>(path: MerkleTreePath<n, Bytes<32>>): MerkleTreeDigest;
示例: 验证 Merkle 证明。
export circuit verifyMerkleProof(
path: MerkleTreePath<8, Bytes<32>>,
expectedRoot: MerkleTreeDigest
): Boolean {
const computedRoot = merkleTreePathRoot(path);
return computedRoot.field == expectedRoot.field;
}
完整示例文件: contracts/merkle.compact
Compact 提供对底层证明系统内嵌椭圆曲线的原生支持,用于密码学运算。
仿射坐标下的椭圆曲线点。
struct NativePoint { x: Field; y: Field; }
椭圆曲线加法(两个 NativePoint 相加)。
circuit ecAdd(a: NativePoint, b: NativePoint): NativePoint;
椭圆曲线标量乘法(NativePoint 乘以标量 Field)。
circuit ecMul(a: NativePoint, b: Field): NativePoint;
主生成元与标量的乘法。
circuit ecMulGenerator(b: Field): NativePoint;
将任意类型映射到 NativePoint。输出相对于群基点和任何其他输出具有未知离散对数。
circuit hashToCurve<T>(value: T): NativePoint;
完整示例文件: contracts/elliptic-curve.compact
合约的地址,封装在 Bytes<32> 中。通过 kernel.self() 获取当前合约地址。
struct ContractAddress { bytes: Bytes<32>; }
用户的公钥地址,封装在 Bytes<32> 中。通过 kernel.selfUserAddress() 获取当前调用者的用户地址。
struct UserAddress { bytes: Bytes<32>; }
示例:
export circuit demoContractIdentity(): ContractAddress {
return kernel.self();
}
export circuit demoUserIdentity(): UserAddress {
return kernel.selfUserAddress();
}
完整示例文件: contracts/address-types.compact
Compact 提供两套哈希/承诺原语:瞬态(电路高效,不保证跨版本持久)和持久(基于 SHA-256,保证一致性)。
电路高效的哈希压缩函数,将任意值映射到 Field。适用于一致性检查,不应用于导出状态数据。
circuit transientHash<T>(value: T): Field;
电路高效的承诺函数。与 transientHash 不同,此函数在随机数足够随机的情况下足以保护输入不被泄露。
circuit transientCommit<T>(value: T, rand: Field): Field;
基于 SHA-256 的持久哈希函数,返回 Bytes<32>。保证跨版本持久,应 用于派生状态数据。
circuit persistentHash<T>(value: T): Bytes<32>;
基于 SHA-256 的持久承诺函数。
circuit persistentCommit<T>(value: T, rand: Bytes<32>): Bytes<32>;
将 persistentHash 或 persistentCommit 的 Bytes<32> 输出降级为 Field,以便在瞬态计算中使用。
circuit degradeToTransient(x: Bytes<32>): Field;
将 Field 升级为 Bytes<32>,以便在持久计算中使用。
circuit upgradeFromTransient(x: Field): Bytes<32>;
完整示例文件: contracts/crypto-hash.compact
返回原生代币的 token type(颜色)。
circuit nativeToken(): Bytes<32>;
将域分隔符转换为全局命名空间的代币类型。一个合约无法为另一个合约的域分隔符铸币。
circuit tokenType(domainSep: Bytes<32>, contract: ContractAddress): Bytes<32>;
铸造非屏蔽代币并发送给接收方,返回 coin color。需要公开披露域分隔符和金额。
export circuit mintUnshieldedToken(
domainSep: Bytes<32>,
value: Uint<64>,
recipient: Either<ContractAddress, UserAddress>
): Bytes<32>;
铸造屏蔽代币并发送给接收方,返回 ShieldedCoinInfo。需要唯一的 nonce。
circuit mintShieldedToken(
domainSep: Bytes<32>,
value: Uint<64>,
nonce: Bytes<32>,
recipient: Either<ZswapCoinPublicKey, ContractAddress>
): ShieldedCoinInfo;
完整示例文件: contracts/minting.compact
ShieldedCoinInfo:新创建的屏蔽代币描述{ nonce, color, value }QualifiedShieldedCoinInfo:账本中已存在的可花费屏蔽代币{ nonce, color, value, mtIndex }ShieldedSendResult:sendShielded的输出{ change, sent }ZswapCoinPublicKey:用户接收屏蔽代币的公钥{ bytes }
receiveShielded 接收一个屏蔽代币并添加验证条件,确保该代币作为本次交易的输出存在。sendShielded 从账本中已有的屏蔽代币发送指定金额,并返回找零(ShieldedSendResult)。sendImmediateShielded 用于发送在同一交易中刚创建的代币,无需账本索引。
| 函数 | 用途 |
|---|---|
receiveShielded |
接收屏蔽代币,添加验证条件 |
sendShielded |
从账本代币发送(返回找零) |
sendImmediateShielded |
从交易内代币发送 |
非屏蔽代币(unshielded token)的交易信息公开可见。sendUnshielded 发送指定金额的代币到合约或用户地址,不返回找零,因此需要精确匹配金额。receiveUnshielded 在当前合约中接收指定金额和类型的非屏蔽代币。
| 函数 | 用途 |
|---|---|
sendUnshielded |
发送非屏蔽代币(不返回找零) |
receiveUnshielded |
接收非屏蔽代币 |
evolveNonce 从计数器索引和之前的 nonce 值可确定性地派生出新的 ShieldedCoinInfo nonce,适合在批量铸币场景中按序生成唯一 nonce。shieldedBurnAddress 返回一个特殊的支付地址,任何发送到该地址的屏蔽代币都将被永久销毁,无法再被花费。
当合约拥有多个代币时,mergeCoin 可以将两个账本上已存在的代币合并为一个新代币,mergeCoinImmediate 则允许将一个账本代币和一个当前交易中新创建的代币合并。合并有助于减少状态大小和降低后续操作的成本。
ownPublicKey 返回发起当前交易的终端用户的 ZswapCoinPublicKey,可用于向自己发送屏蔽代币。createZswapInput 和 createZswapOutput 是底层的 Zswap 输入/输出创建接口,通常不建议手动调用,而应通过 sendShielded、sendImmediateShielded 和 receiveShielded 等高级别函数来操作。
完整示例文件: contracts/coin-operations.compact, contracts/unshielded-operations.compact, contracts/merge-operations.compact
返回合约对于某代币类型的非屏蔽余额。注意合约执行期间余额不更新,精确匹配可能导致交易失败。
circuit unshieldedBalance(color: Bytes<32>): Uint<128>;
推荐使用比较函数避免精确匹配问题:
unshieldedBalanceLt(color, amount)— 余额小于unshieldedBalanceGte(color, amount)— 余额大于等于unshieldedBalanceGt(color, amount)— 余额大于unshieldedBalanceLte(color, amount)— 余额小于等于
示例:
export circuit hasSufficientBalance(
color: Bytes<32>,
required: Uint<128>
): Boolean {
return unshieldedBalanceGte(color, required);
}
基于区块时间的合约逻辑控制函数:
blockTimeLt(time)— 当前时间小于给定时间blockTimeGte(time)— 当前时间大于等于给定时间blockTimeGt(time)— 当前时间大于给定时间blockTimeLte(time)— 当前时间小于等于给定时间
示例: 注册窗口和时间锁提现。
export circuit isRegistrationOpen(deadline: Uint<64>): Boolean {
return blockTimeLt(deadline);
}
export circuit isWithdrawalAvailable(unlockTime: Uint<64>): Boolean {
return blockTimeGte(unlockTime);
}
完整示例文件: contracts/onboard-registration.compact
~/Work/bounties/293-compact-stdlib/
├── README.md # 本篇教程
├── contracts/
│ ├── generic-types.compact # 通用类型:Maybe, Either
│ ├── merkle.compact # Merkle 树相关
│ ├── elliptic-curve.compact # 椭圆曲线
│ ├── address-types.compact # 地址类型
│ ├── crypto-hash.compact # 加密和哈希
│ ├── minting.compact # 铸币/代币操作
│ ├── coin-operations.compact # Coin 操作(屏蔽)
│ ├── unshielded-operations.compact # Coin 操作(非屏蔽)
│ ├── merge-operations.compact # 合并操作
│ └── onboard-registration.compact # 板载注册
└── src/ # (预留)TypeScript 集成代码
- Midnight 官方文档
- Compact 标准库 API 参考
- Compact 语言参考
- 参考项目:
~/Work/unshielded-token-dapp/