From 8df080f767957aaa72def9f7b1003af40a88a4f6 Mon Sep 17 00:00:00 2001 From: Zhe Wu Date: Sat, 16 Mar 2024 21:04:35 -0700 Subject: [PATCH] Allow `sui client call` command to set gas price (#16648) ## Description This PR only adds the `gas_price` option in the `sui client call` command. I can add this option to other `sui client` command if there is a need for it. ## Test Plan How did you test the new or updated feature? --- If your changes are not user-facing and do not break anything, you can skip the following section. Otherwise, please briefly describe what has changed under the Release Notes section. ### Type of Change (Check all that apply) - [ ] protocol change - [ ] 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 --- .../src/test_case/coin_index_test.rs | 7 ++ .../src/transaction_builder_api.rs | 1 + crates/sui-sdk/examples/tic_tac_toe.rs | 2 + crates/sui-transaction-builder/src/lib.rs | 7 +- crates/sui/src/client_commands.rs | 11 ++- crates/sui/tests/cli_tests.rs | 72 ++++++++++++++++++- 6 files changed, 96 insertions(+), 4 deletions(-) diff --git a/crates/sui-cluster-test/src/test_case/coin_index_test.rs b/crates/sui-cluster-test/src/test_case/coin_index_test.rs index 53559fec0c02b..e3977af2602dc 100644 --- a/crates/sui-cluster-test/src/test_case/coin_index_test.rs +++ b/crates/sui-cluster-test/src/test_case/coin_index_test.rs @@ -167,6 +167,7 @@ impl TestCaseImpl for CoinIndexTest { args, None, rgp * 2_000_000, + None, ) .await .unwrap(); @@ -251,6 +252,7 @@ impl TestCaseImpl for CoinIndexTest { ], None, rgp * 2_000_000, + None, ) .await .unwrap(); @@ -316,6 +318,7 @@ impl TestCaseImpl for CoinIndexTest { args, None, rgp * 2_000_000, + None, ) .await .unwrap(); @@ -355,6 +358,7 @@ impl TestCaseImpl for CoinIndexTest { ], None, rgp * 2_000_000, + None, ) .await .unwrap(); @@ -386,6 +390,7 @@ impl TestCaseImpl for CoinIndexTest { ], None, rgp * 2_000_000, + None, ) .await .unwrap(); @@ -455,6 +460,7 @@ impl TestCaseImpl for CoinIndexTest { ], None, rgp * 2_000_000, + None, ) .await .unwrap(); @@ -701,6 +707,7 @@ async fn add_to_envelope( ], None, rgp * 2_000_000, + None, ) .await .unwrap(); diff --git a/crates/sui-json-rpc/src/transaction_builder_api.rs b/crates/sui-json-rpc/src/transaction_builder_api.rs index 22475afc202ed..3a691611beeae 100644 --- a/crates/sui-json-rpc/src/transaction_builder_api.rs +++ b/crates/sui-json-rpc/src/transaction_builder_api.rs @@ -263,6 +263,7 @@ impl TransactionBuilderServer for TransactionBuilderApi { rpc_arguments, gas, *gas_budget, + None, ) .await?, )?) diff --git a/crates/sui-sdk/examples/tic_tac_toe.rs b/crates/sui-sdk/examples/tic_tac_toe.rs index 69cf4c78c5c8c..aa8d327d24917 100644 --- a/crates/sui-sdk/examples/tic_tac_toe.rs +++ b/crates/sui-sdk/examples/tic_tac_toe.rs @@ -89,6 +89,7 @@ impl TicTacToe { ], None, // The node will pick a gas object belong to the signer if not provided. 1000, + None, ) .await?; @@ -188,6 +189,7 @@ impl TicTacToe { ], None, 1000, + None, ) .await?; diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/sui-transaction-builder/src/lib.rs index 469cb6837e97a..3cee3f78f7dcc 100644 --- a/crates/sui-transaction-builder/src/lib.rs +++ b/crates/sui-transaction-builder/src/lib.rs @@ -259,6 +259,7 @@ impl TransactionBuilder { call_args: Vec, gas: Option, gas_budget: u64, + gas_price: Option, ) -> anyhow::Result { let mut builder = ProgrammableTransactionBuilder::new(); self.single_move_call( @@ -279,7 +280,11 @@ impl TransactionBuilder { _ => None, }) .collect(); - let gas_price = self.0.get_reference_gas_price().await?; + let gas_price = if let Some(gas_price) = gas_price { + gas_price + } else { + self.0.get_reference_gas_price().await? + }; let gas = self .select_gas(signer, gas, gas_budget, input_objects, gas_price) .await?; diff --git a/crates/sui/src/client_commands.rs b/crates/sui/src/client_commands.rs index bc6fdfff76a49..8790185d16542 100644 --- a/crates/sui/src/client_commands.rs +++ b/crates/sui/src/client_commands.rs @@ -183,6 +183,10 @@ pub enum SuiClientCommands { #[clap(long)] gas_budget: u64, + /// Optional gas price for this call. Currently use only for testing and not in production enviroments. + #[clap(hide = true)] + gas_price: Option, + /// Instead of executing the transaction, serialize the bcs bytes of the unsigned transaction data /// (TransactionData) using base64 encoding, and print out the string . The string can /// be used to execute transaction with `sui client execute-signed-tx --tx-bytes `. @@ -1128,12 +1132,14 @@ impl SuiClientCommands { type_args, gas, gas_budget, + gas_price, args, serialize_unsigned_transaction, serialize_signed_transaction, } => { let tx_data = construct_move_call_transaction( - package, &module, &function, type_args, gas, gas_budget, args, context, + package, &module, &function, type_args, gas, gas_budget, gas_price, args, + context, ) .await?; serialize_or_execute!( @@ -2070,6 +2076,7 @@ async fn construct_move_call_transaction( type_args: Vec, gas: Option, gas_budget: u64, + gas_price: Option, args: Vec, context: &mut WalletContext, ) -> Result { @@ -2090,7 +2097,7 @@ async fn construct_move_call_transaction( client .transaction_builder() .move_call( - sender, package, module, function, type_args, args, gas, gas_budget, + sender, package, module, function, type_args, args, gas, gas_budget, gas_price, ) .await } diff --git a/crates/sui/tests/cli_tests.rs b/crates/sui/tests/cli_tests.rs index 7b218aa1c050e..c0a742475369d 100644 --- a/crates/sui/tests/cli_tests.rs +++ b/crates/sui/tests/cli_tests.rs @@ -32,7 +32,8 @@ use sui_config::{ use sui_json::SuiJsonValue; use sui_json_rpc_types::{ OwnedObjectRef, SuiObjectData, SuiObjectDataFilter, SuiObjectDataOptions, SuiObjectResponse, - SuiObjectResponseQuery, SuiTransactionBlockEffects, SuiTransactionBlockEffectsAPI, + SuiObjectResponseQuery, SuiTransactionBlockDataAPI, SuiTransactionBlockEffects, + SuiTransactionBlockEffectsAPI, }; use sui_keys::keystore::AccountKeystore; use sui_macros::sim_test; @@ -259,6 +260,7 @@ async fn test_ptb_publish_and_complex_arg_resolution() -> Result<(), anyhow::Err type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![], serialize_unsigned_transaction: false, serialize_signed_transaction: false, @@ -606,6 +608,7 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { args, gas: None, gas_budget: TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS * rgp, + gas_price: None, serialize_unsigned_transaction: false, serialize_signed_transaction: false, } @@ -645,6 +648,7 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { args: args.to_vec(), gas: Some(gas), gas_budget: TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS * rgp, + gas_price: None, serialize_unsigned_transaction: false, serialize_signed_transaction: false, } @@ -671,6 +675,7 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { args: args.to_vec(), gas: Some(gas), gas_budget: TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS * rgp, + gas_price: None, serialize_unsigned_transaction: false, serialize_signed_transaction: false, } @@ -679,6 +684,32 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { assert!(resp.is_err()); + // Try a transfer with explicitly set gas price. + // It should fail due to that gas price is below RGP. + let args = vec![ + SuiJsonValue::new(json!(created_obj))?, + SuiJsonValue::new(json!(address2))?, + ]; + + let resp = SuiClientCommands::Call { + package, + module: "object_basics".to_string(), + function: "transfer".to_string(), + type_args: vec![], + args: args.to_vec(), + gas: Some(gas), + gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS, + gas_price: Some(1), + serialize_unsigned_transaction: false, + serialize_signed_transaction: false, + } + .execute(context) + .await; + + assert!(resp.is_err()); + let err_string = format!("{} ", resp.err().unwrap()); + assert!(err_string.contains("Gas price 1 under reference gas price")); + // FIXME: uncomment once we figure out what is going on with `resolve_and_type_check` // let err_string = format!("{} ", resp.err().unwrap()); // let framework_addr = SUI_FRAMEWORK_ADDRESS.to_hex_literal(); @@ -699,12 +730,43 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { args: args.to_vec(), gas: Some(gas), gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS, + gas_price: None, serialize_unsigned_transaction: false, serialize_signed_transaction: false, } .execute(context) .await?; + // Try a call with customized gas price. + let args = vec![ + SuiJsonValue::new(json!("123"))?, + SuiJsonValue::new(json!(address1))?, + ]; + + let result = SuiClientCommands::Call { + package, + module: "object_basics".to_string(), + function: "create".to_string(), + type_args: vec![], + args, + gas: None, + gas_budget: TEST_ONLY_GAS_UNIT_FOR_OBJECT_BASICS * rgp, + gas_price: Some(12345), + serialize_unsigned_transaction: false, + serialize_signed_transaction: false, + } + .execute(context) + .await?; + + if let SuiClientCommandResult::Call(txn_response) = result { + assert_eq!( + txn_response.transaction.unwrap().data.gas_data().price, + 12345 + ); + } else { + panic!("Command failed with unexpected result.") + }; + Ok(()) } @@ -845,6 +907,7 @@ async fn test_delete_shared_object() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![], serialize_unsigned_transaction: false, serialize_signed_transaction: false, @@ -867,6 +930,7 @@ async fn test_delete_shared_object() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![SuiJsonValue::from_str(&shared_id.to_string()).unwrap()], serialize_unsigned_transaction: false, serialize_signed_transaction: false, @@ -952,6 +1016,7 @@ async fn test_receive_argument() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![], serialize_unsigned_transaction: false, serialize_signed_transaction: false, @@ -990,6 +1055,7 @@ async fn test_receive_argument() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![ SuiJsonValue::from_str(&parent.object_id.to_string()).unwrap(), SuiJsonValue::from_str(&child.object_id.to_string()).unwrap(), @@ -1078,6 +1144,7 @@ async fn test_receive_argument_by_immut_ref() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![], serialize_unsigned_transaction: false, serialize_signed_transaction: false, @@ -1116,6 +1183,7 @@ async fn test_receive_argument_by_immut_ref() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![ SuiJsonValue::from_str(&parent.object_id.to_string()).unwrap(), SuiJsonValue::from_str(&child.object_id.to_string()).unwrap(), @@ -1204,6 +1272,7 @@ async fn test_receive_argument_by_mut_ref() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![], serialize_unsigned_transaction: false, serialize_signed_transaction: false, @@ -1242,6 +1311,7 @@ async fn test_receive_argument_by_mut_ref() -> Result<(), anyhow::Error> { type_args: vec![], gas: None, gas_budget: rgp * TEST_ONLY_GAS_UNIT_FOR_PUBLISH, + gas_price: None, args: vec![ SuiJsonValue::from_str(&parent.object_id.to_string()).unwrap(), SuiJsonValue::from_str(&child.object_id.to_string()).unwrap(),