Skip to content

Commit

Permalink
[sui-framework] Remove ChildRef. Added TypedID. (MystenLabs#3184)
Browse files Browse the repository at this point in the history
* [sui-framework] Remove ChildRef. Added TypedID.

- New typed_id module
- Removed ChildRef, replaced usages with ID or TypedID
- Transfers of child objects are now controlled through the
  "private" transfer mechanism
  • Loading branch information
tnowacki authored Jul 15, 2022
1 parent b3126dc commit c9ba193
Show file tree
Hide file tree
Showing 37 changed files with 325 additions and 741 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ task 1 'publish'. lines 6-21:
created: object(103)
written: object(102)

task 2 'publish'. lines 23-53:
task 2 'publish'. lines 23-52:
created: object(105)
written: object(104)

task 3 'publish'. lines 56-84:
task 3 'publish'. lines 55-82:
created: object(107)
written: object(106)

task 4 'run'. lines 86-86:
task 4 'run'. lines 84-84:
created: object(109)
written: object(108)

task 5 'run'. lines 88-90:
task 5 'run'. lines 86-88:
created: object(111)
written: object(109), object(110)

task 6 'run'. lines 91-91:
task 6 'run'. lines 89-89:
Error: Transaction Effects Status: Invalid Shared Child Object Usage. When a child object (either direct or indirect) of a shared object is passed by-value to an entry function, either the child object's type or the shared object's type must be defined in the same module as the called function. This is violated by object fake(109), whose ancestor fake(111) is a shared object, and neither are defined in this module..
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InvalidSharedChildUse(InvalidSharedChildUse { child: fake(109), ancestor: fake(111) }), source: Some("When a child object (either direct or indirect) of a shared object is passed by-value to an entry function, either the child object's type or the shared object's type must be defined in the same module as the called function. This is violated by object fake(109) (defined in module 'T3::O3'), whose ancestor fake(111) is a shared object (defined in module 'T2::O2'), and neither are defined in this module 'T1::O1'") } }

