Skip to content

Commit

Permalink
[tests] create dry run helper for sui transactional test runner frame…
Browse files Browse the repository at this point in the history
…work (#20920)

## Description 

motivation: #20109 

Mirroring the existing `--dev-inspect` flag, this change adds
`--dry-run` for sui transactional test runner framework. Pulls out
common code into `tx_summary_from_effects`.

## Test plan 

Added new tests for reference return types. Dev inspect flag allows
reference return while dry run does not.

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] gRPC:
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
  • Loading branch information
jordanjennings-mysten authored Jan 30, 2025
1 parent 8a72297 commit 8ba733e
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
processed 3 tasks

init:
A: object(0,0)

task 1, lines 8-14:
//# publish
created: object(1,0)
mutated: object(0,1)
gas summary: computation_cost: 1000000, storage_cost: 3465600, storage_rebate: 0, non_refundable_storage_fee: 0

task 2, lines 16-17:
//# programmable --sender A --inputs 0 --dev-inspect
//> 0: test::m::return_ref(Input(0));
mutated: object(_)
gas summary: computation_cost: 500000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

// attempt to return a reference from a function

//# init --addresses test=0x0 --accounts A

//# publish

module test::m {
public fun return_ref(n: &u64): &u64 {
n
}
}

//# programmable --sender A --inputs 0 --dev-inspect
//> 0: test::m::return_ref(Input(0));
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
processed 3 tasks

init:
A: object(0,0)

task 1, lines 8-14:
//# publish
created: object(1,0)
mutated: object(0,1)
gas summary: computation_cost: 1000000, storage_cost: 3465600, storage_rebate: 0, non_refundable_storage_fee: 0

task 2, lines 16-17:
//# programmable --sender A --inputs 0 --dry-run
//> 0: test::m::return_ref(Input(0));
Error: Transaction Effects Status: InvalidPublicFunctionReturnType { idx: 0 } in command 0
Execution Error: InvalidPublicFunctionReturnType { idx: 0 } in command 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

// attempt to return a reference from a function

//# init --addresses test=0x0 --accounts A

//# publish

module test::m {
public fun return_ref(n: &u64): &u64 {
n
}
}

