Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support list fields for object.move #3142

Merged
merged 33 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e041dd1
feat: support list fields for object.move
lispking Dec 31, 2024
71ebff9
Merge branch 'main' into object-support-list
lispking Dec 31, 2024
eba4105
fix UNKNOWN_INVARIANT_VIOLATION_ERROR
jolestar Jan 2, 2025
14a510b
Merge branch 'main' into object-support-list
lispking Jan 2, 2025
4680640
rename list_fields to list_field_names
lispking Jan 2, 2025
c87f419
fix native return types
jolestar Jan 2, 2025
fe8df10
make code tidy
lispking Jan 2, 2025
f876e1b
make code tidy
lispking Jan 2, 2025
08137bb
make code tidy
lispking Jan 2, 2025
7a2bfe0
support cursor and limit for list_field_keys
lispking Jan 3, 2025
40e719f
update object list_field_keys gas handle and adjust fetch data from db
lispking Jan 3, 2025
7dd4cb1
Merge branch 'main' into object-support-list
lispking Jan 3, 2025
2efe903
make code tidy
lispking Jan 3, 2025
66b468e
Merge branch 'main' into object-support-list
lispking Jan 3, 2025
ba929a6
update native_list_field_keys default value to 0
lispking Jan 3, 2025
1686dac
check is_empty that before add native_list_field_keys
lispking Jan 3, 2025
9684834
update is_empty for ListFieldsGasParametersOption
lispking Jan 3, 2025
11d3ed1
fix review notes
lispking Jan 4, 2025
0de2426
fix lint
lispking Jan 4, 2025
9867ccd
add list_field_keys for table and add rooch-framework-tests
lispking Jan 4, 2025
455c446
update ut assert value
lispking Jan 4, 2025
3488bfa
update list_field_keys of table notes
lispking Jan 4, 2025
4688189
add length ut for list_field_keys
lispking Jan 4, 2025
d387871
add more ut for list_field_keys
lispking Jan 4, 2025
5d400a4
make ut tidy
lispking Jan 4, 2025
3840b28
add iterator for table
lispking Jan 4, 2025
9738258
fix next/next_mut for table issue
lispking Jan 4, 2025
3028208
update borrow_field_key_internal and borrow_mut_field_key_internal notes
lispking Jan 4, 2025
1962a0e
add How to test move contract? notes
lispking Jan 4, 2025
cda0f0b
update borrow_field_key_internal and borrow_mut_field_key_internal notes
lispking Jan 4, 2025
c951e0c
update review notes
lispking Jan 4, 2025
b647253
update review notes
lispking Jan 4, 2025
ca62697
Merge branch 'main' into object-support-list
lispking Jan 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 47 additions & 7 deletions frameworks/moveos-stdlib/sources/object.move
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module moveos_std::object {
const ErrorTypeMismatch: u64 = 10;
/// The child object level is too deep
const ErrorChildObjectTooDeep: u64 = 11;
/// The object has no parent
/// The object has no parent
const ErrorWithoutParent: u64 = 12;
/// The parent object is not match
const ErrorParentNotMatch: u64 = 13;
Expand Down Expand Up @@ -138,10 +138,10 @@ module moveos_std::object {
let child = derive_object_key<ID, T>(id);
let path = parent_id.path;
vector::push_back(&mut path, child);
ObjectID { path }
ObjectID { path }
}

/// Object<T> is a pointer type to the Object in storage, It has `key` and `store` ability.
/// Object<T> is a pointer type to the Object in storage, It has `key` and `store` ability.
struct Object<phantom T> has key, store {
id: ObjectID,
}
Expand Down Expand Up @@ -291,7 +291,7 @@ module moveos_std::object {
}

/// Remove the object from the global storage, and return the object value
/// Do not check if the dynamic fields are empty
/// Do not check if the dynamic fields are empty
public(friend) fun remove_unchecked<T: key>(self: Object<T>): T {
let Object{id} = self;
let (parent, key) = into_parent_id_and_key(id);
Expand Down Expand Up @@ -472,7 +472,7 @@ module moveos_std::object {
let DynamicField { name:_, value } = native_remove_field<DynamicField<Name, Value>>(obj_id, key);
value
}

/// Returns true if `object` contains an field for `key`, include normal field and object field
public fun contains_field<T: key, Name: copy + drop + store>(obj: &Object<T>, name: Name): bool {
contains_field_internal<Name>(obj.id, name)
Expand All @@ -494,6 +494,11 @@ module moveos_std::object {
native_object_size(obj.id)
}

/// List all field names of the object
fun list_field_keys<T: key>(obj: &Object<T>): vector<address> {
lispking marked this conversation as resolved.
Show resolved Hide resolved
native_list_field_keys<address>(obj.id)
}


// ====== Utility functions ======

Expand Down Expand Up @@ -538,7 +543,7 @@ module moveos_std::object {
native fun native_transfer_object<T: key>(obj: Object<T>, new_owner: address);

native fun native_to_shared_object<T: key>(obj: Object<T>);

native fun native_to_frozen_object<T: key>(obj: Object<T>);

native fun native_borrow_object<T: key>(object_id: ObjectID): &Object<T>;
Expand All @@ -562,6 +567,8 @@ module moveos_std::object {

native fun native_remove_field<V>(obj_id: ObjectID, key: address): V;

native fun native_list_field_keys<V>(obj_id: ObjectID): vector<V>;
lispking marked this conversation as resolved.
Show resolved Hide resolved

#[test_only]
/// Testing only: allows to drop a Object even if it's fields is not empty.
public fun drop_unchecked<T: key>(self: Object<T>): T {
Expand Down Expand Up @@ -752,7 +759,7 @@ module moveos_std::object {
let _obj = borrow_mut_object<TestStruct>(alice, object_id);
};

// borrow_mut_object by non-owner failed
// borrow_mut_object by non-owner failed
{
let _obj = borrow_mut_object<TestStruct>(bob, object_id);
};
Expand Down Expand Up @@ -990,4 +997,37 @@ module moveos_std::object {
//test address
field_key_derive_test(@0x1, @0x07d29b5cffb95d39f98baed1a973e676891bc9d379022aba6f4a2e4912a5e552);
}

#[test]
fun test_list_fields(){
let obj = new(TestStruct { count: 1 });
add_field(&mut obj, b"key1", 1u64);
add_field(&mut obj, b"key2", 2u64);

assert!(field_size(&obj) == 2, 1000);

let field_keys = list_field_keys<TestStruct>(&obj);
assert!(!vector::is_empty(&field_keys), 1001);
assert!(vector::length(&field_keys) == 2, 1002);

std::debug::print(&field_keys);

std::debug::print_string(b"test_list_fields");

let field_key1 = *vector::borrow(&field_keys, 0);
std::debug::print(&field_key1);

let field1 = native_borrow_field<DynamicField<vector<u8>, u64>>(obj.id, field_key1);
assert!(field1.name == b"key1", 1003);
assert!(field1.value == 1u64, 1004);

let field_key2 = *vector::borrow(&field_keys, 1);
std::debug::print(&field_key2);

let field2 = native_borrow_field<DynamicField<vector<u8>, u64>>(obj.id, field_key2);
assert!(field2.name == b"key2", 1005);
assert!(field2.value == 2u64, 1006);

let TestStruct{ count: _} = drop_unchecked(obj);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ pub struct GasParameters {
pub native_contains_field: ContainsFieldGasParameters,
pub native_contains_field_with_value_type: ContainsFieldGasParameters,
pub native_remove_field: RemoveFieldGasParameters,
pub native_list_field_keys: ListFieldsGasParameters,
}

impl GasParameters {
Expand Down Expand Up @@ -144,6 +145,10 @@ impl GasParameters {
base: 0.into(),
per_byte_serialized: 0.into(),
},
native_list_field_keys: ListFieldsGasParameters {
base: 0.into(),
per_byte_serialized: 0.into(),
},
}
}
}
Expand Down Expand Up @@ -218,6 +223,10 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator<Item = (String, Nati
"native_contains_field_with_value_type",
helpers::make_native(gas_params.clone(), native_contains_field_with_value_type),
),
(
"native_list_field_keys",
helpers::make_native(gas_params.clone(), native_list_field_keys),
),
];

make_module_natives(natives)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,52 @@ pub(crate) fn native_remove_field(
)
}

/***************************************************************************************************
* native fun native_list_field_keys<V>(obj_id: ObjectID): V;
**************************************************************************************************/
#[derive(Debug, Clone)]
pub struct ListFieldsGasParameters {
pub base: InternalGas,
pub per_byte_serialized: InternalGasPerByte,
lispking marked this conversation as resolved.
Show resolved Hide resolved
}

pub(crate) fn native_list_field_keys(
gas_parameters: &GasParameters,
context: &mut NativeContext,
ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
debug_assert_eq!(ty_args.len(), 1);
debug_assert_eq!(args.len(), 1);

let obj_id = pop_object_id(&mut args)?;

let common_gas_parameter = gas_parameters.common.clone();
let list_fields_gas_parameter = gas_parameters.native_list_field_keys.clone();

let object_context = context.extensions().get::<ObjectRuntimeContext>();
let binding = object_context.object_runtime();
let mut object_runtime = binding.write();
let resolver = object_runtime.resolver();
let (rt_obj, object_load_gas) = object_runtime.load_object(context, &obj_id)?;
let field_key_bytes = AccountAddress::LENGTH as u64;
let gas_cost = list_fields_gas_parameter.base
+ list_fields_gas_parameter.per_byte_serialized * NumBytes::new(field_key_bytes)
+ common_gas_parameter.calculate_load_cost(object_load_gas);

let result = rt_obj.list_field_keys(context, resolver, None, usize::MAX);
match result {
Ok((field_keys, field_load_gas)) => Ok(NativeResult::ok(
gas_cost + common_gas_parameter.calculate_load_cost(field_load_gas),
smallvec![Value::vector_address(field_keys)],
)),
Err(err) => {
let abort_code = error_to_abort_code(err);
Ok(NativeResult::err(gas_cost, abort_code))
}
}
}

fn object_field_fn_dispatch(
common_gas_params: &CommonGasParameters,
base: InternalGas,
Expand Down
37 changes: 37 additions & 0 deletions moveos/moveos-object-runtime/src/runtime_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,43 @@ impl RuntimeObject {

Ok((fields, Some(total_bytes_len)))
}

/// List fields keys of the object from the state store.
pub fn list_field_keys(
&self,
layout_loader: &dyn TypeLayoutLoader,
resolver: &dyn StatelessResolver,
cursor: Option<FieldKey>,
limit: usize,
) -> PartialVMResult<(Vec<AccountAddress>, Option<Option<NumBytes>>)> {
let mut total_bytes_len = NumBytes::zero();
let cached_fields = self
.fields
.iter()
.filter_map(|(key, field)| {
if field.is_none() {
return None;
}
Some(AccountAddress::from(*key))
})
.collect::<Vec<AccountAddress>>();
if !cached_fields.is_empty() {
return Ok((cached_fields, Some(Some(total_bytes_len))));
}

let fields_with_objects =
self.list_field_objects_from_db(layout_loader, resolver, cursor, limit)?;
let fields = fields_with_objects
.into_iter()
.filter_map(|(key, _db_obj, bytes_len_opt)| {
bytes_len_opt
.flatten()
.map(|bytes_len| total_bytes_len += bytes_len);
Some(key.into())
})
.collect::<Vec<AccountAddress>>();
Ok((fields, Some(Some(total_bytes_len))))
}
}

/// Internal functions
Expand Down
Loading