AIP-10 提议在链上单个地址的全局访问 Move 对象,用于存储异构资源集。对象提供了一个丰富的能力模型,允许细粒度的资源控制和所有权管理。通过利用帐户模型的各个方面,对象可以直接发出事件,从而更丰富地理解链上操作。
此 AIP 提议移动对象,以便全局访问链上单个地址存储的异构资源集合。对象提供了一种丰富的能力模型,可以实现细粒度的资源控制和所有权管理。通过利用账户模型的各个方面,对象可以直接发出事件,从而更深入地了解链上操作。
对象模型允许 Move 将复杂类型表示为存储在单个地址内的一组资源。在对象模型中,NFT 或 Token 可以将常见的 Token 数据放置在 Token
资源中,将对象数据放置在 ObjectCore
资源中,然后根据需要专门化为其他资源,例如,Player
对象可以在游戏中定义一个玩家。ObjectCore
本身既存储当前所有者的 address
,也存储用于创建事件流的相应数据。
对象模型通过使用由不同访问器或引用实现的、能力框架来提高类型安全性和数据访问,这些访问器或引用定义并分发各种能力,从而实现丰富的操作。这些能力包括:
ConstructorRef
允许创建所有其他能力,允许将资源添加到对象,并且只能在创建对象时访问。Object<T>
指向包含 T
资源的某个对象。这对于存储对资源的引用以进行反向查找非常有用。DeleteRef
允许持有者从存储中删除对象。ExtendRef
允许持有者获得访问 signer 的权限以添加新资源。TransferRef
允许持有者在创建后将新资源转移到对象。如果存在于模块中的 DeleteRef
允许创建者或所有者销毁 Token(如果存在)。而 TransferRef
可以在模块内使用,以定义确定何时以及由谁可以转移对象的逻辑,也可以将其赠送出去,并将其视为转移对象能力的基础。
此外,对象还支持对象的可组合性,允许对象拥有其他对象。每个对象都在其状态中存储其所有者的身份。所有者可以通过在其自己的存储中创建并存储 Object<T>
来跟踪其拥有的对象。从而实现了对象模型中无缝的双向导航。
现有的 Aptos 数据模型强调在 Move 中使用 store
ability。Store 允许一个 struct 存在于存储在全局存储中的任何 struct 中,并用 key
ability 标记。因此,数据可以存在于任何 struct 中的任何位置和任何地址。虽然这提供了很大的灵活性,但它也有许多限制:
any
存储到单个数据结构(例如,map、vector)中,但对于复杂的数据类型,any
会在 Move 中产生额外的成本,因为每次访问都需要反序列化。如果 API 开发人员期望特定的 any 字段更改其表示的类型,这也可能导致混淆。在这个过程中,考虑了以下几种替代方案:
OwnerRef
而不是在对象中定义所有权。OwnerRef
提供了一种自然的“嵌套”对象方法。但是,OwnerRef
需要额外的存储空间,仍然需要访问对象才能提供门控存储,并且在可删除对象的情况下会成为弱引用。构建对象时考虑了以下几点:
Object
struct,它定义了 create_object
函数。create_object
生成一个新地址,将 Object
struct 存储在该地址的 ObjectGroup
资源组中,并返回一个 ConstructorRef
。ConstructorRef
获取 signer,将适当的 struct 存储在 ObjectGroup
资源中,进行任何其他修改,然后将 ConstructorRef
返回到堆栈中。对象存储在 ObjectGroup
资源组中。这允许对象中的其他资源为了数据局部性和数据成本节省而并置。请注意,对象中的所有资源不必都并置在 ObjectGroup
中。这留给对象的开发者来定义他们的数据布局。
##[resource_group(scope = global)]
struct ObjectGroup { }
核心 ObjectCore
由以下 Move struct 表示:
##[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct ObjectCore has key {
/// 用于 guid 以保证全局唯一对象并创建事件流
guid_creation_num: u64,
/// 拥有此对象的地址(对象或帐户)
owner: address,
/// 对象转移是一种常见操作, 这允许禁用和启用
/// 传输。绕过 TransferRef 的使用。
allow_ungated_transfer: bool,
/// 所有权转移时发出的事件。
transfer_events: event::EventHandle<TransferEvent>,
}
每个对象都存储在由 Object<T>
表示的自己的地址中。底层地址可以从用户提供的输入或从当前账户的全局唯一 ID 生成器 (guid.move
) 生成。此外,地址生成利用了与现有账户地址生成不同的域分隔符:sha3_256(address_of(creator) | seed | DOMAIN_SEPaRATOR)
。
seed
是由 guid.move
生成的当前 GUID:bcs::to_bytes(account address | u64)
。DOMAIN_SEPaRATOR
是 0xFD。seed
由用户定义,例如字符串。DOMAIN_SEPaRATOR
是 0xFE。Object<T>
copy, drop, store
{ inner: address }
ConstructorRef
drop
{ self: ObjectId, can_delete: bool }
DeleteRef
ObjectGroup
中删除对象drop
, store
{ self: ObjectId }
ExtendRef
drop, store
{ self: ObjectId }
TransferRef
drop, store
{ self: ObjectId }
LinearTransferRef
TransferRef
的权限drop
{ self: ObjectId, owner: address }
用于 address
与 ObjectId
之间转换的函数:
/// 从给定的地址生成一个对象。这会验证 T 是否存在。
public fun address_to_object<T: key>(object: address): Object<T>;
/// 返回 ObjectId 中的地址。
public fun object_address<T>(object: &Object<T>): address;
/// 从源材料派生对象地址:sha3_256([创建者地址 | seed | 0xFE])。
/// 对象需要与 create_resource_address 区分开来
public fun create_object_address(source: &address, seed: vector<u8>): ObjectId;
用于创建对象的函数:
/// 创建一个新的命名对象并返回 ConstructorRef。通过知道用于创建它们的、用户生成的 seed,可以全局查询命名对象。命名对象无法删除。
public fun create_named_object(creator: &signer, seed: vector<u8>): ConstructorRef;
/// 从账户生成的 GUID 创建一个新对象。
public fun create_object_from_account(creator: &signer): ConstructorRef;
/// 从对象生成的 GUID 创建一个新对象。
public fun create_object_from_object(creator: &signer): ConstructorRef;
/// 生成 DeleteRef,可用于从全局存储中删除对象。
public fun generate_delete_ref(ref: &ConstructorRef): DeleteRef;
/// 生成 ExtendRef,可用于向对象添加新事件和资源。
public fun generate_extend_ref(ref: &ConstructorRef): ExtendRef;
/// 生成 TransferRef,可用于管理对象转移。
public fun generate_transfer_ref(ref: &ConstructorRef): TransferRef;
/// 为 ConstructorRef 创建一个 signer
public fun generate_signer(ref: &ConstructorRef): signer;
/// 返回 ConstructorRef 中的地址
public fun object_from_constructor_ref<T: key>(ref: &ConstructorRef): Object<T>;
用于向对象添加新事件和资源的函数:
/// 为对象创建一个 guid,通常用于事件
public fun create_guid(object: &signer): guid::GUID;
/// 生成一个新的事件Handle。
public fun new_event_handle<T: drop + store>(object: &signer): event::EventHandle<T>;
用于删除对象的函数:
/// 返回 DeleteRef 中的地址。
public fun object_from_delete<T: key>(ref: &DeleteRef): Object<T>;
/// 从指定的对象中删除全局存储。
public fun delete(ref: DeleteRef);
用于扩展对象的函数:
/// 为 ExtendRef 创建一个 signer
public fun generate_signer_for_extending(ref: &ExtendRef): signer;
用于转移对象的函数:
/// 禁用直接转移,只能通过 TransferRef 触发转移
public fun disable_ungated_transfer(ref: &TransferRef);
/// 启用直接转移。
public fun enable_ungated_transfer(ref: &TransferRef);
/// 为一次性转移创建 LinearTransferRef。这要求生成时的所有者是转移时的所有者。
public fun generate_linear_transfer_ref(ref: TransferRef): LinearTransferRef;
/// 使用 LinearTransferRef 转移到目标地址。
public fun transfer_with_ref(ref: LinearTransferRef, to: address);
/// 如果 allow_ungated_transfer 设置为 true,则可以使用此 entry function 进行转移。
public entry fun transfer_call(owner: &signer, object: address, to: address);
/// 如果 allow_ungated_transfer 设置为 true,则转移给定的对象。请注意,这允许嵌套对象的所有者转移该对象,只要在层次结构的每个阶段都启用了 allow_ungated_transfer。
public fun transfer<T>(owner: &signer, object: Object<T>, to: address);
/// 将给定的对象转移到另一个对象。有关更多信息,请参见 `transfer`。
public fun transfer_to_object<O, T>(owner: &signer, object<O>: Object, to: Object<T>);
用于验证所有权的函数:
/// 返回当前所有者。
public fun owner<T: key>(object: Object<T>): address;
/// 如果提供的地址是当前所有者,则返回 true。
public fun is_owner<T: key>(object: Object<T>, owner: address): bool;
/// 每当对象的所有者字段更改时发出。
struct TransferEvent has drop, store {
object: address,
from: address,
to: address,
}
https://github.com/aptos-labs/aptos-core/pull/5976 中的第一个提交
开放讨论的领域包括:
Object<T>
是否应该阻止其被删除?当前的 base object 可以扩展以支持所有这些用例。决定是偏向于限制较少的一方,以便在对象标准发展后可以采取措施。
Ref
类型。Alias<T>
hero
定义了多个版本的 struct(HeroV1
、HeroV2
、...)hero
中的 Hero
概念,而不关心特定版本。Hero
,模块本身可以期望 Object<Hero>
并分派到 Hero
的相关版本。account_address | type_info<T>() | 0xFC
create_typed_object<T>(account: &signer, &_proof: T)
创建,这确保了这些对象实际上将包含 T,即可互换资产的存储。balance<0x1::aptos_coin::AptosCoin>(addr)
将从存储在 addr | 0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin> | 0xFC
中的 0x1::coin::CoinStore
对象读取。
- 原文链接: github.com/aptos-foundat...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!