Skip to content

Commit

Permalink
Rename object owner types (MystenLabs#1438)
Browse files Browse the repository at this point in the history
  • Loading branch information
lxfind authored Apr 19, 2022
1 parent c41437d commit 03a4b00
Show file tree
Hide file tree
Showing 21 changed files with 86 additions and 87 deletions.
2 changes: 1 addition & 1 deletion doc/src/build/move.md
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ To make an object `obj` shared and immutable, one can call:
```
Transfer::freeze_object(obj);
```
After this call, `obj` becomes immutable which means it can never be mutated or deleted. This process is also irreversible: once an object is frozen, it will stay frozen forever. An shared immutable object can be used as reference by anyone in their Move call.
After this call, `obj` becomes immutable which means it can never be mutated or deleted. This process is also irreversible: once an object is frozen, it will stay frozen forever. An immutable object can be used as reference by anyone in their Move call.
#### Share an object (experimental)
This feature is still in development. It only works in Move for demo purpose, and doesn't yet work in Sui.
Expand Down
8 changes: 4 additions & 4 deletions doc/src/build/objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ struct A {
defines a object type `A` that contains a field whose type is another object type `B`. In this case, we say an object of type `B` is wrapped into an object of type `A`. With object wrapping, the wrapped object (in this example, object `b`) is not stored as a top-level object in Sui storage, and it's not accessible by object ID. Instead, it's simply part of the serialized bytes content of an object of type `A`. You can think of the case of an object being wrapped similar to being deleted, except its content still exist somewhere in another object.
Now back to the topic of object owned by another object. When an object is owned by another object, it's not wrapped. This means the child object still exists independently as a top-level object and can be accessed directly in the Sui storage. The ownership relationship is only tracked through the owner field of the child object. This can be useful if you still want to observe the child object or be able to use it in other transactions. We provide library APIs to make an object owned by another object. More details on how to do this can be found in [the Move library doc](move.md#sui-move-library)

**Shared Immutable**
This means an object is immutable and cannot be mutated by anyone. Because of this, such an object doesn't have an exclusive owner and hence is considered as shared. Anyone can use it in their Move calls. All Move packages are immutable objects: once published, they cannot be changed. A Move object can be turned into an immutable object through the [*freeze_object*](move.md#sui-move-library) library API. An immutable object can only passed as a read-only reference (`&T`) in Move calls.
**Immutable**
This means an object is immutable and cannot be mutated by anyone. Because of this, such an object doesn't have an exclusive owner. Anyone can use it in their Move calls. All Move packages are immutable objects: once published, they cannot be changed. A Move object can be turned into an immutable object through the [*freeze_object*](move.md#sui-move-library) library API. An immutable object can only passed as a read-only reference (`&T`) in Move calls.

**Shared Mutable (WIP)**
An object can be shared and mutable, meaning that anyone can use and mutate this object. Proper support of this is still being developed in Sui. A more detailed explanation about shared mutable object support will come online soon. Here is a brief primer: for any other object that's not shared mutable, no two transactions can be mutating the same object at the same time since a transaction is pinned on a specific (and must be the latest) version of each object, hence Sui doesn't need to worry about reaching a consensus or ordering (it's ordered by construction). However for shared mutable objects, in order to allow multiple transactions mutating the same object at the same time, we need a sequencer to properly order these transactions. Because of this, using a shared mutable object can be much more expensive in terms of latency, throughput and gas cost. On the other hand, a shared mutable object is also a powerful primitive that allows for expressing richer behavior that doesn't require central trust. Examples of this difference can be found in the two different implementations of TicTacToe in our Move examples.
**Shared (WIP)**
An object can be shared, meaning that anyone can use and mutate this object. Proper support of this is still being developed in Sui. A more detailed explanation about shared object support will come online soon. Here is a brief primer: for any other mutable object that's not shared, no two transactions can be mutating the same object at the same time since a transaction is pinned on a specific (and must be the latest) version of each object, hence Sui doesn't need to worry about reaching a consensus or ordering (it's ordered by construction). However for shared objects, in order to allow multiple transactions mutating the same object at the same time, we need a sequencer to properly order these transactions. Because of this, using a shared object can be much more expensive in terms of latency, throughput and gas cost. On the other hand, a shared object is also a powerful primitive that allows for expressing richer behavior that doesn't require central trust. Examples of this difference can be found in the two different implementations of TicTacToe in our Move examples.

## Referring to objects

Expand Down
4 changes: 2 additions & 2 deletions sui_core/src/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ impl AuthorityState {
let shared_ids: HashSet<_> = inputs
.iter()
.filter_map(|(kind, obj)| match kind {
InputObjectKind::MutSharedMoveObject(..) if obj.owner.is_shared_mutable() => {
InputObjectKind::SharedMoveObject(..) if obj.owner.is_shared() => {
Some((obj.id(), obj.version()))
}
_ => None,
Expand Down Expand Up @@ -465,7 +465,7 @@ impl AuthorityState {
ObjectInfoRequestKind::LatestObjectInfo(request_layout) => {
match self.get_object(&request.object_id).await {
Ok(Some(object)) => {
let lock = if object.is_read_only() {
let lock = if object.is_immutable() {
// Read only objects have no locks.
None
} else {
Expand Down
2 changes: 1 addition & 1 deletion sui_core/src/authority/authority_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ impl<const ALL_OBJ_VER: bool, S: Eq + Serialize + for<'de> Deserialize<'de>>
write_batch = write_batch.insert_batch(
&self.transaction_lock,
written.iter().filter_map(|(_, (object_ref, new_object))| {
if !new_object.is_read_only() {
if !new_object.is_immutable() {
Some((object_ref, None))
} else {
None
Expand Down
14 changes: 6 additions & 8 deletions sui_core/src/authority/temporary_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,13 @@ impl<S> AuthorityTemporaryStore<S> {
.filter_map(|(kind, object)| match kind {
InputObjectKind::MovePackage(_) => None,
InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
if object.is_read_only() {
if object.is_immutable() {
None
} else {
Some(*object_ref)
}
}
InputObjectKind::MutSharedMoveObject(_) => {
Some(object.compute_object_reference())
}
InputObjectKind::SharedMoveObject(_) => Some(object.compute_object_reference()),
})
.collect(),
written: BTreeMap::new(),
Expand Down Expand Up @@ -147,7 +145,7 @@ impl<S> AuthorityTemporaryStore<S> {
object.object_size_for_gas_metering(),
storage_rebate,
)?;
if !object.is_read_only() {
if !object.is_immutable() {
// We don't need to set storage rebate for immutable objects, as they will
// never be deleted.
object.storage_rebate = new_storage_rebate;
Expand Down Expand Up @@ -306,7 +304,7 @@ impl<S> Storage for AuthorityTemporaryStore<S> {
// Check it is not read-only
#[cfg(test)] // Movevm should ensure this
if let Some(existing_object) = self.read_object(&object.id()) {
if existing_object.is_read_only() {
if existing_object.is_immutable() {
// This is an internal invariant violation. Move only allows us to
// mutate objects if they are &mut so they cannot be read-only.
panic!("Internal invariant violation: Mutating a read-only object.")
Expand All @@ -326,7 +324,7 @@ impl<S> Storage for AuthorityTemporaryStore<S> {
// Check it is not read-only
#[cfg(test)] // Movevm should ensure this
if let Some(object) = self.read_object(id) {
if object.is_read_only() {
if object.is_immutable() {
// This is an internal invariant violation. Move only allows us to
// mutate objects if they are &mut so they cannot be read-only.
panic!("Internal invariant violation: Deleting a read-only object.")
Expand Down Expand Up @@ -382,7 +380,7 @@ impl<S> ResourceResolver for AuthorityTemporaryStore<S> {
None => match self.read_object(&ObjectID::from(*address)) {
None => return Ok(None),
Some(x) => {
if !x.is_read_only() {
if !x.is_immutable() {
fp_bail!(SuiError::ExecutionInvariantViolation);
}
x
Expand Down
14 changes: 7 additions & 7 deletions sui_core/src/transaction_input_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ pub async fn check_locks(
// duplicate objects in the same SingleTransactionKind. However for a Batch
// Transaction, we still need to make sure that the same mutable object don't show
// up in more than one SingleTransactionKind.
// TODO: We should be able to allow the same shared mutable object to show up
// TODO: We should be able to allow the same shared object to show up
// in more than one SingleTransactionKind. We need to ensure that their
// version number only increases once at the end of the Batch execution.
let mut owned_object_authenticators: HashSet<SuiAddress> = HashSet::new();
for object in objects.iter().flatten() {
if !object.is_read_only() {
if !object.is_immutable() {
fp_ensure!(
owned_object_authenticators.insert(object.id().into()),
SuiError::InvalidBatchTransaction {
Expand Down Expand Up @@ -97,13 +97,13 @@ pub fn filter_owned_objects(all_objects: &[(InputObjectKind, Object)]) -> Vec<Ob
.filter_map(|(object_kind, object)| match object_kind {
InputObjectKind::MovePackage(_) => None,
InputObjectKind::ImmOrOwnedMoveObject(object_ref) => {
if object.is_read_only() {
if object.is_immutable() {
None
} else {
Some(*object_ref)
}
}
InputObjectKind::MutSharedMoveObject(..) => None,
InputObjectKind::SharedMoveObject(..) => None,
})
.collect();

Expand Down Expand Up @@ -162,7 +162,7 @@ fn check_one_lock(
);

match object.owner {
Owner::SharedImmutable => {
Owner::Immutable => {
// Nothing else to check for SharedImmutable.
}
Owner::AddressOwner(owner) => {
Expand All @@ -187,14 +187,14 @@ fn check_one_lock(
}
);
}
Owner::SharedMutable => {
Owner::Shared => {
// This object is a mutable shared object. However the transaction
// specifies it as an owned object. This is inconsistent.
return Err(SuiError::NotSharedObjectError);
}
};
}
InputObjectKind::MutSharedMoveObject(..) => {
InputObjectKind::SharedMoveObject(..) => {
// When someone locks an object as shared it must be shared already.
fp_ensure!(object.is_shared(), SuiError::NotSharedObjectError);
}
Expand Down
4 changes: 2 additions & 2 deletions sui_core/src/unit_tests/authority_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ async fn test_transfer_package() {
let result = authority_state
.handle_transaction(transfer_transaction.clone())
.await;
assert_eq!(result.unwrap_err(), SuiError::TransferSharedError);
assert_eq!(result.unwrap_err(), SuiError::TransferUnownedError);
}

// This test attempts to use an immutable gas object to pay for gas.
Expand Down Expand Up @@ -1417,7 +1417,7 @@ async fn shared_object() {

let content = GasCoin::new(shared_object_id, SequenceNumber::new(), 10);
let obj = MoveObject::new(/* type */ GasCoin::type_(), content.to_bcs_bytes());
Object::new_move(obj, Owner::SharedMutable, TransactionDigest::genesis())
Object::new_move(obj, Owner::Shared, TransactionDigest::genesis())
};

let authority = init_state_with_objects(vec![gas_object, shared_object]).await;
Expand Down
2 changes: 1 addition & 1 deletion sui_core/src/unit_tests/consensus_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn test_shared_object() -> Object {
let shared_object_id = ObjectID::from_hex_literal(seed).unwrap();
let content = GasCoin::new(shared_object_id, SequenceNumber::new(), 10);
let obj = MoveObject::new(/* type */ GasCoin::type_(), content.to_bcs_bytes());
Object::new_move(obj, Owner::SharedMutable, TransactionDigest::genesis())
Object::new_move(obj, Owner::Shared, TransactionDigest::genesis())
}

/// Fixture: a few test certificates containing a shared object.
Expand Down
4 changes: 2 additions & 2 deletions sui_core/src/unit_tests/gateway_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1045,10 +1045,10 @@ async fn test_move_calls_freeze_object() {
let transferred_obj = client_object(&mut client, new_obj_ref.0).await.1;

// Confirm new owner
assert!(transferred_obj.owner == Owner::SharedImmutable);
assert!(transferred_obj.owner == Owner::Immutable);

// Confirm read only
assert!(transferred_obj.is_read_only());
assert!(transferred_obj.is_immutable());
}

#[tokio::test]
Expand Down
6 changes: 3 additions & 3 deletions sui_core/tests/staged/sui.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,9 @@ Owner:
NEWTYPE:
TYPENAME: SuiAddress
2:
SharedMutable: UNIT
Shared: UNIT
3:
SharedImmutable: UNIT
Immutable: UNIT
PublicKeyBytes:
NEWTYPESTRUCT: BYTES
SequenceNumber:
Expand Down Expand Up @@ -445,7 +445,7 @@ SuiError:
SEQ:
TYPENAME: SuiError
1:
TransferSharedError: UNIT
TransferUnownedError: UNIT
2:
TransferNonCoinError: UNIT
3:
Expand Down
10 changes: 5 additions & 5 deletions sui_programmability/adapter/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,11 +464,11 @@ fn process_successful_execution<
EventType::TransferToAddress => {
Owner::AddressOwner(SuiAddress::try_from(recipient.as_slice()).unwrap())
}
EventType::FreezeObject => Owner::SharedImmutable,
EventType::FreezeObject => Owner::Immutable,
EventType::TransferToObject => {
Owner::ObjectOwner(ObjectID::try_from(recipient.borrow()).unwrap().into())
}
EventType::ShareObject => Owner::SharedMutable,
EventType::ShareObject => Owner::Shared,
_ => unreachable!(),
};
handle_transfer(
Expand Down Expand Up @@ -725,7 +725,7 @@ pub fn resolve_and_type_check(
// check that m.type_ matches the parameter types of the function
let inner_param_type = match &param_type {
SignatureToken::MutableReference(inner_t) => {
if object.is_read_only() {
if object.is_immutable() {
let error = format!(
"Argument {} is expected to be mutable, immutable object found",
idx
Expand All @@ -744,12 +744,12 @@ pub fn resolve_and_type_check(
t @ SignatureToken::Struct(_)
| t @ SignatureToken::StructInstantiation(_, _)
| t @ SignatureToken::TypeParameter(_) => {
if object.is_shared() {
if !object.is_owned() {
// Forbid passing shared (both mutable and immutable) object by value.
// This ensures that shared object cannot be transferred, deleted or wrapped.
return Err(SuiError::TypeError {
error: format!(
"Shared object cannot be passed by-value, found in argument {}",
"Only owned object can be passed by-value, violation found in argument {}",
idx
),
});
Expand Down
8 changes: 4 additions & 4 deletions sui_programmability/adapter/src/unit_tests/adapter_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ fn test_freeze() {
let id1 = storage.get_created_keys().pop().unwrap();
storage.flush();
let obj1 = storage.read_object(&id1).unwrap();
assert!(!obj1.is_read_only());
assert!(!obj1.is_immutable());

// 2. Call freeze_object.
call(
Expand All @@ -519,8 +519,8 @@ fn test_freeze() {
assert_eq!(storage.updated().len(), 1);
storage.flush();
let obj1 = storage.read_object(&id1).unwrap();
assert!(obj1.is_read_only());
assert!(obj1.owner == Owner::SharedImmutable);
assert!(obj1.is_immutable());
assert!(obj1.owner == Owner::Immutable);

// 3. Call transfer again and it should fail.
let pure_args = vec![bcs::to_bytes(&AccountAddress::from(addr1)).unwrap()];
Expand All @@ -537,7 +537,7 @@ fn test_freeze() {
let err = result.unwrap_err();
assert!(err
.to_string()
.contains("Shared object cannot be passed by-value, found in argument 0"));
.contains("Only owned object can be passed by-value, violation found in argument 0"));

// 4. Call set_value (pass as mutable reference) should fail as well.
let obj1 = storage.read_object(&id1).unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
// In TicTacToe, since the game object is owned by the admin, the players are not
// able to directly mutate the gameboard. Hence each marker placement takes
// two transactions.
// In this implementation, we make the game object a shared mutable object.
// In this implementation, we make the game object a shared object.
// Both players have access and can mutate the game object, and hence they
// can place markers directly in one transaction.
// In general, using shared mutable object has an extra cost due to the fact
// In general, using shared object has an extra cost due to the fact
// that Sui needs to sequence the operations that mutate the shared object from
// different transactions. In this case however, since it is expected for players
// to take turns to place the marker, there won't be a significant overhead in practice.
// As we can see, by using shared mutable object, the implementation is much
// As we can see, by using shared object, the implementation is much
// simpler than the other implementation.
module Games::SharedTicTacToe {
use Std::Vector;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ module Games::SharedTicTacToeTests {
player: &address,
scenario: &mut Scenario,
): u8 {
// The gameboard is now a shared mutable object.
// The gameboard is now a shared object.
// Any player can place a mark on it directly.
TestScenario::next_tx(scenario, player);
let status;
Expand Down
2 changes: 1 addition & 1 deletion sui_programmability/framework/sources/Transfer.move
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ module Sui::Transfer {
/// https://github.com/MystenLabs/sui/issues/633
/// https://github.com/MystenLabs/sui/issues/681
/// This API is exposed to demonstrate how we may be able to use it to program
/// Move contracts that use shared mutable objects.
/// Move contracts that use shared objects.
public native fun share_object<T: key>(obj: T);

native fun transfer_internal<T: key>(obj: T, recipient: address, to_object: bool);
Expand Down
2 changes: 1 addition & 1 deletion sui_programmability/framework/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub enum EventType {
TransferToObject,
/// System event: freeze object
FreezeObject,
/// System event: turn an object into a shared mutable object
/// System event: turn an object into a shared object
ShareObject,
/// System event: an object ID is deleted. This does not necessarily
/// mean an object is being deleted. However whenever an object is being
Expand Down
Loading

0 comments on commit 03a4b00

Please sign in to comment.