通过 Bitcoin 的 UTXO 和 Inscription 编程
Bitcoin 的 UTXO 和 Inscription 在 Rooch 中会被解析成 Move 的 Object (opens in a new tab),开发者可以通过 Object 相关的 API 来操作 Bitcoin 的 UTXO 和 Inscription。
原子化状态绑定
Rooch 通过 Move 的 Object 嵌套特效,实现 Bitcoin 状态和 Rooch L2 状态之间的原子化绑定。
比如上图中的 UTXO X 在 Move 中表达为一种 Object,它内置一个 Temporary 区域,嵌套放置在该区域的状态,在该 UTXO 被消费的时候会被清理掉。它类似于 RGB 的“一次性密封”,利用了 UTXO 只能被消费一次的特性。比如有个应用提供一种持有 Bitcoin 挖矿的特性,将用户的 Stake 信息存在该区域,一旦用户消费掉该 UTXO,则自动丢失 Stake 信息。而如果是支持 UTXO 映射追踪的协议的状态,则可以提供 Permanent Area,实现 L1 与 L2 的状态的原子化转让。
比如上图中,是通过 Move 表达的一种 Bitcoin 链上状态,比如 Ordinals Inscription(RGB 同理)。它里面的 Permanent Area 可以保存永久的状态,比如 Coin 或者 NFT。一旦该 Inscription 在 L1 被转让,Temporary Area 中的状态会被清空,而 Permanent Area 的状态会被保留,一并转让给新的所有者。
用 UTXO 编程
1. UTXO 的字段说明
UTXO(Unspent Transaction Output)是比特币系统中的一个核心概念,代表未花费的交易输出。在 Rooch Bitcoin framework 中,UTXO 结构体定义了 UTXO 的几个关键字段:
txid:address, 表示产生该 UTXO 的交易的哈希值。vout:u32, 表示该 UTXO 在其交易中的位置索引。value:u64, 表示该 UTXO 的价值,即它包含的比特币数量。seals: 表示与该 UTXO 相关联的协议铭文,使用SimpleMultiMap结构体存储,映射了协议名称(String类型)到对象 ID(ObjectID类型)。
2. 如何通过方法参数接受 UTXO
在 Rooch 中,UTXO 作为 Object 储存在全局状态中。开发者可以通过 ObjectID 获取到 Object<UTXO> 的(可变)引用。可以通过将 Object<UTXO> 的(可变)引用作为参数传递给函数。以下是一些示例:
- 借用不可变引用传递:
fun some_function(utxo: &UTXO) - 借用可变引用传递:
fun some_function(utxo: &mut UTXO) 
UTXO 对象是 MoveVM 在实时解析 Bitcoin 交易时创建和销毁,开发者无法获取其所有权,只能通过引用读取 UTXO 的状态,或者通过可变引用在 UTXO 的临时状态区储存状态。
3. 如何在 UTXO 的临时区域中存储数据
临时区域是 Object<UTXO> 的一个字段,可以用于存放一些状态。一旦 UTXO 在 Bitcoin 上被花费,MoveVM 会及时销毁 Rooch 上的UTXO 对象,其临时状态区的状态也会被清空。
代码中的 Temporary Area 是用来存储与 UTXO 相关的临时状态数据的。以下是如何在 UTXO 的临时区域中添加、修改、删除状态的接口:
- 使用 
add_temp_state函数将状态数据添加到UTXO的临时区域。该函数接受一个UTXO对象和一个要存储的状态数据。 - 使用 
contains_temp_state函数检查UTXO的临时区域是否包含特定类型的状态数据。 - 使用 
borrow_temp_state函数借用临时区域中的特定类型的状态数据的不可变引用。 - 使用 
borrow_mut_temp_state函数借用临时区域中的特定类型的状态数据的可变引用,以便修改它。 - 使用 
remove_temp_state函数从UTXO的临时区域中移除特定类型的状态数据,并获取其值。 
以下是一些示例代码:
struct TempState has store, copy, drop {
    value: u64,
}
 
// 添加临时状态数据
add_temp_state(&mut utxo, TempState{value: 10});
 
// 检查临时状态数据是否存在
if contains_temp_state::<TempState>(&utxo) {
    // ...
}
 
// 借用临时状态数据的不可变引用
let state_ref = borrow_temp_state::<TempState>(&utxo);
 
// 借用临时状态数据的可变引用并修改它
{
    let state_mut = borrow_mut_temp_state::<TempState>(&mut utxo);
    state_mut.value = 20;
};
 
