diff --git a/crates/sui-core/src/generate_format.rs b/crates/sui-core/src/generate_format.rs index d48539ab14898..84a316a7d4886 100644 --- a/crates/sui-core/src/generate_format.rs +++ b/crates/sui-core/src/generate_format.rs @@ -13,6 +13,7 @@ use rand::SeedableRng; use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; use shared_crypto::intent::{Intent, IntentMessage, PersonalMessage}; use std::{fs::File, io::Write}; +use sui_types::effects::{IDOperation, ObjectIn, ObjectOut, TransactionEffects}; use sui_types::execution_status::{ CommandArgumentError, ExecutionFailureStatus, ExecutionStatus, PackageUpgradeError, TypeArgumentError, @@ -159,6 +160,11 @@ fn get_registry() -> Result { tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; + // uncomment once GenericSignature is added tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; diff --git a/crates/sui-core/src/unit_tests/move_package_upgrade_tests.rs b/crates/sui-core/src/unit_tests/move_package_upgrade_tests.rs index 9a3d9483a719d..09dfb58cc7890 100644 --- a/crates/sui-core/src/unit_tests/move_package_upgrade_tests.rs +++ b/crates/sui-core/src/unit_tests/move_package_upgrade_tests.rs @@ -418,7 +418,7 @@ async fn test_upgrade_package_incorrect_digest() { async fn test_upgrade_package_compatibility_too_permissive() { let mut runner = UpgradeStateRunner::new("move_upgrade/base").await; - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let cap = builder diff --git a/crates/sui-core/src/unit_tests/transfer_to_object_tests.rs b/crates/sui-core/src/unit_tests/transfer_to_object_tests.rs index 9cbccb017145a..745dbea520dc6 100644 --- a/crates/sui-core/src/unit_tests/transfer_to_object_tests.rs +++ b/crates/sui-core/src/unit_tests/transfer_to_object_tests.rs @@ -40,7 +40,7 @@ use move_core_types::ident_str; // combinations of that. Some of these tests also check and validate locking behavior around // receiving object arguments as well. -// Run the test twice -- once with agressive pruning enabled, and the other with it not enabled. +// Run the test twice -- once with aggressive pruning enabled, and the other with it not enabled. macro_rules! transfer_test_runner { (gas_objects: $num:expr, $expr:expr) => { let runner = TestRunner::new_with_objects("tto", $num, false).await; @@ -150,14 +150,12 @@ impl TestRunner { .unwrap(); } - let TransactionEffects::V1(fx) = &effects; - - if let Some(updated_cap) = fx + if let Some(updated_cap) = effects .mutated() - .iter() + .into_iter() .find_map(|(cap, _)| (cap.0 == self.upgrade_cap.0).then_some(cap)) { - self.upgrade_cap = *updated_cap; + self.upgrade_cap = updated_cap; } effects @@ -187,14 +185,12 @@ impl TestRunner { .unwrap(); } - let TransactionEffects::V1(fx) = &effects; - - if let Some(updated_cap) = fx + if let Some(updated_cap) = effects .mutated() - .iter() + .into_iter() .find_map(|(cap, _)| (cap.0 == self.upgrade_cap.0).then_some(cap)) { - self.upgrade_cap = *updated_cap; + self.upgrade_cap = updated_cap; } effects @@ -279,7 +275,7 @@ fn get_parent_and_child( #[tokio::test] async fn test_tto_transfer() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -294,7 +290,7 @@ async fn test_tto_transfer() { let transfer_digest = effects.transaction_digest(); // No receive the sent object - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -336,7 +332,7 @@ async fn test_tto_transfer() { #[tokio::test] async fn test_tto_intersection_input_and_receiving_objects() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -395,7 +391,7 @@ async fn test_tto_intersection_input_and_receiving_objects() { #[tokio::test] async fn test_tto_invalid_receiving_arguments() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -504,7 +500,7 @@ async fn test_tto_invalid_receiving_arguments() { #[tokio::test] async fn test_tto_unused_receiver() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -517,7 +513,7 @@ async fn test_tto_unused_receiver() { let (parent, child) = get_parent_and_child(effects.created()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -560,7 +556,7 @@ async fn test_tto_unused_receiver() { #[tokio::test] async fn test_tto_pass_receiving_by_refs() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -573,7 +569,7 @@ async fn test_tto_pass_receiving_by_refs() { let (parent, child) = get_parent_and_child(effects.created()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -624,7 +620,7 @@ async fn test_tto_pass_receiving_by_refs() { #[tokio::test] async fn test_tto_delete() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -637,7 +633,7 @@ async fn test_tto_delete() { let (parent, child) = get_parent_and_child(effects.created()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -676,7 +672,7 @@ async fn test_tto_delete() { #[tokio::test] async fn test_tto_wrap() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -689,7 +685,7 @@ async fn test_tto_wrap() { let (parent, child) = get_parent_and_child(effects.created()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -729,7 +725,7 @@ async fn test_tto_wrap() { #[tokio::test] async fn test_tto_unwrap_transfer() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -743,7 +739,7 @@ async fn test_tto_unwrap_transfer() { let (parent, child) = get_parent_and_child(effects.created()); // No receive the sent object - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -791,7 +787,7 @@ async fn test_tto_unwrap_transfer() { #[tokio::test] async fn test_tto_unwrap_delete() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -805,7 +801,7 @@ async fn test_tto_unwrap_delete() { let (parent, child) = get_parent_and_child(effects.created()); // No receive the sent object - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -847,7 +843,7 @@ async fn test_tto_unwrap_delete() { #[tokio::test] async fn test_tto_unwrap_add_as_dynamic_field() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -861,7 +857,7 @@ async fn test_tto_unwrap_add_as_dynamic_field() { let (parent, child) = get_parent_and_child(effects.created()); // No receive the sent object - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -918,7 +914,7 @@ async fn verify_tto_not_locked( aggressive_pruning: bool, ) -> (TransactionEffects, TransactionEffects) { let mut runner = TestRunner::new_with_objects("tto", 2, aggressive_pruning).await; - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1050,7 +1046,7 @@ async fn test_tto_not_locked() { #[tokio::test] async fn test_tto_valid_dependencies() { transfer_test_runner! {gas_objects: 3, |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1062,7 +1058,7 @@ async fn test_tto_valid_dependencies() { .await; let parent = effects.created()[0]; - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1079,7 +1075,7 @@ async fn test_tto_valid_dependencies() { // object solely because of the fact that we received it in this transaction. // 2. Since the gas coin is fresh it will have a smaller version, so this will test that we // properly compute and update the lamport version that we should use for the transaction. - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1100,7 +1096,7 @@ async fn test_tto_valid_dependencies() { let transfer_digest = effects.transaction_digest(); // No receive the sent object - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1149,7 +1145,7 @@ async fn test_tto_valid_dependencies() { #[tokio::test] async fn test_tto_valid_dependencies_delete_on_receive() { transfer_test_runner! {gas_objects: 3, |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1161,7 +1157,7 @@ async fn test_tto_valid_dependencies_delete_on_receive() { .await; let parent = effects.created()[0]; - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1178,7 +1174,7 @@ async fn test_tto_valid_dependencies_delete_on_receive() { // object solely because of the fact that we received it in this transaction. // 2. Since the gas coin is fresh it will have a smaller version, so this will test that we // properly compute and update the lamport version that we should use for the transaction. - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1199,7 +1195,7 @@ async fn test_tto_valid_dependencies_delete_on_receive() { let transfer_digest = effects.transaction_digest(); // No receive and delete the sent object - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1244,7 +1240,7 @@ async fn test_tto_valid_dependencies_delete_on_receive() { #[tokio::test] async fn test_tto_dependencies_dont_receive() { transfer_test_runner! {gas_objects: 3, |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1256,7 +1252,7 @@ async fn test_tto_dependencies_dont_receive() { .await; let parent = effects.created()[0]; - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1273,7 +1269,7 @@ async fn test_tto_dependencies_dont_receive() { // object solely because of the fact that we received it in this transaction. // 2. Since the gas coin is fresh it will have a smaller version, so this will test that we // properly compute and update the lamport version that we should use for the transaction. - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1298,7 +1294,7 @@ async fn test_tto_dependencies_dont_receive() { assert!(parent.0 .1.value() < child.0 .1.value()); // Now dont receive the sent object but include it in the arguments for the PTB. - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1341,7 +1337,7 @@ async fn test_tto_dependencies_dont_receive() { #[tokio::test] async fn test_tto_dependencies_dont_receive_but_abort() { transfer_test_runner! {gas_objects: 3, |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1353,7 +1349,7 @@ async fn test_tto_dependencies_dont_receive_but_abort() { .await; let parent = effects.created()[0]; - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1370,7 +1366,7 @@ async fn test_tto_dependencies_dont_receive_but_abort() { // object solely because of the fact that we received it in this transaction. // 2. Since the gas coin is fresh it will have a smaller version, so this will test that we // properly compute and update the lamport version that we should use for the transaction. - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1392,7 +1388,7 @@ async fn test_tto_dependencies_dont_receive_but_abort() { assert!(parent.0 .1.value() < child.0 .1.value()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1436,7 +1432,7 @@ async fn test_tto_dependencies_dont_receive_but_abort() { #[tokio::test] async fn test_tto_dependencies_receive_and_abort() { transfer_test_runner! {gas_objects: 3, |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1448,7 +1444,7 @@ async fn test_tto_dependencies_receive_and_abort() { .await; let parent = effects.created()[0]; - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1465,7 +1461,7 @@ async fn test_tto_dependencies_receive_and_abort() { // object solely because of the fact that we received it in this transaction. // 2. Since the gas coin is fresh it will have a smaller version, so this will test that we // properly compute and update the lamport version that we should use for the transaction. - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1487,7 +1483,7 @@ async fn test_tto_dependencies_receive_and_abort() { assert!(parent.0 .1.value() < child.0 .1.value()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1531,7 +1527,7 @@ async fn test_tto_dependencies_receive_and_abort() { async fn receive_and_dof_interleave() { transfer_test_runner! {gas_objects: 3, |mut runner: TestRunner| async move { // step 1 & 2 - let TransactionEffects::V1(effects) = runner + let effects = runner .run_with_gas_object( { let mut builder = ProgrammableTransactionBuilder::new(); @@ -1617,7 +1613,7 @@ async fn receive_and_dof_interleave() { #[tokio::test] async fn test_have_deleted_owned_object() { transfer_test_runner! { |mut runner: TestRunner| async move { - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); move_call! { @@ -1630,7 +1626,7 @@ async fn test_have_deleted_owned_object() { let (parent, child) = get_parent_and_child(effects.created()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(parent.0)).unwrap(); @@ -1650,7 +1646,7 @@ async fn test_have_deleted_owned_object() { assert!(!runner.authority_state.database.have_deleted_owned_object_at_version_or_after(&new_child.0.0, new_child.0.1, 0).unwrap()); assert!(!runner.authority_state.database.have_deleted_owned_object_at_version_or_after(&new_child.0.0, child.0.1, 0).unwrap()); - let TransactionEffects::V1(effects) = runner + let effects = runner .run({ let mut builder = ProgrammableTransactionBuilder::new(); let parent = builder.obj(ObjectArg::ImmOrOwnedObject(new_parent.0)).unwrap(); diff --git a/crates/sui-core/tests/staged/sui.yaml b/crates/sui-core/tests/staged/sui.yaml index 0e150be9343fc..7d3bc813822f6 100644 --- a/crates/sui-core/tests/staged/sui.yaml +++ b/crates/sui-core/tests/staged/sui.yaml @@ -253,6 +253,17 @@ ECMHLiveObjectSetDigest: STRUCT: - digest: TYPENAME: Digest +EffectsAuxDataDigest: + NEWTYPESTRUCT: + TYPENAME: Digest +EffectsObjectChange: + STRUCT: + - input_state: + TYPENAME: ObjectIn + - output_state: + TYPENAME: ObjectOut + - id_operation: + TYPENAME: IDOperation EmptySignInfo: STRUCT: [] EndOfEpochData: @@ -452,6 +463,14 @@ GenesisTransaction: - objects: SEQ: TYPENAME: GenesisObject +IDOperation: + ENUM: + 0: + None: UNIT + 1: + Created: UNIT + 2: + Deleted: UNIT Identifier: NEWTYPESTRUCT: STR Intent: @@ -637,6 +656,18 @@ ObjectDigest: ObjectID: NEWTYPESTRUCT: TYPENAME: AccountAddress +ObjectIn: + ENUM: + 0: + NotExist: UNIT + 1: + Exist: + NEWTYPE: + TUPLE: + - TUPLE: + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - TYPENAME: Owner ObjectInfoRequestKind: ENUM: 0: @@ -645,6 +676,22 @@ ObjectInfoRequestKind: PastObjectInfoDebug: NEWTYPE: TYPENAME: SequenceNumber +ObjectOut: + ENUM: + 0: + NotExist: UNIT + 1: + ObjectWrite: + NEWTYPE: + TUPLE: + - TYPENAME: ObjectDigest + - TYPENAME: Owner + 2: + PackageWrite: + NEWTYPE: + TUPLE: + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest Owner: ENUM: 0: @@ -790,6 +837,10 @@ TransactionEffects: V1: NEWTYPE: TYPENAME: TransactionEffectsV1 + 1: + V2: + NEWTYPE: + TYPENAME: TransactionEffectsV2 TransactionEffectsDigest: NEWTYPESTRUCT: TYPENAME: Digest @@ -868,6 +919,38 @@ TransactionEffectsV1: - dependencies: SEQ: TYPENAME: TransactionDigest +TransactionEffectsV2: + STRUCT: + - status: + TYPENAME: ExecutionStatus + - executed_epoch: U64 + - gas_used: + TYPENAME: GasCostSummary + - transaction_digest: + TYPENAME: TransactionDigest + - gas_object_index: + OPTION: U32 + - events_digest: + OPTION: + TYPENAME: TransactionEventsDigest + - dependencies: + SEQ: + TYPENAME: TransactionDigest + - lamport_version: + TYPENAME: SequenceNumber + - changed_objects: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: EffectsObjectChange + - unchanged_shared_objects: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: UnchangedSharedKind + - aux_data_digest: + OPTION: + TYPENAME: EffectsAuxDataDigest TransactionEventsDigest: NEWTYPESTRUCT: TYPENAME: Digest @@ -962,6 +1045,14 @@ TypedStoreError: MetricsReporting: UNIT 5: RetryableTransactionError: UNIT +UnchangedSharedKind: + ENUM: + 0: + ReadOnlyRoot: + NEWTYPE: + TUPLE: + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest UpgradeInfo: STRUCT: - upgraded_id: diff --git a/crates/sui-json-rpc-types/src/sui_transaction.rs b/crates/sui-json-rpc-types/src/sui_transaction.rs index 406158f3e37ab..c18c1646f2216 100644 --- a/crates/sui-json-rpc-types/src/sui_transaction.rs +++ b/crates/sui-json-rpc-types/src/sui_transaction.rs @@ -638,56 +638,43 @@ impl TryFrom for SuiTransactionBlockEffects { type Error = SuiError; fn try_from(effect: TransactionEffects) -> Result { - let message_version = effect - .message_version() - .expect("TransactionEffects defines message_version()"); - - match message_version { - 1 => Ok(SuiTransactionBlockEffects::V1( - SuiTransactionBlockEffectsV1 { - status: effect.status().clone().into(), - executed_epoch: effect.executed_epoch(), - modified_at_versions: effect - .modified_at_versions() + Ok(SuiTransactionBlockEffects::V1( + SuiTransactionBlockEffectsV1 { + status: effect.status().clone().into(), + executed_epoch: effect.executed_epoch(), + modified_at_versions: effect + .modified_at_versions() + .into_iter() + .map(|(object_id, sequence_number)| { + SuiTransactionBlockEffectsModifiedAtVersions { + object_id, + sequence_number, + } + }) + .collect(), + gas_used: effect.gas_cost_summary().clone(), + shared_objects: to_sui_object_ref( + effect + .input_shared_objects() .into_iter() - .map(|(object_id, sequence_number)| { - SuiTransactionBlockEffectsModifiedAtVersions { - object_id, - sequence_number, - } - }) + .map(|(obj_ref, _)| obj_ref) .collect(), - gas_used: effect.gas_cost_summary().clone(), - shared_objects: to_sui_object_ref( - effect - .input_shared_objects() - .into_iter() - .map(|(obj_ref, _)| obj_ref) - .collect(), - ), - transaction_digest: *effect.transaction_digest(), - created: to_owned_ref(effect.created()), - mutated: to_owned_ref(effect.mutated().to_vec()), - unwrapped: to_owned_ref(effect.unwrapped().to_vec()), - deleted: to_sui_object_ref(effect.deleted().to_vec()), - unwrapped_then_deleted: to_sui_object_ref( - effect.unwrapped_then_deleted().to_vec(), - ), - wrapped: to_sui_object_ref(effect.wrapped().to_vec()), - gas_object: OwnedObjectRef { - owner: effect.gas_object().1, - reference: effect.gas_object().0.into(), - }, - events_digest: effect.events_digest().copied(), - dependencies: effect.dependencies().to_vec(), + ), + transaction_digest: *effect.transaction_digest(), + created: to_owned_ref(effect.created()), + mutated: to_owned_ref(effect.mutated().to_vec()), + unwrapped: to_owned_ref(effect.unwrapped().to_vec()), + deleted: to_sui_object_ref(effect.deleted().to_vec()), + unwrapped_then_deleted: to_sui_object_ref(effect.unwrapped_then_deleted().to_vec()), + wrapped: to_sui_object_ref(effect.wrapped().to_vec()), + gas_object: OwnedObjectRef { + owner: effect.gas_object().1, + reference: effect.gas_object().0.into(), }, - )), - - _ => Err(SuiError::UnexpectedVersion(format!( - "Support for TransactionEffects version {} not implemented", - message_version - ))), - } + events_digest: effect.events_digest().copied(), + dependencies: effect.dependencies().to_vec(), + }, + )) } } diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 7624c6e52ff23..ba3d025992106 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1358,6 +1358,7 @@ "disable_invariant_violation_check_in_swap_loc": false, "disallow_adding_abilities_on_upgrade": false, "disallow_change_struct_type_params_on_upgrade": false, + "enable_effects_v2": false, "enable_jwk_consensus_updates": false, "end_of_epoch_transaction_supported": false, "loaded_child_object_format": false, diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index c5beaaa26f3e1..58d89b216e164 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -292,6 +292,9 @@ struct FeatureFlags { // Enable receiving sent objects #[serde(skip_serializing_if = "is_false")] receive_objects: bool, + + #[serde(skip_serializing_if = "is_false")] + enable_effects_v2: bool, } fn is_false(b: &bool) -> bool { @@ -945,6 +948,10 @@ impl ProtocolConfig { pub fn create_authenticator_state_in_genesis(&self) -> bool { self.enable_jwk_consensus_updates() } + + pub fn enable_effects_v2(&self) -> bool { + self.feature_flags.enable_effects_v2 + } } #[cfg(not(msim))] @@ -1514,6 +1521,10 @@ impl ProtocolConfig { cfg.check_zklogin_id_cost_base = Some(200); // zklogin::check_zklogin_issuer cfg.check_zklogin_issuer_cost_base = Some(200); + // Only enable effects v2 on devnet. + if chain != Chain::Mainnet && chain != Chain::Testnet { + cfg.feature_flags.enable_effects_v2 = true; + } } // Use this template when making changes: // diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_28.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_28.snap index e1cf335e1c85f..361d906b89f10 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_28.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_28.snap @@ -35,6 +35,7 @@ feature_flags: simple_conservation_checks: true loaded_child_object_format_type: true receive_objects: true + enable_effects_v2: true max_tx_size_bytes: 131072 max_input_objects: 2048 max_size_written_objects: 5000000 diff --git a/crates/sui-types/src/digests.rs b/crates/sui-types/src/digests.rs index 57a6fc6490a61..fef2f140ea615 100644 --- a/crates/sui-types/src/digests.rs +++ b/crates/sui-types/src/digests.rs @@ -693,6 +693,60 @@ impl std::str::FromStr for TransactionEventsDigest { } } +#[serde_as] +#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] +pub struct EffectsAuxDataDigest(Digest); + +impl EffectsAuxDataDigest { + pub const ZERO: Self = Self(Digest::ZERO); + + pub const fn new(digest: [u8; 32]) -> Self { + Self(Digest::new(digest)) + } + + pub fn random() -> Self { + Self(Digest::random()) + } + + pub fn next_lexicographical(&self) -> Option { + self.0.next_lexicographical().map(Self) + } + + pub fn into_inner(self) -> [u8; 32] { + self.0.into_inner() + } +} + +impl fmt::Debug for EffectsAuxDataDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("EffectsAuxDataDigest") + .field(&self.0) + .finish() + } +} + +impl AsRef<[u8]> for EffectsAuxDataDigest { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8; 32]> for EffectsAuxDataDigest { + fn as_ref(&self) -> &[u8; 32] { + self.0.as_ref() + } +} + +impl std::str::FromStr for EffectsAuxDataDigest { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let mut result = [0; 32]; + result.copy_from_slice(&Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?); + Ok(Self::new(result)) + } +} + // Each object has a unique digest #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] pub struct ObjectDigest(Digest); diff --git a/crates/sui-types/src/effects/effects_v1.rs b/crates/sui-types/src/effects/effects_v1.rs index e982254b32f13..6165f8b90809f 100644 --- a/crates/sui-types/src/effects/effects_v1.rs +++ b/crates/sui-types/src/effects/effects_v1.rs @@ -8,9 +8,7 @@ use crate::digests::TransactionEventsDigest; use crate::effects::{InputSharedObjectKind, TransactionEffectsAPI}; use crate::execution_status::ExecutionStatus; use crate::gas::GasCostSummary; -use crate::message_envelope::Message; use crate::object::Owner; -use crate::transaction::SenderSignedData; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::fmt::{Display, Formatter, Write}; @@ -93,22 +91,6 @@ impl TransactionEffectsV1 { dependencies, } } - - pub fn new_with_tx_and_gas(tx: &SenderSignedData, gas_object: (ObjectRef, Owner)) -> Self { - Self { - transaction_digest: tx.digest(), - gas_object, - ..Default::default() - } - } - - pub fn new_with_tx_and_status(tx: &SenderSignedData, status: ExecutionStatus) -> Self { - Self { - transaction_digest: tx.digest(), - status, - ..Default::default() - } - } } impl TransactionEffectsAPI for TransactionEffectsV1 { @@ -121,6 +103,9 @@ impl TransactionEffectsAPI for TransactionEffectsV1 { fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> { self.modified_at_versions.clone() } + fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)> { + unimplemented!("Only supposed by v2 and above"); + } fn input_shared_objects(&self) -> Vec<(ObjectRef, InputSharedObjectKind)> { let modified: HashSet<_> = self.modified_at_versions.iter().map(|(r, _)| r).collect(); diff --git a/crates/sui-types/src/effects/effects_v2.rs b/crates/sui-types/src/effects/effects_v2.rs index 24c0f7b48d613..3eb5f6926175a 100644 --- a/crates/sui-types/src/effects/effects_v2.rs +++ b/crates/sui-types/src/effects/effects_v2.rs @@ -4,16 +4,18 @@ use super::object_change::{IDOperation, ObjectIn, ObjectOut}; use super::EffectsObjectChange; use crate::base_types::{ - EpochId, ObjectDigest, ObjectRef, SuiAddress, TransactionDigest, VersionDigest, + EpochId, ObjectDigest, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest, + VersionDigest, }; -use crate::digests::{Digest, TransactionEventsDigest}; +use crate::digests::{EffectsAuxDataDigest, TransactionEventsDigest}; use crate::effects::{InputSharedObjectKind, TransactionEffectsAPI}; use crate::execution_status::ExecutionStatus; use crate::gas::GasCostSummary; +use crate::message_envelope::Message; use crate::object::{Owner, OBJECT_START_VERSION}; -use crate::{ObjectID, SequenceNumber}; +use crate::transaction::SenderSignedData; use serde::{Deserialize, Serialize}; - +use std::collections::BTreeMap; #[cfg(debug_assertions)] use std::collections::HashSet; @@ -29,7 +31,8 @@ pub struct TransactionEffectsV2 { transaction_digest: TransactionDigest, /// The updated gas object reference, as an index into the `changed_objects` vector. /// Having a dedicated field for convenient access. - gas_object_index: u16, + /// System transaction that don't require gas will leave this as None. + gas_object_index: Option, /// The digest of the events emitted during execution, /// can be None if the transaction does not emit any event. events_digest: Option, @@ -48,7 +51,7 @@ pub struct TransactionEffectsV2 { /// Auxiliary data that are not protocol-critical, generated as part of the effects but are stored separately. /// Storing it separately allows us to avoid bloating the effects with data that are not critical. /// It also provides more flexibility on the format and type of the data. - aux_data_digest: Option, + aux_data_digest: Option, } impl TransactionEffectsAPI for TransactionEffectsV2 { @@ -64,7 +67,6 @@ impl TransactionEffectsAPI for TransactionEffectsV2 { self.executed_epoch } - // TODO: Add a new API to return modified object refs. fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> { self.changed_objects .iter() @@ -78,6 +80,19 @@ impl TransactionEffectsAPI for TransactionEffectsV2 { .collect() } + fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)> { + self.changed_objects + .iter() + .filter_map(|(id, change)| { + if let ObjectIn::Exist(((version, digest), owner)) = &change.input_state { + Some(((*id, *version, *digest), *owner)) + } else { + None + } + }) + .collect() + } + fn input_shared_objects(&self) -> Vec<(ObjectRef, InputSharedObjectKind)> { self.changed_objects .iter() @@ -220,12 +235,19 @@ impl TransactionEffectsAPI for TransactionEffectsV2 { } fn gas_object(&self) -> (ObjectRef, Owner) { - let entry = &self.changed_objects[self.gas_object_index as usize]; - match entry.1.output_state { - ObjectOut::ObjectWrite((digest, owner)) => { - ((entry.0, self.lamport_version, digest), owner) + if let Some(gas_object_index) = self.gas_object_index { + let entry = &self.changed_objects[gas_object_index as usize]; + match entry.1.output_state { + ObjectOut::ObjectWrite((digest, owner)) => { + ((entry.0, self.lamport_version, digest), owner) + } + _ => panic!("Gas object must be an ObjectWrite in changed_objects"), } - _ => panic!("Gas object must be an ObjectWrite in changed_objects"), + } else { + ( + (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN), + Owner::AddressOwner(SuiAddress::default()), + ) } } @@ -308,11 +330,88 @@ impl TransactionEffectsAPI for TransactionEffectsV2 { } impl TransactionEffectsV2 { - #[cfg(debug_assertions)] + pub fn new( + status: ExecutionStatus, + executed_epoch: EpochId, + gas_used: GasCostSummary, + shared_objects: Vec, + transaction_digest: TransactionDigest, + lamport_version: SequenceNumber, + changed_objects: BTreeMap, + gas_object: Option, + events_digest: Option, + dependencies: Vec, + ) -> Self { + let unchanged_shared_objects = shared_objects + .into_iter() + .filter_map(|(id, version, digest)| { + if changed_objects.contains_key(&id) { + None + } else { + Some((id, UnchangedSharedKind::ReadOnlyRoot((version, digest)))) + } + }) + .collect(); + let changed_objects: Vec<_> = changed_objects.into_iter().collect(); + + let gas_object_index = gas_object.map(|gas_id| { + changed_objects + .iter() + .position(|(id, _)| id == &gas_id) + .unwrap() as u32 + }); + + let result = Self { + status, + executed_epoch, + gas_used, + transaction_digest, + lamport_version, + changed_objects, + unchanged_shared_objects, + gas_object_index, + events_digest, + dependencies, + aux_data_digest: None, + }; + #[cfg(debug_assertions)] + result.check_invariant(); + + result + } + + pub fn new_with_tx_and_gas(tx: &SenderSignedData, gas_object: (ObjectRef, Owner)) -> Self { + Self { + transaction_digest: tx.digest(), + lamport_version: gas_object.0 .1, + changed_objects: vec![( + gas_object.0 .0, + EffectsObjectChange { + input_state: ObjectIn::Exist(( + (SequenceNumber::default(), ObjectDigest::MIN), + gas_object.1, + )), + output_state: ObjectOut::ObjectWrite((gas_object.0 .2, gas_object.1)), + id_operation: IDOperation::None, + }, + )], + gas_object_index: Some(0), + ..Default::default() + } + } + + pub fn new_with_tx_and_status(tx: &SenderSignedData, status: ExecutionStatus) -> Self { + Self { + transaction_digest: tx.digest(), + status, + ..Default::default() + } + } + /// This function demonstrates what's the invariant of the effects. /// It also documents the semantics of different combinations in object changes. - /// TODO: It will be called in the constructor of `TransactionEffectsV2` in the future. - fn _check_invariant(&self) { + #[cfg(debug_assertions)] + fn check_invariant(&self) { let mut unique_ids = HashSet::new(); for (id, change) in &self.changed_objects { assert!(unique_ids.insert(*id)); @@ -338,8 +437,9 @@ impl TransactionEffectsV2 { (ObjectIn::NotExist, ObjectOut::PackageWrite(_), IDOperation::Created) => { // created Move package or user Move package upgrade. } - (ObjectIn::Exist(_), ObjectOut::NotExist, IDOperation::None) => { + (ObjectIn::Exist((_, old_owner)), ObjectOut::NotExist, IDOperation::None) => { // wrapped. + assert!(!old_owner.is_shared(), "Cannot wrap shared object"); } (ObjectIn::Exist(_), ObjectOut::NotExist, IDOperation::Deleted) => { // deleted. @@ -376,8 +476,26 @@ impl TransactionEffectsV2 { } } +impl Default for TransactionEffectsV2 { + fn default() -> Self { + Self { + status: ExecutionStatus::Success, + executed_epoch: 0, + gas_used: GasCostSummary::default(), + transaction_digest: TransactionDigest::default(), + lamport_version: SequenceNumber::default(), + changed_objects: vec![], + unchanged_shared_objects: vec![], + gas_object_index: None, + events_digest: None, + dependencies: vec![], + aux_data_digest: None, + } + } +} + #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -enum UnchangedSharedKind { +pub(crate) enum UnchangedSharedKind { /// Read-only shared objects from the input. We don't really need ObjectDigest /// for protocol correctness, but it will make it easier to verify untrusted read. ReadOnlyRoot(VersionDigest), diff --git a/crates/sui-types/src/effects/mod.rs b/crates/sui-types/src/effects/mod.rs index 6616a056416bd..117b9aa06ce3a 100644 --- a/crates/sui-types/src/effects/mod.rs +++ b/crates/sui-types/src/effects/mod.rs @@ -1,14 +1,13 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use self::effects_v2::TransactionEffectsV2; use crate::base_types::{random_object_ref, ExecutionDigests, ObjectID, ObjectRef, SequenceNumber}; use crate::committee::EpochId; use crate::crypto::{ default_hash, AuthoritySignInfo, AuthorityStrongQuorumSignInfo, EmptySignInfo, }; -use crate::digests::{ - ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest, -}; +use crate::digests::{TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest}; use crate::error::{SuiError, SuiResult}; use crate::event::Event; use crate::execution_status::ExecutionStatus; @@ -21,13 +20,11 @@ use crate::storage::WriteKind; use crate::transaction::{SenderSignedData, TransactionDataAPI, VersionedProtocolMessage}; use effects_v1::TransactionEffectsV1; use enum_dispatch::enum_dispatch; -pub use object_change::EffectsObjectChange; +pub use object_change::{EffectsObjectChange, IDOperation, ObjectIn, ObjectOut}; use serde::{Deserialize, Serialize}; use shared_crypto::intent::IntentScope; use std::collections::BTreeMap; -use sui_protocol_config::{ProtocolConfig, ProtocolVersion, SupportedProtocolVersions}; - -use self::object_change::{IDOperation, ObjectIn, ObjectOut}; +use sui_protocol_config::ProtocolConfig; mod effects_v1; mod effects_v2; @@ -53,34 +50,35 @@ pub const APPROX_SIZE_OF_OWNER: usize = 48; /// The response from processing a transaction or a certified transaction #[enum_dispatch(TransactionEffectsAPI)] #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +#[allow(clippy::large_enum_variant)] pub enum TransactionEffects { V1(TransactionEffectsV1), + V2(TransactionEffectsV2), } impl VersionedProtocolMessage for TransactionEffects { fn message_version(&self) -> Option { Some(match self { Self::V1(_) => 1, + Self::V2(_) => 2, }) } fn check_version_supported(&self, protocol_config: &ProtocolConfig) -> SuiResult { - let (message_version, supported) = match self { - Self::V1(_) => (1, SupportedProtocolVersions::new_for_message(1, u64::MAX)), - // Suppose we add V2 at protocol version 7, then we must change this to: - // Self::V1 => (1, SupportedProtocolVersions::new_for_message(1, u64::MAX)), - // Self::V2 => (2, SupportedProtocolVersions::new_for_message(7, u64::MAX)), - }; - - if supported.is_version_supported(protocol_config.version) { - Ok(()) - } else { - Err(SuiError::WrongMessageVersion { - error: format!( - "TransactionEffectsV{} is not supported at {:?}. (Supported range is {:?}", - message_version, protocol_config.version, supported - ), - }) + match self { + Self::V1(_) => Ok(()), + Self::V2(_) => { + if protocol_config.enable_effects_v2() { + Ok(()) + } else { + Err(SuiError::WrongMessageVersion { + error: format!( + "TransactionEffectsV2 is not supported at protocol {:?}.", + protocol_config.version + ), + }) + } + } } } } @@ -117,7 +115,6 @@ impl TransactionEffects { /// Creates a TransactionEffects message from the results of execution, choosing the correct /// format for the current protocol version. pub fn new_from_execution_v1( - _protocol_version: ProtocolVersion, status: ExecutionStatus, executed_epoch: EpochId, gas_used: GasCostSummary, @@ -156,107 +153,25 @@ impl TransactionEffects { /// Creates a TransactionEffects message from the results of execution, choosing the correct /// format for the current protocol version. pub fn new_from_execution_v2( - _protocol_version: ProtocolVersion, status: ExecutionStatus, executed_epoch: EpochId, gas_used: GasCostSummary, shared_objects: Vec, transaction_digest: TransactionDigest, lamport_version: SequenceNumber, - object_changes: BTreeMap, - gas_object: (ObjectRef, Owner), + changed_objects: BTreeMap, + gas_object: Option, events_digest: Option, dependencies: Vec, ) -> Self { - let mut created = vec![]; - let mut mutated = vec![]; - let mut unwrapped = vec![]; - let mut deleted = vec![]; - let mut unwrapped_then_deleted = vec![]; - let mut wrapped = vec![]; - // It is important that we constructs `modified_at_versions` and `deleted_at_versions` - // separately, and merge them latter to achieve the exact same order as in v1. - let mut modified_at_versions = vec![]; - let mut deleted_at_versions = vec![]; - for (id, change) in object_changes { - match change.input_state { - ObjectIn::Exist(((old_version, _), _)) => { - match (change.output_state, change.id_operation) { - (ObjectOut::ObjectWrite((new_digest, new_owner)), IDOperation::None) => { - mutated.push(((id, lamport_version, new_digest), new_owner)); - modified_at_versions.push((id, old_version)); - } - (ObjectOut::PackageWrite((new_version, new_digest)), IDOperation::None) => { - mutated.push(((id, new_version, new_digest), Owner::Immutable)); - modified_at_versions.push((id, old_version)); - } - (ObjectOut::NotExist, IDOperation::Deleted) => { - deleted.push(( - id, - lamport_version, - ObjectDigest::OBJECT_DIGEST_DELETED, - )); - deleted_at_versions.push((id, old_version)); - } - (ObjectOut::NotExist, IDOperation::None) => { - wrapped.push(( - id, - lamport_version, - ObjectDigest::OBJECT_DIGEST_WRAPPED, - )); - deleted_at_versions.push((id, old_version)); - } - _ => { - unreachable!("Impossible combination of output state and id operation"); - } - } - } - ObjectIn::NotExist => { - match (change.output_state, change.id_operation) { - (ObjectOut::ObjectWrite((new_digest, new_owner)), IDOperation::Created) => { - created.push(((id, lamport_version, new_digest), new_owner)); - } - ( - ObjectOut::PackageWrite((new_version, new_digest)), - IDOperation::Created, - ) => { - created.push(((id, new_version, new_digest), Owner::Immutable)); - } - (ObjectOut::ObjectWrite((new_digest, new_owner)), IDOperation::None) => { - unwrapped.push(((id, lamport_version, new_digest), new_owner)); - } - (ObjectOut::NotExist, IDOperation::Deleted) => { - unwrapped_then_deleted.push(( - id, - lamport_version, - ObjectDigest::OBJECT_DIGEST_DELETED, - )); - } - (ObjectOut::NotExist, IDOperation::Created) => { - // Created then wrapped. - } - _ => { - unreachable!("Impossible combination of output state and id operation"); - } - } - } - } - } - modified_at_versions.extend(deleted_at_versions); - - Self::V1(TransactionEffectsV1::new( + Self::V2(TransactionEffectsV2::new( status, executed_epoch, gas_used, - modified_at_versions, shared_objects, transaction_digest, - created, - mutated, - unwrapped, - deleted, - unwrapped_then_deleted, - wrapped, + lamport_version, + changed_objects, gas_object, events_digest, dependencies, @@ -270,7 +185,7 @@ impl TransactionEffects { } } - pub fn estimate_effects_size_upperbound( + pub fn estimate_effects_size_upperbound_v1( num_writes: usize, num_mutables: usize, num_deletes: usize, @@ -294,6 +209,26 @@ impl TransactionEffects { fixed_sizes + approx_change_entry_size + deps_size } + pub fn estimate_effects_size_upperbound_v2( + num_writes: usize, + num_modifies: usize, + num_deps: usize, + ) -> usize { + let fixed_sizes = APPROX_SIZE_OF_EXECUTION_STATUS + + APPROX_SIZE_OF_EPOCH_ID + + APPROX_SIZE_OF_GAS_COST_SUMMARY + + APPROX_SIZE_OF_OPT_TX_EVENTS_DIGEST; + + // We store object ref and owner for both old objects and new objects. + let approx_change_entry_size = 1_000 + + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_writes + + (APPROX_SIZE_OF_OWNER + APPROX_SIZE_OF_OBJECT_REF) * num_modifies; + + let deps_size = 1_000 + APPROX_SIZE_OF_TX_DIGEST * num_deps; + + fixed_sizes + approx_change_entry_size + deps_size + } + /// Return an iterator that iterates through all changed objects, including mutated, /// created and unwrapped objects. In other words, all objects that still exist /// in the object state after this transaction. @@ -369,11 +304,11 @@ impl TransactionEffects { pub fn new_with_tx_and_gas(tx: &SenderSignedData, gas_object: (ObjectRef, Owner)) -> Self { // TODO: Figure out who is calling this and why. // This creates an inconsistent effects where gas object is not mutated. - TransactionEffects::V1(TransactionEffectsV1::new_with_tx_and_gas(tx, gas_object)) + TransactionEffects::V2(TransactionEffectsV2::new_with_tx_and_gas(tx, gas_object)) } pub fn new_with_tx_and_status(tx: &SenderSignedData, status: ExecutionStatus) -> Self { - TransactionEffects::V1(TransactionEffectsV1::new_with_tx_and_status(tx, status)) + TransactionEffects::V2(TransactionEffectsV2::new_with_tx_and_status(tx, status)) } } @@ -388,6 +323,12 @@ pub trait TransactionEffectsAPI { fn into_status(self) -> ExecutionStatus; fn executed_epoch(&self) -> EpochId; fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>; + + /// Metadata of objects prior to modification. This includes any object that exists in the + /// store prior to this transaction and is modified in this transaction. + /// It includes objects that are mutated, wrapped and deleted. + /// This API is only available on effects v2 and above. + fn old_object_metadata(&self) -> Vec<(ObjectRef, Owner)>; /// Returns the list of shared objects used in the input, with full object reference /// and use kind. This is needed in effects because in transaction we only have object ID /// for shared objects. Their version and digest can only be figured out after sequencing. @@ -400,7 +341,12 @@ pub trait TransactionEffectsAPI { fn deleted(&self) -> Vec; fn unwrapped_then_deleted(&self) -> Vec; fn wrapped(&self) -> Vec; + + // TODO: We should consider having this function to return Option. + // When the gas object is not available (i.e. system transaction), we currently return + // dummy object ref and owner. This is not ideal. fn gas_object(&self) -> (ObjectRef, Owner); + fn events_digest(&self) -> Option<&TransactionEventsDigest>; fn dependencies(&self) -> &[TransactionDigest]; diff --git a/crates/sui-types/src/effects/object_change.rs b/crates/sui-types/src/effects/object_change.rs index 1f6178b93378d..d2b8c9167a48d 100644 --- a/crates/sui-types/src/effects/object_change.rs +++ b/crates/sui-types/src/effects/object_change.rs @@ -55,7 +55,7 @@ impl EffectsObjectChange { } #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -pub(crate) enum IDOperation { +pub enum IDOperation { None, Created, Deleted, @@ -65,14 +65,14 @@ pub(crate) enum IDOperation { /// it should be Exist, otherwise it's NonExist, e.g. wrapped objects should be /// NonExist. #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -pub(crate) enum ObjectIn { +pub enum ObjectIn { NotExist, /// The old version, digest and owner. Exist((VersionDigest, Owner)), } #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -pub(crate) enum ObjectOut { +pub enum ObjectOut { /// Same definition as in ObjectIn. NotExist, /// Any written object, including all of mutated, created, unwrapped today. diff --git a/crates/sui-types/src/execution.rs b/crates/sui-types/src/execution.rs index d54c1ee367f0c..808226021afe2 100644 --- a/crates/sui-types/src/execution.rs +++ b/crates/sui-types/src/execution.rs @@ -124,7 +124,8 @@ impl ExecutionResultsV2 { prev_tx: TransactionDigest, ) { for (id, mut obj) in self.written_objects.iter_mut() { - // TODO: All of the following is no longer necessary and can be simplified. + // TODO: We can now get rid of the following logic by passing in lamport version + // into the execution layer, and create new objects using the lamport version directly. // Update the version for the written object. match &mut obj.data { diff --git a/sui-execution/latest/sui-adapter/src/temporary_store.rs b/sui-execution/latest/sui-adapter/src/temporary_store.rs index 7e28e5d960901..a727c2fa328be 100644 --- a/sui-execution/latest/sui-adapter/src/temporary_store.rs +++ b/sui-execution/latest/sui-adapter/src/temporary_store.rs @@ -12,6 +12,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use sui_protocol_config::ProtocolConfig; use sui_types::base_types::VersionDigest; use sui_types::committee::EpochId; +use sui_types::digests::ObjectDigest; use sui_types::effects::{TransactionEffects, TransactionEvents}; use sui_types::execution::{DynamicallyLoadedObjectMetadata, ExecutionResults, ExecutionResultsV2}; use sui_types::execution_status::ExecutionStatus; @@ -20,9 +21,7 @@ use sui_types::storage::BackingStore; use sui_types::sui_system_state::{get_sui_system_state_wrapper, AdvanceEpochParams}; use sui_types::type_resolver::LayoutResolver; use sui_types::{ - base_types::{ - ObjectDigest, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest, - }, + base_types::{ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest}, effects::EffectsObjectChange, error::{ExecutionError, SuiError, SuiResult}, fp_bail, @@ -195,10 +194,39 @@ impl<'backing> TemporaryStore<'backing> { } } - // In the case of special transactions that don't require a gas object, - // we don't really care about the effects to gas, just use the input for it. - // Gas coins are guaranteed to be at least size 1 and if more than 1 - // the first coin is where all the others are merged. + if self.protocol_config.enable_effects_v2() { + self.into_effects_v2( + shared_object_refs, + transaction_digest, + transaction_dependencies, + gas_cost_summary, + status, + gas_charger, + epoch, + ) + } else { + self.into_effects_v1( + shared_object_refs, + transaction_digest, + transaction_dependencies, + gas_cost_summary, + status, + gas_charger, + epoch, + ) + } + } + + fn into_effects_v1( + self, + shared_object_refs: Vec, + transaction_digest: &TransactionDigest, + transaction_dependencies: BTreeSet, + gas_cost_summary: GasCostSummary, + status: ExecutionStatus, + gas_charger: &mut GasCharger, + epoch: EpochId, + ) -> (InnerTemporaryStore, TransactionEffects) { let updated_gas_object_info = if let Some(coin_id) = gas_charger.gas_coin() { let object = &self.execution_results.written_objects[&coin_id]; (object.compute_object_reference(), object.owner) @@ -208,27 +236,115 @@ impl<'backing> TemporaryStore<'backing> { Owner::AddressOwner(SuiAddress::default()), ) }; + let lampot_version = self.lamport_timestamp; + + let mut created = vec![]; + let mut mutated = vec![]; + let mut unwrapped = vec![]; + let mut deleted = vec![]; + let mut unwrapped_then_deleted = vec![]; + let mut wrapped = vec![]; + // It is important that we constructs `modified_at_versions` and `deleted_at_versions` + // separately, and merge them latter to achieve the exact same order as in v1. + let mut modified_at_versions = vec![]; + let mut deleted_at_versions = vec![]; + self.execution_results + .written_objects + .iter() + .for_each(|(id, object)| { + let object_ref = object.compute_object_reference(); + let owner = object.owner; + if let Some(old_object_meta) = self.get_object_modified_at(id) { + modified_at_versions.push((*id, old_object_meta.version)); + mutated.push((object_ref, owner)); + } else if self.execution_results.created_object_ids.contains(id) { + created.push((object_ref, owner)); + } else { + unwrapped.push((object_ref, owner)); + } + }); + self.execution_results + .modified_objects + .iter() + .filter(|id| !self.execution_results.written_objects.contains_key(id)) + .for_each(|id| { + let old_object_meta = self.get_object_modified_at(id).unwrap(); + deleted_at_versions.push((*id, old_object_meta.version)); + if self.execution_results.deleted_object_ids.contains(id) { + deleted.push((*id, lampot_version, ObjectDigest::OBJECT_DIGEST_DELETED)); + } else { + wrapped.push((*id, lampot_version, ObjectDigest::OBJECT_DIGEST_WRAPPED)); + } + }); + self.execution_results + .deleted_object_ids + .iter() + .filter(|id| !self.execution_results.modified_objects.contains(id)) + .for_each(|id| { + unwrapped_then_deleted.push(( + *id, + lampot_version, + ObjectDigest::OBJECT_DIGEST_DELETED, + )); + }); + modified_at_versions.extend(deleted_at_versions); + + let inner = self.into_inner(); + let effects = TransactionEffects::new_from_execution_v1( + status, + epoch, + gas_cost_summary, + modified_at_versions, + shared_object_refs, + *transaction_digest, + created, + mutated, + unwrapped, + deleted, + unwrapped_then_deleted, + wrapped, + updated_gas_object_info, + if inner.events.data.is_empty() { + None + } else { + Some(inner.events.digest()) + }, + transaction_dependencies.into_iter().collect(), + ); + (inner, effects) + } + + fn into_effects_v2( + self, + shared_object_refs: Vec, + transaction_digest: &TransactionDigest, + transaction_dependencies: BTreeSet, + gas_cost_summary: GasCostSummary, + status: ExecutionStatus, + gas_charger: &mut GasCharger, + epoch: EpochId, + ) -> (InnerTemporaryStore, TransactionEffects) { + // In the case of special transactions that don't require a gas object, + // we don't really care about the effects to gas, just use the input for it. + // Gas coins are guaranteed to be at least size 1 and if more than 1 + // the first coin is where all the others are merged. + let gas_coin = gas_charger.gas_coin(); let object_changes = self.get_object_changes(); let lamport_version = self.lamport_timestamp; - let protocol_version = self.protocol_config.version; let inner = self.into_inner(); let effects = TransactionEffects::new_from_execution_v2( - protocol_version, status, epoch, gas_cost_summary, - // While we could derive the list of shared objects from object_changes, - // it is difficult to keep it in the same order as today. - // TODO: We will remove it when we actually construct effects v2, but for v1 - // we keep it as it is. + // TODO: Provide the list of read-only shared objects directly. shared_object_refs, *transaction_digest, lamport_version, object_changes, - updated_gas_object_info, + gas_coin, if inner.events.data.is_empty() { None } else { @@ -236,6 +352,7 @@ impl<'backing> TemporaryStore<'backing> { }, transaction_dependencies.into_iter().collect(), ); + (inner, effects) } @@ -374,26 +491,33 @@ impl<'backing> TemporaryStore<'backing> { self.loaded_runtime_objects.extend(loaded_runtime_objects); } - // TODO: Simplify this logic for effects v2. pub fn estimate_effects_size_upperbound(&self) -> usize { - let num_deletes = self.execution_results.deleted_object_ids.len() - + self - .execution_results - .modified_objects - .iter() - .filter(|id| { - // Filter for wrapped objects. - !self.execution_results.written_objects.contains_key(id) - && !self.execution_results.deleted_object_ids.contains(id) - }) - .count(); - // In the worst case, the number of deps is equal to the number of input objects - TransactionEffects::estimate_effects_size_upperbound( - self.execution_results.written_objects.len(), - self.mutable_input_refs.len(), - num_deletes, - self.input_objects.len(), - ) + if self.protocol_config.enable_effects_v2() { + TransactionEffects::estimate_effects_size_upperbound_v2( + self.execution_results.written_objects.len(), + self.execution_results.modified_objects.len(), + self.input_objects.len(), + ) + } else { + let num_deletes = self.execution_results.deleted_object_ids.len() + + self + .execution_results + .modified_objects + .iter() + .filter(|id| { + // Filter for wrapped objects. + !self.execution_results.written_objects.contains_key(id) + && !self.execution_results.deleted_object_ids.contains(id) + }) + .count(); + // In the worst case, the number of deps is equal to the number of input objects + TransactionEffects::estimate_effects_size_upperbound_v1( + self.execution_results.written_objects.len(), + self.mutable_input_refs.len(), + num_deletes, + self.input_objects.len(), + ) + } } pub fn written_objects_size(&self) -> usize { diff --git a/sui-execution/v0/sui-adapter/src/temporary_store.rs b/sui-execution/v0/sui-adapter/src/temporary_store.rs index 7225fc28589f8..4e8c7688dcb3e 100644 --- a/sui-execution/v0/sui-adapter/src/temporary_store.rs +++ b/sui-execution/v0/sui-adapter/src/temporary_store.rs @@ -256,11 +256,9 @@ impl<'backing> TemporaryStore<'backing> { } } - let protocol_version = self.protocol_config.version; let inner = self.into_inner(); let effects = TransactionEffects::new_from_execution_v1( - protocol_version, status, epoch, gas_cost_summary, @@ -442,7 +440,7 @@ impl<'backing> TemporaryStore<'backing> { pub fn estimate_effects_size_upperbound(&self) -> usize { // In the worst case, the number of deps is equal to the number of input objects - TransactionEffects::estimate_effects_size_upperbound( + TransactionEffects::estimate_effects_size_upperbound_v1( self.written.len(), self.mutable_input_refs.len(), self.deleted.len(), diff --git a/sui-execution/vm-rework/sui-adapter/src/temporary_store.rs b/sui-execution/vm-rework/sui-adapter/src/temporary_store.rs index 7e28e5d960901..f685d105595eb 100644 --- a/sui-execution/vm-rework/sui-adapter/src/temporary_store.rs +++ b/sui-execution/vm-rework/sui-adapter/src/temporary_store.rs @@ -20,9 +20,7 @@ use sui_types::storage::BackingStore; use sui_types::sui_system_state::{get_sui_system_state_wrapper, AdvanceEpochParams}; use sui_types::type_resolver::LayoutResolver; use sui_types::{ - base_types::{ - ObjectDigest, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest, - }, + base_types::{ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest}, effects::EffectsObjectChange, error::{ExecutionError, SuiError, SuiResult}, fp_bail, @@ -199,24 +197,14 @@ impl<'backing> TemporaryStore<'backing> { // we don't really care about the effects to gas, just use the input for it. // Gas coins are guaranteed to be at least size 1 and if more than 1 // the first coin is where all the others are merged. - let updated_gas_object_info = if let Some(coin_id) = gas_charger.gas_coin() { - let object = &self.execution_results.written_objects[&coin_id]; - (object.compute_object_reference(), object.owner) - } else { - ( - (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN), - Owner::AddressOwner(SuiAddress::default()), - ) - }; + let gas_coin = gas_charger.gas_coin(); let object_changes = self.get_object_changes(); let lamport_version = self.lamport_timestamp; - let protocol_version = self.protocol_config.version; let inner = self.into_inner(); let effects = TransactionEffects::new_from_execution_v2( - protocol_version, status, epoch, gas_cost_summary, @@ -228,7 +216,7 @@ impl<'backing> TemporaryStore<'backing> { *transaction_digest, lamport_version, object_changes, - updated_gas_object_info, + gas_coin, if inner.events.data.is_empty() { None } else { @@ -388,7 +376,7 @@ impl<'backing> TemporaryStore<'backing> { }) .count(); // In the worst case, the number of deps is equal to the number of input objects - TransactionEffects::estimate_effects_size_upperbound( + TransactionEffects::estimate_effects_size_upperbound_v1( self.execution_results.written_objects.len(), self.mutable_input_refs.len(), num_deletes,