task 7 'run'. lines 93-93:
task 7 'run'. lines 91-91:
written: object(109), object(111), object(113)
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ module T3::O3 {

module T2::O2 {
use sui::id::VersionedID;
use sui::transfer::{Self, ChildRef};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
use T3::O3::O3;

struct O2 has key, store {
id: VersionedID,
child: ChildRef<O3>,
}

public entry fun create_shared(child: O3, ctx: &mut TxContext) {
Expand All @@ -47,8 +46,8 @@ module T2::O2 {

fun new(child: O3, ctx: &mut TxContext): O2 {
let id = tx_context::new_id(ctx);
let (id, child) = transfer::transfer_to_object_id(child, id);
O2 { id, child }
transfer::transfer_to_object_id(child, &id);
O2 { id }
}
}

Expand All @@ -57,14 +56,13 @@ module T2::O2 {

module T1::O1 {
use sui::id::VersionedID;
use sui::transfer::{Self, ChildRef};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
use T2::O2::O2;
use T3::O3::O3;

struct O1 has key {
id: VersionedID,
child: ChildRef<O2>,
}

public entry fun create_shared(child: O2, ctx: &mut TxContext) {
Expand All @@ -78,8 +76,8 @@ module T1::O1 {

fun new(child: O2, ctx: &mut TxContext): O1 {
let id = tx_context::new_id(ctx);
let (id, child) = transfer::transfer_to_object_id(child, id);
O1 { id, child }
transfer::transfer_to_object_id(child, &id);
O1 { id }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@ processed 7 tasks
init:
A: object(100), B: object(101)

task 1 'publish'. lines 8-28:
task 1 'publish'. lines 8-27:
created: object(105)
written: object(104)

task 2 'run'. lines 30-30:
task 2 'run'. lines 29-29:
created: object(107)
written: object(106)

task 3 'run'. lines 32-32:
task 3 'run'. lines 31-31:
created: object(109)
written: object(107), object(108)

task 4 'view-object'. lines 34-34:
task 4 'view-object'. lines 33-33:
Owner: Object ID: ( fake(107) )
Contents: test::m::Child {id: sui::id::VersionedID {id: sui::id::UniqueID {id: sui::id::ID {bytes: fake(109)}}, version: 1u64}}

task 5 'transfer-object'. lines 36-36:
task 5 'transfer-object'. lines 35-35:
Error: Transaction Effects Status: Invalid Transfer Object Transaction. Possibly not address-owned or possibly does not have public transfer.
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InvalidTransferObject, source: None } }

task 6 'view-object'. lines 38-38:
task 6 'view-object'. lines 37-37:
Owner: Object ID: ( fake(107) )
Contents: test::m::Child {id: sui::id::VersionedID {id: sui::id::UniqueID {id: sui::id::ID {bytes: fake(109)}}, version: 2u64}}
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,21 @@
//# publish

module test::m {
use sui::transfer::{Self, ChildRef};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
use sui::id::VersionedID;

struct S has key { id: VersionedID, children: vector<ChildRef<Child>> }
struct S has key { id: VersionedID }
struct Child has key { id: VersionedID }

public entry fun mint_s(ctx: &mut TxContext) {
let id = tx_context::new_id(ctx);
transfer::share_object(S { id, children: vector[] })
transfer::share_object(S { id })
}

public entry fun mint_child(s: &mut S, ctx: &mut TxContext) {
let id = tx_context::new_id(ctx);
let child = transfer::transfer_to_object(Child { id }, s);
std::vector::push_back(&mut s.children, child)
transfer::transfer_to_object(Child { id }, s);
}
}

Expand Down
18 changes: 0 additions & 18 deletions crates/sui-adapter/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,15 +484,6 @@ fn process_successful_execution<
// but only to be deleted.
if !newly_generated_ids.contains(obj_id) {
match by_value_objects.remove(id.object_id()) {
Some((Owner::ObjectOwner { .. }, _)) => {
// If an object is owned by another object, we are not allowed to directly delete the child
// object because this could lead to a dangling reference of the ownership. Such
// dangling reference can never be dropped. To delete this object, one must either first transfer
// the child object to an account address, or call through transfer::delete_child_object(),
// which would consume both the child object and the ChildRef ownership reference,
// and emit the DeleteChildObject event. These child objects can be safely deleted.
return Err(ExecutionErrorKind::DeleteObjectOwnedObject.into());
}
Some(_) => {
state_view.log_event(Event::delete_object(
module_id.address(),
Expand Down Expand Up @@ -526,15 +517,6 @@ fn process_successful_execution<
}
Ok(())
}
EventType::DeleteChildObject => {
let id_bytes: AccountAddress = bcs::from_bytes(&event_bytes).unwrap();
let obj_id: ObjectID = id_bytes.into();
// unwrap safe since to delete a child object, this child object
// must be passed by value in the input.
let (_owner, version) = by_value_objects.remove(&obj_id).unwrap();
state_view.delete_object(&obj_id, version, DeleteKind::Normal);
Ok(())
}
EventType::User => {
match type_ {
TypeTag::Struct(s) => state_view.log_event(Event::move_event(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

module object_owner::object_owner {
use std::option::{Self, Option};
use sui::id::{Self, VersionedID};
use sui::transfer::{Self, ChildRef};
use sui::id::{Self, ID, VersionedID};
use sui::transfer;
use sui::tx_context::{Self, TxContext};

struct Parent has key {
id: VersionedID,
child: Option<ChildRef<Child>>,
child: Option<ID>,
}

struct Child has key {
Expand All @@ -18,7 +18,7 @@ module object_owner::object_owner {

struct AnotherParent has key {
id: VersionedID,
child: ChildRef<Child>,
child: ID,
}

public entry fun create_child(ctx: &mut TxContext) {
Expand All @@ -39,17 +39,19 @@ module object_owner::object_owner {
public entry fun create_parent_and_child(ctx: &mut TxContext) {
let parent_id = tx_context::new_id(ctx);
let child = Child { id: tx_context::new_id(ctx) };
let (parent_id, child_ref) = transfer::transfer_to_object_id(child, parent_id);
let child_id = *id::id(&child);
transfer::transfer_to_object_id(child, &parent_id);
let parent = Parent {
id: parent_id,
child: option::some(child_ref),
child: option::some(child_id),
};
transfer::transfer(parent, tx_context::sender(ctx));
}

public entry fun add_child(parent: &mut Parent, child: Child) {
let child_ref = transfer::transfer_to_object(child, parent);
option::fill(&mut parent.child, child_ref);
let child_id = *id::id(&child);
transfer::transfer_to_object(child, parent);
option::fill(&mut parent.child, child_id);
}

// Call to mutate_child will fail if its owned by a parent,
Expand All @@ -60,14 +62,16 @@ module object_owner::object_owner {
public entry fun mutate_child_with_parent(_child: &mut Child, _parent: &mut Parent) {}

public entry fun transfer_child(parent: &mut Parent, child: Child, new_parent: &mut Parent) {
let child_ref = option::extract(&mut parent.child);
let new_child_ref = transfer::transfer_child_to_object(child, child_ref, new_parent);
option::fill(&mut new_parent.child, new_child_ref);
let child_id = option::extract(&mut parent.child);
assert!(id::id(&child) == &child_id, 0);
transfer::transfer_to_object(child, new_parent);
option::fill(&mut new_parent.child, child_id);
}

public entry fun remove_child(parent: &mut Parent, child: Child, ctx: &mut TxContext) {
let child_ref = option::extract(&mut parent.child);
transfer::transfer_child_to_address(child, child_ref, tx_context::sender(ctx));
let child_id = option::extract(&mut parent.child);
assert!(id::id(&child) == &child_id, 0);
transfer::transfer(child, tx_context::sender(ctx));
}

// Call to delete_child can fail if it's still owned by a parent.
Expand All @@ -78,20 +82,21 @@ module object_owner::object_owner {

public entry fun delete_parent_and_child(parent: Parent, child: Child) {
let Parent { id: parent_id, child: child_ref_opt } = parent;
let child_ref = option::extract(&mut child_ref_opt);
option::destroy_none(child_ref_opt);
let child_id = option::extract(&mut child_ref_opt);
assert!(id::id(&child) == &child_id, 0);
id::delete(parent_id);

let Child { id: child_id } = child;
transfer::delete_child_object(child_id, child_ref);
id::delete(child_id);
}

public entry fun create_another_parent(child: Child, ctx: &mut TxContext) {
let id = tx_context::new_id(ctx);
let (id, child_ref) = transfer::transfer_to_object_id(child, id);
let child_id = *id::id(&child);
transfer::transfer_to_object_id(child, &id);
let parent = AnotherParent {
id,
child: child_ref,
child: child_id,
};
transfer::transfer(parent, tx_context::sender(ctx));
}
Expand Down
50 changes: 0 additions & 50 deletions crates/sui-core/src/unit_tests/move_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,57 +399,7 @@ async fn test_object_owning_another_object() {
.await
.unwrap();
// we expect this to be and error due to Deleting an Object Owned Object
assert!(matches!(
effects.status.unwrap_err(),
ExecutionFailureStatus::DeleteObjectOwnedObject,
));

// Remove the child from the parent.
let effects = call_move(
&authority,
&gas,
&sender,
&sender_key,
&package,
"object_owner",
"remove_child",
vec![],
vec![
TestCallArg::Object(new_parent.0),
TestCallArg::Object(child.0),
],
)
.await
.unwrap();
assert!(effects.status.is_ok());
// Removing child from parent results in a transfer event
assert_eq!(effects.events.len(), 1);
let event1 = &effects.events[0];
assert_eq!(event1.event_type(), EventType::TransferObject);
assert_eq!(event1.object_id(), Some(child.0));

// Delete the child again. This time it will succeed because it's no longer owned by a parent.
let effects = call_move(
&authority,
&gas,
&sender,
&sender_key,
&package,
"object_owner",
"delete_child",
vec![],
vec![
TestCallArg::Object(child.0),
TestCallArg::Object(new_parent.0),
],
)
.await
.unwrap();
assert!(effects.status.is_ok());
assert_eq!(effects.events.len(), 1);
let event1 = &effects.events[0];
assert_eq!(event1.event_type(), EventType::DeleteObject);
assert_eq!(event1.object_id(), Some(child.0));

// Create a parent and a child together. This tests the
// transfer::transfer_to_object_id() API.
Expand Down
16 changes: 7 additions & 9 deletions crates/sui-core/tests/staged/sui.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,25 +130,23 @@ ExecutionFailureStatus:
NEWTYPE:
TYPENAME: InvalidSharedByValue
16:
DeleteObjectOwnedObject: UNIT
17:
PublishErrorEmptyPackage: UNIT
18:
17:
PublishErrorNonZeroAddress: UNIT
19:
18:
PublishErrorDuplicateModule: UNIT
20:
19:
SuiMoveVerificationError: UNIT
21:
20:
MovePrimitiveRuntimeError: UNIT
22:
21:
MoveAbort:
TUPLE:
- TYPENAME: ModuleId
- U64
23:
22:
VMVerificationOrDeserializationError: UNIT
24:
23:
VMInvariantViolation: UNIT
ExecutionStatus:
ENUM:
Expand Down
Loading

0 comments on commit c9ba193

Please sign in to comment.