Skip to content

Commit

Permalink
add doc to describe object dynamic fields
Browse files Browse the repository at this point in the history
  • Loading branch information
pause125 committed May 28, 2024
1 parent 13318e5 commit 2a77324
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 11 deletions.
71 changes: 66 additions & 5 deletions docs/website/pages/learn/core-concepts/objects/object.en-US.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ module moveos_std::object {

#### Shared and Frozen Object

`Object<T>` has two states, `shared` and `frozen`.
SystemOwnedObject `Object<T>` has two states, `shared` and `frozen`.

* `SharedObject`: Everyone can directly get the `&mut Object<T>` reference.
* `FrozenObject`: No one can get the `&mut Object<T>` reference, even the module where `T` is located.
Expand Down Expand Up @@ -180,6 +180,27 @@ module moveos_std::object {

Deleting an Object will return the encapsulated data in the Object, this method can only be called by the module where `T` is located.

In summary, different users have different permissions for operations on Objects in different states. The following are the operations that contract developers and normal users can perform on different types of Objects using the methods provided by `moveos_std::object`:

- Contract developers

| object | owner | value abilities | transfer | borrow mut | take value | remove |
|---------|-------------------|-----------------|----------|------------|------------|--------|
| shared | SystemOwnedObject | not required | × || × | × |
| frozen | SystemOwnedObject | not required | × | × | × | × |
| public | UserOwnedObject | key, store |||||
| private | UserOwnedObject | key |||||

- Normal users

| object | owner | value abilities | transfer | borrow mut | take value | remove |
|---------|-------------------|-----------------|----------|------------|------------|--------|
| shared | SystemOwnedObject | not required | × || × | × |
| frozen | SystemOwnedObject | not required | × | × | × | × |
| public | UserOwnedObject | key, store |||| × |
| private | UserOwnedObject | key | × || × | × |


### Object RPC

`ObjectEntity` data can be retrieved through `rooch_getState` RPC interface.
Expand Down Expand Up @@ -247,15 +268,55 @@ The `context` and `object` modules provide the following functions that can oper

In the above functions, if the `#[private_generics<T>]` column is `true`, it indicates that only the module where `T` is located can call the function.

### Comparison between Rooch Object, Sui Object, and Aptos Object
## Dynamic Fields of Object in Rooch

Rooch provides the capability to manage dynamic fields for objects. Dynamic fields are Resources or Objects stored within an Object in the form of key-value pairs. Notably, the key can be heterogeneous, meaning it is not restricted by the type of the key. More specifically, an Object can be used as a [Table](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/sources/table.move) or [Bag](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/sources/bag.move).

Rooch objects offer two types of dynamic fields: normal types and Object types.

Normal dynamic fields are resources with `store` ability stored under an object; Object type dynamic fields store child Object instances under an object.

<Callout>
Note: Since the Object type itself also has the `store` ability, what is the difference between storing the entire `Object<T>` as a normal field under an object and using an Object type field?
1. If a child object is created via `add_object_field`, it is a child of the parent object and is under the same SMT subtree. This facilitates management of the entire parent object's state transition, queries and so on.
2. If an object is created globally, even if it is added to the dynamic fields of an object via `add_field`, it actually a global object, and its state tree is under the global Root.
</Callout>

### List of Methods for Regular Dynamic Fields

| Method | Description
|---|---
| `add_field<T: key, K: copy + drop, V: store>(obj: &mut Object<T>, key: K, val: V)` | Adds a dynamic field to the object. If the same key already exists, it aborts. The field itself is not stored in the object and cannot be discovered from the object.
| `borrow_field<T: key, K: copy + drop, V: store>(obj: &Object<T>, key: K): &V` | Gets an immutable reference to the value corresponding to the key in the object. If there is no corresponding key, it aborts.
| `borrow_field_with_default<T: key, K: copy + drop, V: store>(obj: &Object<T>, key: K, default: &V): &V` | Gets an immutable reference to the value corresponding to the key in the object. If there is no corresponding key, it returns the default value.
| `borrow_mut_field<T: key, K: copy + drop, V: store>(obj: &mut Object<T>, key: K): &mut V` | Gets a mutable reference to the value corresponding to the key in the object. If there is no corresponding key, it aborts.
| `borrow_mut_field_with_default<T: key, K: copy + drop, V: store + drop>(obj: &mut Object<T>, key: K, default: V): &mut V` | Gets a mutable reference to the value corresponding to the key in the object. If there is no corresponding key, it inserts the key-value pair (`key`, `default`) and then returns a mutable reference to the corresponding value.
| `remove_field<T: key, K: copy + drop, V: store>(obj: &mut Object<T>, key: K): V` | Removes the field corresponding to the key from the object and returns the value of the field. If there is no corresponding key, it aborts.
| `contains_field<T: key, K: copy + drop>(obj: &Object<T>, key: K): bool` | Returns `true` if the object contains the field corresponding to the key, otherwise `false`.
| `contains_field_with_type<T: key, K: copy + drop, V: store>(obj: &Object<T>, key: K): bool` | Returns `true` if the object contains the field corresponding to the key and the value type is `V`, otherwise `false`.
| `upsert_field<T: key, K: copy + drop, V: store + drop>(obj: &mut Object<T>, key: K, value: V)` | If the object contains the field corresponding to the key, it updates the value of the field. If there is no corresponding key, it inserts the key-value pair (`key`, `value`).
| `field_size<T: key>(obj: &Object<T>): u64` | Returns the number of fields in the object, i.e., the number of key-value pairs.

### List of Methods for Object Type Dynamic Fields

| Method | Description
|---|---
| `add_object_field<T: key, V: key>(obj: &mut Object<T>, v: V): Object<V>` | Adds a new child object field to the object and returns the newly added child object. Only shared objects can add child object fields.
| `add_object_field_with_id<T: key, ID:drop, V: key>(obj: &mut Object<T>, id: ID, v: V): Object<V>` | Adds a new child object field to the object with a custom ID and returns the newly added child object. Only shared objects can add child object fields.
| `borrow_object_field<T: key, V: key>(obj: &Object<T>, key: ObjectID): &Object<V>` | Borrows an immutable reference to the specified child object field in the object. If the child object field does not exist, the operation fails.
| `borrow_mut_object_field<T: key, V: key>(obj: &mut Object<T>, key: ObjectID): &mut Object<V>` | Borrows a mutable reference to the specified child object field in the object. If the child object field does not exist, the operation fails.
| `remove_object_field<T: key, V: key>(obj: &mut Object<T>, child: Object<V>): V` | Removes the specified child object field from the object and returns its value. If the child object field does not exist, the operation fails.
| `contains_object_field<T: key, V: key>(obj: &Object<T>, key: ObjectID): bool` | Checks if the object contains the specified child object field.

## Comparison between Rooch Object, Sui Object, and Aptos Object

#### Sui Object
### Sui Object

* Sui Object is a special kind of `struct` that requires the `struct` to has a `key` ability, and UID must be its first field. An Object is provided by the VM and storage, and there's no Object type in Move. In Rooch, Object is a type defined in Move itself.
* Sui Object is indexed by the external system, and there's no method provided in the contract to retrieve the Object using ID; it can only be passed through parameters. Rooch provides both methods.
* If a Sui Object gets nested or saved into other containers, it will become invisible in the global Object Storage. However, even when nested or saved into other containers, the Rooch Object can still be accessed in the global Object Storage.

#### Aptos Object
### Aptos Object

* At the base level, an Aptos Object is a special account, where the `address` is the `ObjectID`.
* `Object<T>` represents the reference to an Object that can be `copy`,`drop`, whereas in Rooch, `Object<T>` is a single instance and cannot be `copy`, `drop`.
Expand All @@ -265,7 +326,7 @@ In the above functions, if the `#[private_generics<T>]` column is `true`, it ind
TODO: This part of this document needs to be improved
</Callout>

### References
## References

1. [Rooch Object API document](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/doc/object.md)
2. [Rooch Object Source code](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/sources/object.move)
Expand Down
70 changes: 65 additions & 5 deletions docs/website/pages/learn/core-concepts/objects/object.zh-CN.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ module moveos_std::object {

#### 共享的(Shared)和冻结的(Frozen) Object

`Object<T>` 有两种状态,一种是 `shared`,一种是 `frozen`
SystemOwnedObject `Object<T>` 有两种状态,一种是 `shared`,一种是 `frozen`

* `SharedObject`:任何人都可以直接获取到 `&mut Object<T>` 引用。
* `FrozenObject`:任何人都无法获取到 `&mut Object<T>` 引用,包括 `T` 所在的模块。
Expand Down Expand Up @@ -180,6 +180,26 @@ module moveos_std::object {

删除 Object 后会返回 Object 中封装的数据,只有 `T` 所在的模块才能调用该方法。

综上所述,针对不同状态的 Object 的操作,不同的用户具有不同的权限。以下是合约开发者和普通用户对不同类型 Object,使用 `moveos_std::object` 提供的方法可以进行的操作:

- 合约开发者

| object | owner | value abilities | transfer | borrow mut | take value | remove |
|---------|-------------------|-----------------|----------|------------|------------|--------|
| shared | SystemOwnedObject | not required | × || × | × |
| frozen | SystemOwnedObject | not required | × | × | × | × |
| public | UserOwnedObject | key, store |||||
| private | UserOwnedObject | key |||||

- 普通用户

| object | owner | value abilities | transfer | borrow mut | take value | remove |
|---------|-------------------|-----------------|----------|------------|------------|--------|
| shared | SystemOwnedObject | not required | × || × | × |
| frozen | SystemOwnedObject | not required | × | × | × | × |
| public | UserOwnedObject | key, store |||| × |
| private | UserOwnedObject | key | × || × | × |

### Object RPC

通过 `rooch_getState` RPC 接口可以获取到 `ObjectEntity` 的数据。
Expand Down Expand Up @@ -247,15 +267,55 @@ https://dev-seed.rooch.network

以上函数中,如果 `#[private_generics<T>]` 列为 `true`,表明只有 `T` 所在的模块才能调用。

### Rooch Object, Sui Object, Aptos Object 的比较
## Object 的 dynamic fields

Rooch 为 object 提供了管理动态字段的能力。动态字段是指将 Resource 或者 Object 以 key, value 的形式储存在 Object 中。特别是,key 可以是异质的,即不受 key 类型的限制。更具体的说,Object 可以被当作 [Table](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/sources/table.move)[Bag](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/sources/bag.move) 来使用。

Rooch object 的提供了两种类型的动态字段:常规类型和Object类型。

常规类型的动态字段是指任何具有 `store` ability 的类型存放在 object 下;Object 类型的动态字段是将子 Object 对象存放在 object 下。

<Callout>
注意:由于 Object 类型本身也具有 `store` ability,那把整个 `Obejct<T>` 作为一个普通的字段存放在 object 下和使用 Object 类型字段有什么区别?
1. 如果通过 `add_object_field` 创建的子 object,是属于父 object 的子对象,与父 object 在同一个 SMT 子树下。这对于整个父 object 的状态迁移,查询等管理都很便捷。
2. 如果在全局创建的 object,即使通过 `add_field` 放到 object 的动态字段中了,它实际上也是属于 global object,它的状态树处于 Root 根下。
</Callout>

### 常规类型动态字段相关方法列表

| 方法 | 说明
|---|---
| `add_field<T: key, K: copy + drop, V: store>(obj: &mut Object<T>, key: K, val: V)` | 添加一个动态字段到对象。如果已经存在相同的键,则会中止。字段本身不会存储在对象中,并且不能从对象中发现。
| `borrow_field<T: key, K: copy + drop, V: store>(obj: &Object<T>, key: K): &V` | 获取对象中键对应的值的不可变引用。如果没有对应的键,则会中止。
| `borrow_field_with_default<T: key, K: copy + drop, V: store>(obj: &Object<T>, key: K, default: &V): &V` | 获取对象中键对应的值的不可变引用。如果没有对应的键,则返回默认值。
| `borrow_mut_field<T: key, K: copy + drop, V: store>(obj: &mut Object<T>, key: K): &mut V` | 获取对象中键对应的值的可变引用。如果没有对应的键,则会中止。
| `borrow_mut_field_with_default<T: key, K: copy + drop, V: store + drop>(obj: &mut Object<T>, key: K, default: V): &mut V` | 获取对象中键对应的值的可变引用。如果没有对应的键,则插入键值对(`key`, `default`),然后返回对应的值的可变引用。
| `remove_field<T: key, K: copy + drop, V: store>(obj: &mut Object<T>, key: K): V` | 从对象中移除键对应的字段,并返回字段的值。如果没有对应的键,则会中止。
| `contains_field<T: key, K: copy + drop>(obj: &Object<T>, key: K): bool` | 如果对象中存在键对应的字段,则返回`true`,否则返回`false`
| `contains_field_with_type<T: key, K: copy + drop, V: store>(obj: &Object<T>, key: K): bool` | 如果对象中存在键对应的字段,并且字段的值类型为`V`,则返回`true`,否则返回`false`
| `upsert_field<T: key, K: copy + drop, V: store + drop>(obj: &mut Object<T>, key: K, value: V)` | 如果对象中存在键对应的字段,则更新字段的值。如果没有对应的键,则插入键值对(`key`, `value`)。
| `field_size<T: key>(obj: &Object<T>): u64` | 返回对象中字段的数量,即键值对的数量。

### Object 类型动态字段相关方法列表

| 方法 | 说明
|---|---
| `add_object_field<T: key, V: key>(obj: &mut Object<T>, v: V): Object<V>` | 向对象添加一个新的子对象字段,返回新添加的子对象。只有共享对象可以添加子对象字段。
| `add_object_field_with_id<T: key, ID:drop, V: key>(obj: &mut Object<T>, id: ID, v: V): Object<V>` | 使用自定义ID向对象添加一个新的子对象字段,返回新添加的子对象。只有共享对象可以添加子对象字段。
| `borrow_object_field<T: key, V: key>(obj: &Object<T>, key: ObjectID): &Object<V>` | 借用对象中指定子对象字段的不可变引用。如果子对象字段不存在,此操作将失败。
| `borrow_mut_object_field<T: key, V: key>(obj: &mut Object<T>, key: ObjectID): &mut Object<V>` | 借用对象中指定子对象字段的可变引用。如果子对象字段不存在,此操作将失败。
| `remove_object_field<T: key, V: key>(obj: &mut Object<T>, child: Object<V>): V` | 从对象中移除指定的子对象字段并返回其值。如果子对象字段不存在,此操作将失败。
| `contains_object_field<T: key, V: key>(obj: &Object<T>, key: ObjectID): bool` | 检查对象中是否存在指定的子对象字段。

## Rooch Object, Sui Object, Aptos Object 的比较

#### Sui Object
### Sui Object

* Sui Object 是一种特殊的 `struct` 要求该 `struct` 必须拥有 `key` ability, 同时第一个字段必须是 `UID`,Object 是虚拟机和存储提供的,Move 中并不存在 Object 类型。Rooch 中的 Object 是在 Move 中定义的类型。
* Sui Object 由外部系统索引,合约内并没有提供通过 ID 获取 Object 的方法,只能通过参数传递。Rooch 同时提供两种方式。
* Sui Object 如果发生嵌套或者保存到其他容器中,Object 在全局 Object Storage 就不可见。而 Rooch 中的 Object 嵌套或者保存到其他容器中,Object 依然在全局 Object Storage 中可访问。

#### Aptos Object
### Aptos Object

* Aptos Object 底层是一种特殊的账户,该账户的 `address``ObjectID`
* `Object<T>` 代表对 Object 的引用,可以 `copy``drop`,而 Rooch 中 `Object<T>` 只有一个实例,不可以 `copy``drop`
Expand All @@ -265,7 +325,7 @@ https://dev-seed.rooch.network
TODO: This part of this document needs to be improved
</Callout>

### 参考链接
## 参考链接

1. [Rooch Object API document](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/doc/object.md)
2. [Rooch Object Source code](https://github.com/rooch-network/rooch/blob/main/frameworks/moveos-stdlib/sources/object.move)
Expand Down
2 changes: 1 addition & 1 deletion frameworks/moveos-stdlib/sources/object.move
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ module moveos_std::object {
// === Object Raw Dynamic Fields ===

#[private_generics(T)]
/// Add a dynamic filed to the object. Aborts if an field for this
/// Add a dynamic field to the object. Aborts if an field for this
/// key already exists. The field itself is not stored in the
/// object, and cannot be discovered from it.
public fun add_field<T: key, K: copy + drop, V: store>(obj: &mut Object<T>, key: K, val: V) {
Expand Down

0 comments on commit 2a77324

Please sign in to comment.