diff --git a/crates/sui-core/tests/staged/sui.yaml b/crates/sui-core/tests/staged/sui.yaml index a81156be9eb4e..310e77740ccb6 100644 --- a/crates/sui-core/tests/staged/sui.yaml +++ b/crates/sui-core/tests/staged/sui.yaml @@ -355,6 +355,7 @@ ObjectArg: TYPENAME: ObjectID - initial_shared_version: TYPENAME: SequenceNumber + - mutable: BOOL ObjectDigest: NEWTYPESTRUCT: BYTES ObjectFormatOptions: diff --git a/crates/sui-types/src/messages.rs b/crates/sui-types/src/messages.rs index 7469f6b9b24c7..264c1baaba6f5 100644 --- a/crates/sui-types/src/messages.rs +++ b/crates/sui-types/src/messages.rs @@ -86,16 +86,10 @@ pub enum ObjectArg { SharedObject { id: ObjectID, initial_shared_version: SequenceNumber, - // Temporary fix until SDK will be aware of mutable flag - #[serde(skip, default = "bool_true")] mutable: bool, }, } -fn bool_true() -> bool { - true -} - #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] pub struct TransferObject { pub recipient: SuiAddress, diff --git a/crates/sui/tests/shared_objects_tests.rs b/crates/sui/tests/shared_objects_tests.rs index 015ee2abf85b2..16374d183cbde 100644 --- a/crates/sui/tests/shared_objects_tests.rs +++ b/crates/sui/tests/shared_objects_tests.rs @@ -4,7 +4,8 @@ use futures::{stream, StreamExt}; use sui_core::authority_client::AuthorityAPI; use sui_types::messages::{ - CallArg, ExecutionStatus, ObjectArg, ObjectInfoRequest, ObjectInfoRequestKind, + CallArg, EntryArgumentError, EntryArgumentErrorKind, ExecutionFailureStatus, ExecutionStatus, + ObjectArg, ObjectInfoRequest, ObjectInfoRequestKind, }; use test_utils::authority::get_client; use test_utils::transaction::{ @@ -86,8 +87,7 @@ async fn call_shared_object_contract() { ); let effects = submit_single_owner_transaction(transaction, configs.validator_set()).await; assert!(matches!(effects.status, ExecutionStatus::Success { .. })); - // todo(RWLock) uncomment when serialization of mutable field is fixed - // let counter_creation_transaction = effects.transaction_digest; + let counter_creation_transaction = effects.transaction_digest; let ((counter_id, counter_initial_shared_version, _), _) = effects.created[0]; let counter_object_arg = ObjectArg::SharedObject { id: counter_id, @@ -120,9 +120,8 @@ async fn call_shared_object_contract() { // Only gas object transaction and counter creation are dependencies // Note that this assert would fail for second transaction // if they send counter_object_arg instead of counter_object_arg_imm - // todo(RWLock) uncomment when serialization of mutable field is fixed - // assert_eq!(effects.dependencies.len(), 2); - // assert!(effects.dependencies.contains(&counter_creation_transaction)); + assert_eq!(effects.dependencies.len(), 2); + assert!(effects.dependencies.contains(&counter_creation_transaction)); } // Make a transaction to increment the counter. @@ -136,19 +135,16 @@ async fn call_shared_object_contract() { let effects = submit_shared_object_transaction(transaction, configs.validator_set()) .await .unwrap(); - // todo(RWLock) uncomment when serialization of mutable field is fixed - // let increment_transaction = effects.transaction_digest; + let increment_transaction = effects.transaction_digest; assert!(matches!(effects.status, ExecutionStatus::Success { .. })); // Again - only gas object transaction and counter creation are dependencies // Previously executed assert_value transaction(s) are not a dependency because they took immutable reference to shared object - // todo(RWLock) uncomment when serialization of mutable field is fixed - // assert_eq!(effects.dependencies.len(), 2); - // assert!(effects.dependencies.contains(&counter_creation_transaction)); + assert_eq!(effects.dependencies.len(), 2); + assert!(effects.dependencies.contains(&counter_creation_transaction)); // assert_value can take both mutable and immutable references // it is allowed to pass mutable shared object arg to move call taking immutable reference - // todo(RWLock) uncomment when serialization of mutable field is fixed - // let mut assert_value_mut_transaction = None; + let mut assert_value_mut_transaction = None; for imm in [true, false] { // Ensure the value of the counter is `1`. let transaction = move_transaction( @@ -170,39 +166,36 @@ async fn call_shared_object_contract() { .unwrap(); assert!(matches!(effects.status, ExecutionStatus::Success { .. })); // Gas object transaction and increment transaction are dependencies - // todo(RWLock) uncomment when serialization of mutable field is fixed - // assert_eq!(effects.dependencies.len(), 2); - // assert!(effects.dependencies.contains(&increment_transaction)); - // assert_value_mut_transaction = Some(effects.transaction_digest); + assert_eq!(effects.dependencies.len(), 2); + assert!(effects.dependencies.contains(&increment_transaction)); + assert_value_mut_transaction = Some(effects.transaction_digest); } - // todo(RWLock) uncomment when serialization of mutable field is fixed - // let assert_value_mut_transaction = assert_value_mut_transaction.unwrap(); + let assert_value_mut_transaction = assert_value_mut_transaction.unwrap(); // And last check - attempt to send increment transaction with immutable reference - // todo(RWLock) uncomment when serialization of mutable field is fixed - // let transaction = move_transaction( - // gas_objects.pop().unwrap(), - // "counter", - // "increment", - // package_id, - // vec![CallArg::Object(counter_object_arg_imm)], - // ); - // let effects = submit_shared_object_transaction(transaction, configs.validator_set()) - // .await - // .unwrap(); - // // Transaction fails - // assert!(matches!( - // effects.status, - // ExecutionStatus::Failure { - // error: ExecutionFailureStatus::EntryArgumentError(EntryArgumentError { - // kind: EntryArgumentErrorKind::ObjectMutabilityMismatch, - // .. - // }) - // } - // )); - // assert_eq!(effects.dependencies.len(), 2); - // assert!(effects.dependencies.contains(&assert_value_mut_transaction)); + let transaction = move_transaction( + gas_objects.pop().unwrap(), + "counter", + "increment", + package_id, + vec![CallArg::Object(counter_object_arg_imm)], + ); + let effects = submit_shared_object_transaction(transaction, configs.validator_set()) + .await + .unwrap(); + // Transaction fails + assert!(matches!( + effects.status, + ExecutionStatus::Failure { + error: ExecutionFailureStatus::EntryArgumentError(EntryArgumentError { + kind: EntryArgumentErrorKind::ObjectMutabilityMismatch, + .. + }) + } + )); + assert_eq!(effects.dependencies.len(), 2); + assert!(effects.dependencies.contains(&assert_value_mut_transaction)); } /// Same test as `call_shared_object_contract` but the clients submits many times the same diff --git a/sdk/typescript/src/signers/txn-data-serializers/call-arg-serializer.ts b/sdk/typescript/src/signers/txn-data-serializers/call-arg-serializer.ts index 12e7407e19091..d1dd3e50fad1c 100644 --- a/sdk/typescript/src/signers/txn-data-serializers/call-arg-serializer.ts +++ b/sdk/typescript/src/signers/txn-data-serializers/call-arg-serializer.ts @@ -148,10 +148,17 @@ export class CallArgSerializer { async newObjectArg(objectId: string): Promise { const object = await this.provider.getObject(objectId); const initialSharedVersion = getSharedObjectInitialVersion(object); + + const mutable = true; // Defaulted to True to match current behavior. + const api = await this.provider.getRpcApiVersion(); + if (initialSharedVersion) { - return { Shared: { objectId, initialSharedVersion } }; + const object_args = + api?.major === 0 && api?.minor < 25 + ? { Shared: { objectId, initialSharedVersion } } + : { Shared: { objectId, initialSharedVersion, mutable } }; + return object_args; } - return { ImmOrOwned: getObjectReference(object)! }; } diff --git a/sdk/typescript/src/types/sui-bcs.ts b/sdk/typescript/src/types/sui-bcs.ts index 06e44213eceb9..a1dc457887317 100644 --- a/sdk/typescript/src/types/sui-bcs.ts +++ b/sdk/typescript/src/types/sui-bcs.ts @@ -132,6 +132,20 @@ export type SharedObjectRef = { /** The version the object was shared at */ initialSharedVersion: number; + + /** Whether reference is mutable */ + mutable: boolean; +}; + +/** + * A reference to a shared object from 0.23.0. + */ +export type SharedObjectRef_23 = { + /** Hex code as string representing the object id */ + objectId: string; + + /** The version the object was shared at */ + initialSharedVersion: number; }; /** @@ -139,7 +153,7 @@ export type SharedObjectRef = { */ export type ObjectArg = | { ImmOrOwned: SuiObjectRef } - | { Shared: SharedObjectRef }; + | { Shared: SharedObjectRef | SharedObjectRef_23 }; /** * A pure argument. @@ -329,6 +343,7 @@ const BCS_SPEC = { struct: { objectId: 'address', initialSharedVersion: 'u64', + mutable: 'bool', }, }, @@ -431,6 +446,22 @@ const BCS_0_23_SPEC = { arguments: 'vector', }, }, + SharedObjectRef: { + struct: { + objectId: 'address', + initialSharedVersion: 'u64', + }, + }, +}; + +const BCS_0_24_SPEC = { + ...BCS_SPEC, + SharedObjectRef: { + struct: { + objectId: 'address', + initialSharedVersion: 'u64', + }, + }, }; const bcs = new BCS(getSuiMoveConfig()); @@ -444,9 +475,17 @@ registerUTF8String(bcs_0_23); registerObjectDigest(bcs_0_23); registerTypes(bcs_0_23, BCS_0_23_SPEC); +const bcs_0_24 = new BCS(getSuiMoveConfig()); +registerUTF8String(bcs_0_24); +registerObjectDigest(bcs_0_24); +registerTypes(bcs_0_24, BCS_0_24_SPEC); + export function bcsForVersion(v?: RpcApiVersion) { if (v?.major === 0 && v?.minor < 24) { return bcs_0_23; + } + if (v?.major === 0 && v?.minor === 24) { + return bcs_0_24; } else { return bcs; }