通过 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 一起转移。