From 86b4311663b3707ba9d8aad10e2bdca1ddd38d19 Mon Sep 17 00:00:00 2001 From: Chris Li <76067158+666lcz@users.noreply.github.com> Date: Mon, 13 Mar 2023 06:46:19 -0400 Subject: [PATCH] [rpc][2 of 2] implement options for queryTransactions (#9181) ## Description - follow up to https://github.com/MystenLabs/sui/pull/9156 for using `SuiTransactionResponseOptions` in `queryTransactions` ## Test Plan Added TS e2e tests --- If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process. ### Type of Change (Check all that apply) - [ ] user-visible impact - [ ] breaking change for a client SDKs - [ ] breaking change for FNs (FN binary must upgrade) - [ ] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [ ] necessitate either a data wipe or data migration ### Release notes --- crates/sui-indexer/src/apis/read_api.rs | 8 ++++ .../sui-indexer/src/bin/checkpoint_check.rs | 5 ++- .../src/handlers/checkpoint_handler.rs | 5 ++- .../sui-json-rpc-types/src/sui_transaction.rs | 20 +++++---- crates/sui-json-rpc/src/read_api.rs | 45 +++++++++++++++---- crates/sui/src/client_commands.rs | 6 ++- .../test/e2e/read-transactions.test.ts | 11 +++++ 7 files changed, 80 insertions(+), 20 deletions(-) diff --git a/crates/sui-indexer/src/apis/read_api.rs b/crates/sui-indexer/src/apis/read_api.rs index 61e1a4fa710b8..01d61efd8200c 100644 --- a/crates/sui-indexer/src/apis/read_api.rs +++ b/crates/sui-indexer/src/apis/read_api.rs @@ -68,6 +68,14 @@ impl ReadApi { let is_descending = descending_order.unwrap_or_default(); let cursor_str = cursor.map(|digest| digest.to_string()); + let opts = query.options.unwrap_or_default(); + if !opts.only_digest() { + // TODO(chris): implement this as a separate PR + return Err(IndexerError::NotImplementedError( + "options has not been implemented on indexer for queryTransactions".to_string(), + )); + } + let digests_from_db = match query.filter { None => { let indexer_seq_number = self diff --git a/crates/sui-indexer/src/bin/checkpoint_check.rs b/crates/sui-indexer/src/bin/checkpoint_check.rs index 46ddd9cdb5886..2a7a2987c0e98 100644 --- a/crates/sui-indexer/src/bin/checkpoint_check.rs +++ b/crates/sui-indexer/src/bin/checkpoint_check.rs @@ -58,7 +58,10 @@ async fn main() -> Result<()> { ); if let (Some(fn_txn_digest), Some(idx_txn_digest)) = (fn_txn_digest, idx_txn_digest) { - let fetch_options = SuiTransactionResponseOptions::full_content(); + let fetch_options = SuiTransactionResponseOptions::new() + .with_events() + .with_effects() + .with_input(); let fn_sui_txn_response = fn_rpc_client .read_api() .get_transaction_with_options(fn_txn_digest, fetch_options.clone()) diff --git a/crates/sui-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-indexer/src/handlers/checkpoint_handler.rs index ab6437d0dbf2e..0940301c0809f 100644 --- a/crates/sui-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-indexer/src/handlers/checkpoint_handler.rs @@ -139,7 +139,10 @@ where .read_api() .multi_get_transactions_with_options( digests.to_vec(), - SuiTransactionResponseOptions::full_content(), + SuiTransactionResponseOptions::new() + .with_effects() + .with_input() + .with_events(), ) }, )) diff --git a/crates/sui-json-rpc-types/src/sui_transaction.rs b/crates/sui-json-rpc-types/src/sui_transaction.rs index 0da5b8c759863..bd62bfe4f0e3b 100644 --- a/crates/sui-json-rpc-types/src/sui_transaction.rs +++ b/crates/sui-json-rpc-types/src/sui_transaction.rs @@ -137,6 +137,16 @@ impl SuiTransactionResponseOptions { self } + pub fn with_balance_changes(mut self) -> Self { + self.show_balance_changes = true; + self + } + + pub fn with_object_changes(mut self) -> Self { + self.show_object_changes = true; + self + } + /// default to return `WaitForEffectsCert` unless some options require /// local execution pub fn default_execution_request_type(&self) -> ExecuteTransactionRequestType { @@ -159,14 +169,8 @@ impl SuiTransactionResponseOptions { || self.show_object_changes } - pub fn with_balance_changes(mut self) -> Self { - self.show_balance_changes = true; - self - } - - pub fn with_object_changes(mut self) -> Self { - self.show_object_changes = true; - self + pub fn only_digest(&self) -> bool { + self == &Self::default() } } diff --git a/crates/sui-json-rpc/src/read_api.rs b/crates/sui-json-rpc/src/read_api.rs index 5f3ae05fe86c1..a8cfba80f6c24 100644 --- a/crates/sui-json-rpc/src/read_api.rs +++ b/crates/sui-json-rpc/src/read_api.rs @@ -421,6 +421,18 @@ impl ReadApiServer for ReadApi { }) .into()); } + + let opts = opts.unwrap_or_default(); + if opts.show_balance_changes || opts.show_object_changes { + // Not supported because it's likely the response will easily exceed response limit + return Err(anyhow!(UserInputError::Unsupported( + "show_balance_changes and show_object_changes is not available on \ + multiGetTransactions" + .to_string() + )) + .into()); + } + // use LinkedHashMap to dedup and can iterate in insertion order. let mut temp_response: LinkedHashMap<&TransactionDigest, IntermediateTransactionResponse> = LinkedHashMap::from_iter( @@ -432,8 +444,6 @@ impl ReadApiServer for ReadApi { return Err(anyhow!("The list of digests in the input contain duplicates").into()); } - let opts = opts.unwrap_or_default(); - if opts.show_input { let transactions = self .state @@ -686,20 +696,39 @@ impl ReadApiServer for ReadApi { ) -> RpcResult { let limit = cap_page_limit(limit); let descending = descending_order.unwrap_or_default(); + let opts = query.options.unwrap_or_default(); + if opts.show_balance_changes || opts.show_object_changes { + // Not supported because it's likely the response will easily exceed response limit + return Err(anyhow!(UserInputError::Unsupported( + "show_balance_changes and show_object_changes is not available on \ + queryTransactions" + .to_string() + )) + .into()); + } // Retrieve 1 extra item for next cursor - let mut data = + let mut digests = self.state .get_transactions(query.filter, cursor, Some(limit + 1), descending)?; // extract next cursor - let has_next_page = data.len() > limit; - data.truncate(limit); - let next_cursor = data.last().cloned().map_or(cursor, Some); + let has_next_page = digests.len() > limit; + digests.truncate(limit); + let next_cursor = digests.last().cloned().map_or(cursor, Some); + + let data: Vec = if opts.only_digest() { + digests + .into_iter() + .map(SuiTransactionResponse::new) + .collect() + } else { + self.multi_get_transactions_with_options(digests, Some(opts)) + .await? + }; - // TODO(chris): fetch transaction response based on `query.options` Ok(Page { - data: data.into_iter().map(SuiTransactionResponse::new).collect(), + data, next_cursor, has_next_page, }) diff --git a/crates/sui/src/client_commands.rs b/crates/sui/src/client_commands.rs index 8d15ea865676b..34d051fd37fda 100644 --- a/crates/sui/src/client_commands.rs +++ b/crates/sui/src/client_commands.rs @@ -1252,8 +1252,10 @@ impl WalletContext { .quorum_driver() .execute_transaction( tx, - // TODO(chris): we probably don't need full content here - SuiTransactionResponseOptions::full_content(), + SuiTransactionResponseOptions::new() + .with_effects() + .with_events() + .with_input(), Some(sui_types::messages::ExecuteTransactionRequestType::WaitForLocalExecution), ) .await?) diff --git a/sdk/typescript/test/e2e/read-transactions.test.ts b/sdk/typescript/test/e2e/read-transactions.test.ts index b66efee6422c1..93d1e6aa11d65 100644 --- a/sdk/typescript/test/e2e/read-transactions.test.ts +++ b/sdk/typescript/test/e2e/read-transactions.test.ts @@ -24,6 +24,17 @@ describe('Transaction Reading API', () => { expect(getTransactionDigest(txn)).toEqual(digest); }); + it('Query Transactions with opts', async () => { + const options = { showEvents: true, showEffects: true }; + const resp = await toolbox.provider.queryTransactions({ options }, null, 1); + const digest = resp.data[0].digest; + const response2 = await toolbox.provider.getTransactionResponse( + digest, + options, + ); + expect(resp.data[0]).toEqual(response2); + }); + it('Get Transactions', async () => { const resp = await toolbox.provider.queryTransactionsForAddressDeprecated( toolbox.address(),