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

[sui framework] eliminate polymorphic delete in Transfer::delete_chil… #2179

Merged
merged 1 commit into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ module ObjectOwner::ObjectOwner {
let Parent { id, child: child_ref_opt } = parent;
let child_ref = Option::extract(&mut child_ref_opt);
Option::destroy_none(child_ref_opt);
Transfer::delete_child_object(child, child_ref);
let Child { id: child_id } = child;
Transfer::delete_child_object(child_id, child_ref);
ID::delete(id);
}
}
1 change: 0 additions & 1 deletion crates/sui-core/src/unit_tests/move_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ async fn test_object_wrapping_unwrapping() {
assert_eq!(parent_object_ref.1, OBJECT_START_VERSION);

// Extract the child out of the parent.
println!("before this call");
let effects = call_move(
&authority,
&gas,
Expand Down
21 changes: 10 additions & 11 deletions crates/sui-framework/sources/Transfer.move
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,20 @@ module Sui::Transfer {
/// Transfer a child object to an account address. This is one of the two ways that can
/// consume a ChildRef. No new ChildRef will be created, as the object is no longer
/// owned by an object.
// TODO: Figure out a way to make it easier to destroy a child object in one call.
// Currently one has to first transfer it to an address, and then delete it.
public fun transfer_child_to_address<T: key>(child: T, child_ref: ChildRef<T>, recipient: address) {
let ChildRef { child_id } = child_ref;
assert!(&child_id == ID::id(&child), EChildIDMismatch);
transfer(child, recipient)
}

/// Delete the child object along with a ownership reference that shows this object
/// is owned by another object. Deleting both the child object and the reference
/// is safe because the ownership will also be destroyed, and hence there won't
/// be dangling reference to the child object through ownership.
public fun delete_child_object<T: key>(child: T, child_ref: ChildRef<T>) {
let ChildRef { child_id } = child_ref;
assert!(&child_id == ID::id(&child), EChildIDMismatch);
delete_child_object_internal(child);
/// Delete `child_ref`, which must point at `child_id`.
/// This is the second way to consume a `ChildRef`.
/// Passing ownership of `child_id` to this function implies that the child object
/// has been unpacked, so it is now safe to delete `child_ref`.
public fun delete_child_object<T: key>(child_id: VersionedID, child_ref: ChildRef<T>) {
let ChildRef { child_id: child_ref_id } = child_ref;
assert!(&child_ref_id == ID::inner(&child_id), EChildIDMismatch);
delete_child_object_internal(ID::id_address(&child_ref_id), child_id)
}

/// Freeze `obj`. After freezing `obj` becomes immutable and can no
Expand All @@ -150,5 +148,6 @@ module Sui::Transfer {

native fun transfer_internal<T: key>(obj: T, recipient: address, to_object: bool);

native fun delete_child_object_internal<T: key>(child: T);
// delete `child_id`, emit a system `DeleteChildObject(child)` event
native fun delete_child_object_internal(child: address, child_id: VersionedID);
}
11 changes: 6 additions & 5 deletions crates/sui-framework/src/natives/transfer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::{natives::get_object_id, EventType};
use crate::EventType;
use move_binary_format::errors::PartialVMResult;
use move_core_types::account_address::AccountAddress;
use move_vm_runtime::native_functions::NativeContext;
Expand Down Expand Up @@ -100,12 +100,13 @@ pub fn delete_child_object_internal(
ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
debug_assert!(ty_args.len() == 1);
debug_assert!(args.len() == 1);
debug_assert!(ty_args.is_empty());
// first args is an object ID that we will emit in a DeleteChildObject event
// second arg is VersionedID that we want to ignore
debug_assert!(args.len() == 2);

let obj = args.pop_back().unwrap();
let obj_id = args.pop_front().unwrap();
let event_type = EventType::DeleteChildObject;
let obj_id = get_object_id(obj)?;
// TODO: Decide the cost.
let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 1);
if context.save_event(vec![], event_type as u64, Type::Address, obj_id)? {
Expand Down
21 changes: 17 additions & 4 deletions crates/sui-verifier/src/id_leak_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,31 @@ impl<'a> TransferFunctions for IDLeakAnalysis<'a> {

impl<'a> AbstractInterpreter for IDLeakAnalysis<'a> {}

/// Sui::ID::delete function is allowed to take an ID by value.
/// Certain Sui Framework functions can safely take a `VersionedID` by value
fn is_call_safe_to_leak(verifier: &IDLeakAnalysis, function_handle: &FunctionHandle) -> bool {
let m = verifier
.binary_view
.module_handle_at(function_handle.module);
verifier.binary_view.address_identifier_at(m.address) == &SUI_FRAMEWORK_ADDRESS
&& verifier.binary_view.identifier_at(m.name).as_str() == "ID"
let is_framework =
verifier.binary_view.address_identifier_at(m.address) == &SUI_FRAMEWORK_ADDRESS;
if !is_framework {
return false;
}

// Sui::ID::delete
(verifier.binary_view.identifier_at(m.name).as_str() == "ID"
&& verifier
.binary_view
.identifier_at(function_handle.name)
.as_str()
== "delete"
== "delete") ||
// Sui::Transfer::delete_child_object
(verifier.binary_view.identifier_at(m.name).as_str() == "Transfer"
&& verifier
.binary_view
.identifier_at(function_handle.name)
.as_str()
== "delete_child_object")
}

fn call(verifier: &mut IDLeakAnalysis, function_handle: &FunctionHandle) -> SuiResult {
Expand Down