// 移除临时状态数据并获取其值
let removed_state = remove_temp_state::<TempState>(&mut utxo);在上述代码中,TempState 是一个示例状态结构体,实际使用时应替换为具体的数据类型。
示例
- btc_holder_coin (opens in a new tab):一个简单的 UTXO 示例,实现了给 Bitcoin 持有者按持有时间发放代币的功能。
 - grow_bitcoin (opens in a new tab):一个更复杂的 UTXO 示例,实现了给 Bitcoin 持有者通过 DeFi farmer 模式发放代币奖励的功能。
 
用 Inscription 编程
1. Inscription 的字段说明
Inscription 结构在 ord.move 文件中定义,它代表了一个在 Rooch 网络上的有序记录(Ordered Record)。
Inscription 结构体包含了以下字段:
txid: 交易ID,类型为address。index: 索引,类型为u32。input: 交易输入索引,类型为u32。offset: 偏移量,类型为u64。body: 内容主体,类型为vector<u8>。content_encoding: 内容编码,类型为Option<String>。content_type: 内容类型,类型为Option<String>。metadata: 元数据,类型为vector<u8>。metaprotocol: 元协议,类型为Option<String>。parent: 父对象ID,类型为Option<ObjectID>。pointer: 指针,类型为Option<u64>。
TODO: 这部分文档需要改进
- 更详细的描述 Inscription 字段。
 
2. 如何通过方法参数接受 Inscription
在 Rooch 中 Inscription 以对象 Object<Inscription> 的形式储存在全局状态中。 开发者可以通过引用(&)或可变引用(&mut)的方式接受结构体作为参数,读取 Inscription 包含的数据或者在 Inscription 对象中保存临时状态或者永久状态。
例如,你可以这样定义一个方法来接受一个 Inscription 的可变引用
public entry fun plant(seed: &mut Object<Inscription>) {
    // ...
}3. 如何在 Inscription 的临时区域和永久区域中存储数据
类似于 UTXO 的临时区域,Object<Inscription> 对象也有临时区域,一旦 Object<Inscription> 被转移,其临时区域会被清空。同时 Object<Inscription> 还有永久区域,可以永久存放状态,随着 Object<Inscription> 的转移,永久区域的状态会跟着被转移。
在 plants.move 示例中,展示了如何在 Inscription 的永久区域和临时区域中存储数据:
- 永久区域:使用 
ord::add_permanent_state函数来存储数据。这些数据将一直保留在 Inscription 中,直到被显式移除。例如,种植信息和植物状态被存储在永久区域: 
ord::add_permanent_state(seed, plant);- 临时区域:使用 
ord::add_temp_state函数来存储数据。这些数据是暂时的,并且可以在某些操作后被清理。例如,种植行为被存储在临时区域: 
ord::add_temp_state(seed, actions);此外,还有对应的函数用于从这些区域中读取和移除数据:
ord::borrow_mut_permanent_state<T>用于获取永久区域中存储的特定类型数据的可变引用。ord::borrow_mut_temp_state<T>用于获取临时区域中存储的特定类型数据的可变引用。ord::remove_permanent_state<T>用于从永久区域中移除特定类型的数据。
临时区域中添加、修改、删除状态的接口:
- 使用 
add_temp_state函数将状态数据添加到Inscription的临时区域。该函数接受一个Inscription对象和一个要存储的状态数据。 - 使用 
contains_temp_state函数检查Inscription的临时区域是否包含特定类型的状态数据。 - 使用 
borrow_temp_state函数借用临时区域中的特定类型的状态数据的不可变引用。 - 使用 
borrow_mut_temp_state函数借用临时区域中的特定类型的状态数据的可变引用,以便修改它。 - 使用 
remove_temp_state函数从Inscription的临时区域中移除特定类型的状态数据,并获取其值。 
永久区域中添加、修改、删除状态的接口:
- 使用 
add_permanent_state函数将状态数据添加到Inscription的永久区域。该函数接受一个Inscription对象和一个要存储的状态数据。 - 使用 
contains_permanent_state函数检查Inscription的永久区域是否包含特定类型的状态数据。 - 使用 
borrow_permanent_state函数借用永久区域中的特定类型的状态数据的不可变引用。 - 使用 
borrow_mut_permanent_state函数借用永久区域中的特定类型的状态数据的可变引用,以便修改它。 - 使用 
remove_permanent_state函数从Inscription的永久区域中移除特定类型的状态数据,并获取其值。 
示例
- bitcoin_plants (opens in a new tab):一个简单的 Inscription 示例,实现了将 Bitcoin Inscription 作为种子,在 Layer2 上种植植物的游戏。这个植物的所有权会随着 Inscription 一起转移。