//# programmable --sender A --inputs 0 --dry-run
//> 0: test::m::return_ref(Input(0));
2 changes: 2 additions & 0 deletions crates/sui-transactional-test-runner/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ pub struct ProgrammableTransactionCommand {
pub gas_payment: Option<FakeID>,
#[clap(long = "dev-inspect")]
pub dev_inspect: bool,
#[clap(long = "dry-run")]
pub dry_run: bool,
#[clap(
long = "inputs",
value_parser = ParsedValue::<SuiExtraValueArgs>::parse,
Expand Down
29 changes: 27 additions & 2 deletions crates/sui-transactional-test-runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use sui_core::authority::authority_per_epoch_store::CertLockGuard;
use sui_core::authority::authority_test_utils::send_and_confirm_transaction_with_execution_error;
use sui_core::authority::AuthorityState;
use sui_json_rpc::authority_state::StateRead;
use sui_json_rpc_types::DevInspectResults;
use sui_json_rpc_types::EventFilter;
use sui_json_rpc_types::{DevInspectResults, DryRunTransactionBlockResponse};
use sui_storage::key_value_store::TransactionKeyValueStore;
use sui_types::base_types::ObjectID;
use sui_types::base_types::SuiAddress;
Expand All @@ -45,10 +45,10 @@ use sui_types::storage::ObjectStore;
use sui_types::storage::ReadStore;
use sui_types::sui_system_state::epoch_start_sui_system_state::EpochStartSystemStateTrait;
use sui_types::sui_system_state::SuiSystemStateTrait;
use sui_types::transaction::InputObjects;
use sui_types::transaction::Transaction;
use sui_types::transaction::TransactionDataAPI;
use sui_types::transaction::TransactionKind;
use sui_types::transaction::{InputObjects, TransactionData};
use test_adapter::{SuiTestAdapter, PRE_COMPILED};

#[cfg_attr(not(msim), tokio::main)]
Expand Down Expand Up @@ -99,6 +99,12 @@ pub trait TransactionalAdapter: Send + Sync + ReadStore {
amount: u64,
) -> anyhow::Result<TransactionEffects>;

async fn dry_run_transaction_block(
&self,
transaction_block: TransactionData,
transaction_digest: TransactionDigest,
) -> SuiResult<DryRunTransactionBlockResponse>;

async fn dev_inspect_transaction_block(
&self,
sender: SuiAddress,
Expand Down Expand Up @@ -172,6 +178,17 @@ impl TransactionalAdapter for ValidatorWithFullnode {
Ok((effects, error))
}

async fn dry_run_transaction_block(
&self,
transaction_block: TransactionData,
transaction_digest: TransactionDigest,
) -> SuiResult<DryRunTransactionBlockResponse> {
self.fullnode
.dry_exec_transaction(transaction_block, transaction_digest)
.await
.map(|result| result.0)
}

async fn dev_inspect_transaction_block(
&self,
sender: SuiAddress,
Expand Down Expand Up @@ -409,6 +426,14 @@ impl TransactionalAdapter for Simulacrum<StdRng, PersistedStore> {
unimplemented!("dev_inspect_transaction_block not supported in simulator mode")
}

async fn dry_run_transaction_block(
&self,
_transaction_block: TransactionData,
_transaction_digest: TransactionDigest,
) -> SuiResult<DryRunTransactionBlockResponse> {
unimplemented!("dry_run_transaction_block not supported in simulator mode")
}

async fn query_tx_events_asc(
&self,
tx_digest: &TransactionDigest,
Expand Down
125 changes: 88 additions & 37 deletions crates/sui-transactional-test-runner/src/test_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ use sui_core::authority::AuthorityState;
use sui_framework::DEFAULT_FRAMEWORK_PATH;
use sui_graphql_rpc::test_infra::cluster::{RetentionConfig, SnapshotLagConfig};
use sui_json_rpc_api::QUERY_MAX_RESULT_LIMIT;
use sui_json_rpc_types::{DevInspectResults, SuiExecutionStatus, SuiTransactionBlockEffectsAPI};
use sui_json_rpc_types::{
DevInspectResults, DryRunTransactionBlockResponse, SuiExecutionStatus,
SuiTransactionBlockEffects, SuiTransactionBlockEffectsAPI, SuiTransactionBlockEvents,
};
use sui_protocol_config::{Chain, ProtocolConfig};
use sui_storage::{
key_value_store::TransactionKeyValueStore, key_value_store_metrics::KeyValueStoreMetrics,
Expand Down Expand Up @@ -832,12 +835,17 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter {
gas_price,
gas_payment,
dev_inspect,
dry_run,
inputs,
}) => {
if dev_inspect && self.is_simulator() {
bail!("Dev inspect is not supported on simulator mode");
}

if dry_run && dev_inspect {
bail!("Cannot set both dev-inspect and dry-run");
}

let inputs = self.compiled_state().resolve_args(inputs)?;
let inputs: Vec<CallArg> = inputs
.into_iter()
Expand Down Expand Up @@ -873,7 +881,8 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter {
)
})
.collect::<anyhow::Result<Vec<Command>>>()?;
let summary = if !dev_inspect {

let summary = if !dev_inspect && !dry_run {
let gas_budget = gas_budget.unwrap_or(DEFAULT_GAS_BUDGET);
let gas_price = gas_price.unwrap_or(self.gas_price);
let transaction = self.sign_sponsor_txn(
Expand All @@ -892,6 +901,22 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter {
},
);
self.execute_txn(transaction).await?
} else if dry_run {
let gas_budget = gas_budget.unwrap_or(DEFAULT_GAS_BUDGET);
let gas_price = gas_price.unwrap_or(self.gas_price);
let sender = self.get_sender(sender);
let sponsor = sponsor.map_or(sender, |a| self.get_sender(Some(a)));

let payment = self.get_payment(sponsor, gas_payment);

let transaction = TransactionData::new_programmable(
sender.address,
vec![payment],
ProgrammableTransaction { inputs, commands },
gas_budget,
gas_price,
);
self.dry_run(transaction).await?
} else {
assert!(
gas_budget.is_none(),
Expand Down Expand Up @@ -1380,6 +1405,7 @@ impl<'a> SuiTestAdapter {
gas_budget: Option<u64>,
policy: u8,
gas_price: u64,
// dry_run: bool,
) -> anyhow::Result<Option<String>> {
let modules_bytes = modules
.iter()
Expand Down Expand Up @@ -1473,6 +1499,19 @@ impl<'a> SuiTestAdapter {
})
}

fn get_payment(&self, sponsor: &TestAccount, payment: Option<FakeID>) -> ObjectRef {
let payment = if let Some(payment) = payment {
self.fake_to_real_object_id(payment)
.expect("Could not find specified payment object")
} else {
sponsor.gas
};

self.get_object(&payment, None)
.unwrap()
.compute_object_reference()
}

fn sign_sponsor_txn(
&self,
sender: Option<String>,
Expand All @@ -1487,17 +1526,7 @@ impl<'a> SuiTestAdapter {
let sender = self.get_sender(sender);
let sponsor = sponsor.map_or(sender, |a| self.get_sender(Some(a)));

let payment = if let Some(payment) = payment {
self.fake_to_real_object_id(payment)
.expect("Could not find specified payment object")
} else {
sponsor.gas
};

let payment_ref = self
.get_object(&payment, None)
.unwrap()
.compute_object_reference();
let payment_ref = self.get_payment(sponsor, payment);

let data = txn_data(sender.address, sponsor.address, payment_ref);
if sender.address == sponsor.address {
Expand Down Expand Up @@ -1663,6 +1692,19 @@ impl<'a> SuiTestAdapter {
}
}

async fn dry_run(&mut self, transaction: TransactionData) -> anyhow::Result<TxnSummary> {
let digest = transaction.digest();
let results = self
.executor
.dry_run_transaction_block(transaction, digest)
.await?;
let DryRunTransactionBlockResponse {
effects, events, ..
} = results;

self.tx_summary_from_effects(effects, events)
}

async fn dev_inspect(
&mut self,
sender: SuiAddress,
Expand All @@ -1676,6 +1718,21 @@ impl<'a> SuiTestAdapter {
let DevInspectResults {
effects, events, ..
} = results;

self.tx_summary_from_effects(effects, events)
}

fn tx_summary_from_effects(
&mut self,
effects: SuiTransactionBlockEffects,
events: SuiTransactionBlockEvents,
) -> anyhow::Result<TxnSummary> {
if let SuiExecutionStatus::Failure { error } = effects.status() {
return Err(anyhow::anyhow!(self.stabilize_str(format!(
"Transaction Effects Status: {error}\nExecution Error: {error}",
))));
}

let mut created_ids: Vec<_> = effects.created().iter().map(|o| o.object_id()).collect();
let mut mutated_ids: Vec<_> = effects.mutated().iter().map(|o| o.object_id()).collect();
let mut unwrapped_ids: Vec<_> = effects.unwrapped().iter().map(|o| o.object_id()).collect();
Expand Down Expand Up @@ -1712,30 +1769,24 @@ impl<'a> SuiTestAdapter {
unwrapped_then_deleted_ids.sort_by_key(|id| self.real_to_fake_object_id(id));
wrapped_ids.sort_by_key(|id| self.real_to_fake_object_id(id));

match effects.status() {
SuiExecutionStatus::Success { .. } => {
let events = events
.data
.into_iter()
.map(|sui_event| sui_event.into())
.collect();
Ok(TxnSummary {
events,
gas_summary: gas_summary.clone(),
created: created_ids,
mutated: mutated_ids,
unwrapped: unwrapped_ids,
deleted: deleted_ids,
unwrapped_then_deleted: unwrapped_then_deleted_ids,
wrapped: wrapped_ids,
// TODO: Properly propagate unchanged shared objects in dev_inspect.
unchanged_shared: vec![],
})
}
SuiExecutionStatus::Failure { error } => Err(anyhow::anyhow!(self.stabilize_str(
format!("Transaction Effects Status: {error}\nExecution Error: {error}",)
))),
}
let events = events
.data
.into_iter()
.map(|sui_event| sui_event.into())
.collect();

Ok(TxnSummary {
events,
gas_summary: gas_summary.clone(),
created: created_ids,
mutated: mutated_ids,
unwrapped: unwrapped_ids,
deleted: deleted_ids,
unwrapped_then_deleted: unwrapped_then_deleted_ids,
wrapped: wrapped_ids,
// TODO: Properly propagate unchanged shared objects in dev_inspect.
unchanged_shared: vec![],
})
}

fn get_object(&self, id: &ObjectID, version: Option<SequenceNumber>) -> anyhow::Result<Object> {
Expand Down

0 comments on commit 8ba733e

Please sign in to comment.