From a27b6aff85d15f3a5db26ba87b422cfb8f79a007 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Wed, 18 Sep 2024 07:52:29 +0400 Subject: [PATCH] fix(wallet)!: improve transactions.submit api (#1151) Description --- Submit an UnsignedTransaction to the wallet for signing. Split dry run from submit (`transactions.submit` and `transactions.submit_dry_run`) Remove no_fee setting from validator node Motivation and Context --- Avoid confusion with `transactions.submit` API by removing duplicate data in request. The result field is removed from the transactions.submit response as that only applies to dry run. Dry run is submitted by using a separate request that returns the result in the response. tari.js will need to be updated (PR https://github.com/tari-project/tari.js/pull/36) How Has This Been Tested? --- Cucumber, manually What process can a PR reviewer use to test or verify this change? --- Call `transactions.submit` Breaking Changes --- - [ ] None - [ ] Requires data directory to be deleted - [x] Other - Please specify BREAKING CHANGE: transactions.submit call has breaking changes, no_fees setting is removed from VNs (fees are required) --- .../src/command/transaction.rs | 203 ++++++++++-------- .../src/handlers/transaction.rs | 150 +++++++------ .../tari_dan_wallet_daemon/src/jrpc_server.rs | 1 + .../tari_validator_node/src/bootstrap.rs | 38 +--- .../tari_validator_node/src/config.rs | 3 - .../tari_validator_node/src/consensus/mod.rs | 21 +- .../CallInstructionRequest.d.ts | 1 - .../TransactionSubmitDryRunRequest.d.ts | 9 + .../TransactionSubmitDryRunRequest.js | 1 + .../TransactionSubmitDryRunResponse.d.ts | 6 + .../TransactionSubmitDryRunResponse.js | 1 + .../TransactionSubmitRequest.d.ts | 13 +- .../TransactionSubmitResponse.d.ts | 5 - .../TransactionSubmitResponse.js | 1 + bindings/dist/wallet-daemon-client.d.ts | 2 + bindings/dist/wallet-daemon-client.js | 2 + bindings/package.json | 2 +- .../CallInstructionRequest.ts | 1 - .../TransactionSubmitDryRunRequest.ts | 11 + .../TransactionSubmitDryRunResponse.ts | 8 + .../TransactionSubmitRequest.ts | 13 +- .../TransactionSubmitResponse.ts | 5 - bindings/src/wallet-daemon-client.ts | 2 + clients/wallet_daemon_client/src/lib.rs | 10 +- clients/wallet_daemon_client/src/types.rs | 49 +++-- dan_layer/transaction/src/builder.rs | 2 +- integration_tests/src/lib.rs | 4 - integration_tests/src/validator_node.rs | 2 - integration_tests/src/wallet_daemon_cli.rs | 139 ++++++------ integration_tests/tests/cucumber.rs | 6 - .../tests/features/committee.feature | 2 - .../tests/features/counter.feature | 1 - .../tests/features/fungible.feature | 35 ++- integration_tests/tests/features/nft.feature | 72 +++---- .../tests/features/transfer.feature | 2 - .../tests/features/wallet_daemon.feature | 9 +- 36 files changed, 429 insertions(+), 403 deletions(-) create mode 100644 bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.d.ts create mode 100644 bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.js create mode 100644 bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.d.ts create mode 100644 bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.js create mode 100644 bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunRequest.ts create mode 100644 bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunResponse.ts diff --git a/applications/tari_dan_wallet_cli/src/command/transaction.rs b/applications/tari_dan_wallet_cli/src/command/transaction.rs index 45c0ac99d..4ce73e2d3 100644 --- a/applications/tari_dan_wallet_cli/src/command/transaction.rs +++ b/applications/tari_dan_wallet_cli/src/command/transaction.rs @@ -53,7 +53,7 @@ use tari_template_lib::{ models::{Amount, BucketId, NonFungibleAddress, NonFungibleId}, prelude::ResourceAddress, }; -use tari_transaction::TransactionId; +use tari_transaction::{Transaction, TransactionId, UnsignedTransaction}; use tari_transaction_manifest::{parse_manifest, ManifestValue}; use tari_utilities::{hex::to_hex, ByteArray}; use tari_wallet_daemon_client::{ @@ -62,8 +62,8 @@ use tari_wallet_daemon_client::{ AccountsTransferRequest, ConfidentialTransferRequest, TransactionGetResultRequest, + TransactionSubmitDryRunRequest, TransactionSubmitRequest, - TransactionSubmitResponse, TransactionWaitResultRequest, TransactionWaitResultResponse, }, @@ -104,8 +104,8 @@ pub struct CommonSubmitArgs { pub num_outputs: Option, #[clap(long, short = 'i')] pub inputs: Vec, - #[clap(long, short = 'o')] - pub override_inputs: Option, + #[clap(long, alias = "autofill")] + pub detect_inputs: Option, #[clap(long, short = 'v')] pub version: Option, #[clap(long, short = 'd')] @@ -241,41 +241,53 @@ pub async fn handle_submit(args: SubmitArgs, client: &mut WalletDaemonClient) -> fee_account = client.accounts_get_default().await?.account; } - let mut instructions = vec![instruction]; + let mut builder = Transaction::builder() + .fee_transaction_pay_from_component( + fee_account.address.as_component_address().unwrap(), + Amount::try_from(common.max_fee.unwrap_or(1000))?, + ) + .add_instruction(instruction) + .with_inputs(common.inputs) + .with_min_epoch(common.min_epoch.map(Epoch)) + .with_max_epoch(common.max_epoch.map(Epoch)); + if let Some(dump_account) = common.dump_outputs_into { - instructions.push(Instruction::PutLastInstructionOutputOnWorkspace { - key: b"bucket".to_vec(), - }); - let AccountGetResponse { - account: dump_account2, .. - } = client.accounts_get(dump_account).await?; - - instructions.push(Instruction::CallMethod { - component_address: dump_account2.address.as_component_address().unwrap(), - method: "deposit".to_string(), - args: args![Variable("bucket")], - }); + let AccountGetResponse { account, .. } = client.accounts_get(dump_account).await?; + + builder = builder.put_last_instruction_output_on_workspace("bucket").call_method( + account.address.as_component_address().unwrap(), + "deposit", + args![Workspace("bucket")], + ); } - let fee_instructions = vec![Instruction::CallMethod { - component_address: fee_account.address.as_component_address().unwrap(), - method: "pay_fee".to_string(), - args: args![Amount::try_from(common.max_fee.unwrap_or(1000))?], - }]; - - let request = TransactionSubmitRequest { - transaction: None, - signing_key_index: None, - fee_instructions, - instructions, - inputs: common.inputs, - override_inputs: common.override_inputs.unwrap_or_default(), - is_dry_run: common.dry_run, - proof_ids: vec![], - min_epoch: common.min_epoch.map(Epoch), - max_epoch: common.max_epoch.map(Epoch), - }; - submit_transaction(request, client).await?; + let transaction = builder.build_unsigned_transaction(); + summarize_transaction(&transaction); + + if common.dry_run { + println!("NOTE: Dry run is enabled. This transaction will not be processed by the network."); + println!(); + let resp = client + .submit_transaction_dry_run(TransactionSubmitDryRunRequest { + transaction, + signing_key_index: None, + autofill_inputs: vec![], + detect_inputs: common.detect_inputs.unwrap_or(true), + proof_ids: vec![], + }) + .await?; + wait_transaction_result(resp.transaction_id, client).await?; + } else { + let request = TransactionSubmitRequest { + transaction, + signing_key_index: None, + autofill_inputs: vec![], + detect_inputs: common.detect_inputs.unwrap_or(true), + proof_ids: vec![], + }; + let resp = client.submit_transaction(&request).await?; + wait_transaction_result(resp.transaction_id, client).await?; + } Ok(()) } @@ -283,6 +295,7 @@ async fn handle_submit_manifest( args: SubmitManifestArgs, client: &mut WalletDaemonClient, ) -> Result<(), anyhow::Error> { + let timer = Instant::now(); let contents = fs::read_to_string(&args.manifest).map_err(|e| anyhow!("Failed to read manifest: {}", e))?; let instructions = parse_manifest(&contents, parse_globals(args.input_variables)?, Default::default())?; let common = args.common; @@ -294,28 +307,53 @@ async fn handle_submit_manifest( fee_account = client.accounts_get_default().await?.account; } - let request = TransactionSubmitRequest { - transaction: None, - signing_key_index: None, - fee_instructions: instructions - .fee_instructions - .into_iter() - .chain(vec![Instruction::CallMethod { - component_address: fee_account.address.as_component_address().unwrap(), - method: "pay_fee".to_string(), - args: args![Amount::try_from(common.max_fee.unwrap_or(1000))?], - }]) - .collect(), - instructions: instructions.instructions, - inputs: common.inputs, - override_inputs: common.override_inputs.unwrap_or_default(), - is_dry_run: common.dry_run, - proof_ids: vec![], - min_epoch: common.min_epoch.map(Epoch), - max_epoch: common.max_epoch.map(Epoch), - }; + let builder = Transaction::builder() + .with_fee_instructions( + instructions + .fee_instructions + .into_iter() + .chain(vec![Instruction::CallMethod { + component_address: fee_account.address.as_component_address().unwrap(), + method: "pay_fee".to_string(), + args: args![Amount::try_from(common.max_fee.unwrap_or(1000))?], + }]) + .collect(), + ) + .with_instructions(instructions.instructions) + .with_inputs(common.inputs) + .with_min_epoch(common.min_epoch.map(Epoch)) + .with_max_epoch(common.max_epoch.map(Epoch)); + + let transaction = builder.build_unsigned_transaction(); + summarize_transaction(&transaction); + + if common.dry_run { + println!("NOTE: Dry run is enabled. This transaction will not be processed by the network."); + println!(); + + let resp = client + .submit_transaction_dry_run(TransactionSubmitDryRunRequest { + transaction, + signing_key_index: None, + autofill_inputs: vec![], + detect_inputs: common.detect_inputs.unwrap_or(true), + proof_ids: vec![], + }) + .await?; + summarize(&resp.result.finalize, timer.elapsed()); + } else { + let request = TransactionSubmitRequest { + transaction, + signing_key_index: None, + autofill_inputs: vec![], + detect_inputs: common.detect_inputs.unwrap_or(true), + proof_ids: vec![], + }; + + let resp = client.submit_transaction(&request).await?; + wait_transaction_result(resp.transaction_id, client).await?; + } - submit_transaction(request, client).await?; Ok(()) } @@ -389,26 +427,22 @@ pub async fn handle_confidential_transfer( Ok(()) } -pub async fn submit_transaction( - request: TransactionSubmitRequest, +pub async fn wait_transaction_result( + transaction_id: TransactionId, client: &mut WalletDaemonClient, -) -> Result { +) -> Result { let timer = Instant::now(); - let resp = client.submit_transaction(&request).await?; - println!(); - println!("✅ Transaction {} submitted.", resp.transaction_id); + println!("✅ Transaction {} submitted.", transaction_id); println!(); - // TODO: Would be great if we could display the substate ids as well as substate addresses - summarize_request(&request, &resp.inputs); println!(); println!("⏳️ Waiting for transaction result..."); println!(); let wait_resp = client .wait_transaction_result(TransactionWaitResultRequest { - transaction_id: resp.transaction_id, + transaction_id, // Never timeout, you can ctrl+c to exit timeout_secs: None, }) @@ -416,41 +450,38 @@ pub async fn submit_transaction( if wait_resp.timed_out { println!("⏳️ Transaction result timed out.",); println!(); + } else if let Some(ref result) = wait_resp.result { + summarize(result, timer.elapsed()); } else { - summarize(&wait_resp, timer.elapsed()); + println!("⚠️ Transaction not finalized"); } - Ok(resp) + Ok(wait_resp) } -fn summarize_request(request: &TransactionSubmitRequest, inputs: &[SubstateRequirement]) { - if request.is_dry_run { - println!("NOTE: Dry run is enabled. This transaction will not be processed by the network."); - println!(); - } +fn summarize_transaction(transaction: &UnsignedTransaction) { println!("Inputs:"); - if inputs.is_empty() { + if transaction.inputs().is_empty() { println!(" None"); } else { - for address in inputs { - println!("- {}", address); + for req in transaction.inputs() { + println!("- {}", req); } } println!(); println!("🌟 Submitting fee instructions:"); - for instruction in &request.fee_instructions { + for instruction in &transaction.fee_instructions { println!("- {}", instruction); } println!(); println!("🌟 Submitting instructions:"); - for instruction in &request.instructions { + for instruction in &transaction.instructions { println!("- {}", instruction); } println!(); } -#[allow(clippy::too_many_lines)] -fn summarize(resp: &TransactionWaitResultResponse, time_taken: Duration) { +fn summarize(result: &FinalizeResult, time_taken: Duration) { println!("✅️ Transaction complete"); println!(); // if let Some(qc) = resp.qcs.first() { @@ -462,19 +493,13 @@ fn summarize(resp: &TransactionWaitResultResponse, time_taken: Duration) { // } // println!(); - if let Some(ref result) = resp.result { - summarize_finalize_result(result); - } + summarize_finalize_result(result); println!(); - println!("Fee: {}", resp.final_fee); + println!("Fee: {}", result.fee_receipt.total_fees_charged()); println!("Time taken: {:?}", time_taken); println!(); - if let Some(ref result) = resp.result { - println!("OVERALL DECISION: {}", result.result); - } else { - println!("STATUS: {:?}", resp.status); - } + println!("OVERALL DECISION: {}", result.result); } pub fn print_substate_diff(diff: &SubstateDiff) { diff --git a/applications/tari_dan_wallet_daemon/src/handlers/transaction.rs b/applications/tari_dan_wallet_daemon/src/handlers/transaction.rs index 2a6b52f3a..0aa16636c 100644 --- a/applications/tari_dan_wallet_daemon/src/handlers/transaction.rs +++ b/applications/tari_dan_wallet_daemon/src/handlers/transaction.rs @@ -6,7 +6,7 @@ use anyhow::anyhow; use futures::{future, future::Either}; use log::*; use tari_dan_app_utilities::json_encoding; -use tari_dan_common_types::{optional::Optional, Epoch}; +use tari_dan_common_types::{optional::Optional, Epoch, SubstateRequirement}; use tari_dan_wallet_sdk::apis::{jwt::JrpcPermission, key_manager}; use tari_engine_types::{indexed_value::IndexedValue, instruction::Instruction, substate::SubstateId}; use tari_template_lib::{args, args::Arg, models::Amount}; @@ -21,6 +21,8 @@ use tari_wallet_daemon_client::types::{ TransactionGetResponse, TransactionGetResultRequest, TransactionGetResultResponse, + TransactionSubmitDryRunRequest, + TransactionSubmitDryRunResponse, TransactionSubmitRequest, TransactionSubmitResponse, TransactionWaitResultRequest, @@ -71,16 +73,11 @@ pub async fn handle_submit_instruction( .build_unsigned_transaction(); let request = TransactionSubmitRequest { - transaction: Some(transaction), + transaction, signing_key_index: Some(fee_account.key_index), - fee_instructions: vec![], - instructions: vec![], - inputs: req.inputs, - override_inputs: req.override_inputs.unwrap_or_default(), - is_dry_run: req.is_dry_run, + autofill_inputs: vec![], + detect_inputs: req.override_inputs.unwrap_or_default(), proof_ids: vec![], - min_epoch: None, - max_epoch: None, }; handle_submit(context, token, request).await } @@ -99,42 +96,33 @@ pub async fn handle_submit( // TODO: Ideally the SDK should take care of signing the transaction internally let (_, key) = key_api.get_key_or_active(key_manager::TRANSACTION_BRANCH, req.signing_key_index)?; - let inputs = if req.override_inputs { - req.inputs - } else { + let autofill_inputs = req.autofill_inputs; + let detected_inputs = if req.detect_inputs { // If we are not overriding inputs, we will use inputs that we know about in the local substate id db - let mut substates = get_referenced_substate_addresses( - req.transaction - .as_ref() - .map(|t| &t.instructions) - .unwrap_or(&req.instructions), - )?; - substates.extend(get_referenced_substate_addresses( - req.transaction - .as_ref() - .map(|t| &t.fee_instructions) - .unwrap_or(&req.fee_instructions), - )?); + let mut substates = get_referenced_substate_addresses(&req.transaction.instructions)?; + substates.extend(get_referenced_substate_addresses(&req.transaction.fee_instructions)?); let substates = substates.into_iter().collect::>(); - let loaded_dependent_substates = sdk.substate_api().locate_dependent_substates(&substates).await?; - [req.inputs, loaded_dependent_substates].concat() - }; - - let transaction = if let Some(transaction) = req.transaction { - Transaction::builder() - .with_unsigned_transaction(transaction) - .sign(&key.key) - .build() + let loaded_substates = sdk.substate_api().locate_dependent_substates(&substates).await?; + loaded_substates + .into_iter() + .chain(substates.into_iter().map(SubstateRequirement::unversioned)) + .collect() } else { - Transaction::builder() - .with_instructions(req.instructions) - .with_fee_instructions(req.fee_instructions) - .with_min_epoch(req.min_epoch) - .with_max_epoch(req.max_epoch) - .sign(&key.key) - .build() + vec![] }; + info!( + target: LOG_TARGET, + "Detected {} input(s)", + detected_inputs.len() + ); + + let transaction = Transaction::builder() + .with_unsigned_transaction(req.transaction) + .with_inputs(detected_inputs) + .sign(&key.key) + .build(); + for proof_id in req.proof_ids { // update the proofs table with the corresponding transaction hash sdk.confidential_outputs_api() @@ -146,33 +134,69 @@ pub async fn handle_submit( "Submitted transaction with hash {}", transaction.hash() ); - if req.is_dry_run { - let exec_result = context - .transaction_service() - .submit_dry_run_transaction(transaction, inputs.clone()) - .await?; - let json_result = json_encoding::encode_finalize_result_into_json(&exec_result.finalize)?; + let transaction_id = context + .transaction_service() + .submit_transaction(transaction, autofill_inputs.clone()) + .await?; - Ok(TransactionSubmitResponse { - transaction_id: exec_result.finalize.transaction_hash.into_array().into(), - result: Some(exec_result), - json_result: Some(json_result), - inputs, - }) + Ok(TransactionSubmitResponse { transaction_id }) +} + +pub async fn handle_submit_dry_run( + context: &HandlerContext, + token: Option, + req: TransactionSubmitDryRunRequest, +) -> Result { + let sdk = context.wallet_sdk(); + // TODO: fine-grained checks of individual addresses involved (resources, components, etc) + sdk.jwt_api() + .check_auth(token, &[JrpcPermission::TransactionSend(None)])?; + let key_api = sdk.key_manager_api(); + // Fetch the key to sign the transaction + // TODO: Ideally the SDK should take care of signing the transaction internally + let (_, key) = key_api.get_key_or_active(key_manager::TRANSACTION_BRANCH, req.signing_key_index)?; + + let autofill_inputs = req.autofill_inputs; + let detected_inputs = if req.detect_inputs { + // If we are not overriding inputs, we will use inputs that we know about in the local substate id db + let mut substates = get_referenced_substate_addresses(&req.transaction.instructions)?; + substates.extend(get_referenced_substate_addresses(&req.transaction.fee_instructions)?); + let substates = substates.into_iter().collect::>(); + sdk.substate_api().locate_dependent_substates(&substates).await? } else { - let transaction_id = context - .transaction_service() - .submit_transaction(transaction, inputs.clone()) - .await?; - - Ok(TransactionSubmitResponse { - transaction_id, - inputs, - result: None, - json_result: None, - }) + vec![] + }; + + let transaction = Transaction::builder() + .with_unsigned_transaction(req.transaction) + .with_inputs(detected_inputs) + .sign(&key.key) + .build(); + + for proof_id in req.proof_ids { + // update the proofs table with the corresponding transaction hash + sdk.confidential_outputs_api() + .proofs_set_transaction_hash(proof_id, *transaction.id())?; } + + info!( + target: LOG_TARGET, + "Submitted transaction with hash {}", + transaction.hash() + ); + let exec_result = context + .transaction_service() + .submit_dry_run_transaction(transaction, autofill_inputs.clone()) + .await?; + + let json_result = json_encoding::encode_finalize_result_into_json(&exec_result.finalize)?; + + Ok(TransactionSubmitDryRunResponse { + transaction_id: exec_result.finalize.transaction_hash.into_array().into(), + result: exec_result, + json_result, + }) } pub async fn handle_get( diff --git a/applications/tari_dan_wallet_daemon/src/jrpc_server.rs b/applications/tari_dan_wallet_daemon/src/jrpc_server.rs index eb80ca542..fc554e7e0 100644 --- a/applications/tari_dan_wallet_daemon/src/jrpc_server.rs +++ b/applications/tari_dan_wallet_daemon/src/jrpc_server.rs @@ -123,6 +123,7 @@ async fn handler( Some(("transactions", method)) => match method { "submit_instruction" => call_handler(context, value, token, transaction::handle_submit_instruction).await, "submit" => call_handler(context, value, token, transaction::handle_submit).await, + "submit_dry_run" => call_handler(context, value, token, transaction::handle_submit_dry_run).await, "get" => call_handler(context, value, token, transaction::handle_get).await, "get_result" => call_handler(context, value, token, transaction::handle_get_result).await, "wait_result" => call_handler(context, value, token, transaction::handle_wait_result).await, diff --git a/applications/tari_validator_node/src/bootstrap.rs b/applications/tari_validator_node/src/bootstrap.rs index 3add8e857..bf2725a9f 100644 --- a/applications/tari_validator_node/src/bootstrap.rs +++ b/applications/tari_validator_node/src/bootstrap.rs @@ -117,7 +117,6 @@ use crate::{ validator_registration_file::ValidatorRegistrationFile, virtual_substate::VirtualSubstateManager, ApplicationConfig, - ValidatorNodeConfig, }; const LOG_TARGET: &str = "tari::validator_node::bootstrap"; @@ -233,15 +232,11 @@ pub async fn spawn_services( info!(target: LOG_TARGET, "Payload processor initializing"); // Payload processor - let fee_table = if config.validator_node.no_fees { - FeeTable::zero_rated() - } else { - FeeTable { - per_module_call_cost: 1, - per_byte_storage_cost: 1, - per_event_cost: 1, - per_log_cost: 1, - } + let fee_table = FeeTable { + per_module_call_cost: 1, + per_byte_storage_cost: 1, + per_event_cost: 1, + per_log_cost: 1, }; // Messaging @@ -261,7 +256,7 @@ pub async fn spawn_services( let payload_processor = TariDanTransactionProcessor::new(config.network, template_manager.clone(), fee_table); let transaction_executor = TariDanBlockTransactionExecutor::new( payload_processor.clone(), - consensus::create_transaction_validator(&config.validator_node, template_manager.clone()), + consensus::create_transaction_validator(template_manager.clone()).boxed(), ); #[cfg(feature = "metrics")] @@ -295,7 +290,7 @@ pub async fn spawn_services( consensus_constants.num_preshards, gossip, epoch_manager.clone(), - create_mempool_transaction_validator(&config.validator_node, template_manager.clone()), + create_mempool_transaction_validator(template_manager.clone()), state_store.clone(), consensus_handle.clone(), #[cfg(feature = "metrics")] @@ -600,22 +595,9 @@ where } fn create_mempool_transaction_validator( - config: &ValidatorNodeConfig, template_manager: TemplateManager, ) -> impl Validator { - let mut validator = TemplateExistsValidator::new(template_manager).boxed(); - if !config.no_fees { - // A transaction without fee payment may have 0 inputs. - validator = HasInputs::new() - .and_then(validator) - .and_then(FeeTransactionValidator) - .boxed(); - } - validator + HasInputs::new() + .and_then(TemplateExistsValidator::new(template_manager)) + .and_then(FeeTransactionValidator) } - -// fn create_mempool_after_execute_validator( -// store: SqliteStateStore, -// ) -> impl MempoolValidator { -// HasInvolvedShards::new().and_then(OutputsDontExistLocally::new(store)) -// } diff --git a/applications/tari_validator_node/src/config.rs b/applications/tari_validator_node/src/config.rs index a5f4073bb..e00911b02 100644 --- a/applications/tari_validator_node/src/config.rs +++ b/applications/tari_validator_node/src/config.rs @@ -97,8 +97,6 @@ pub struct ValidatorNodeConfig { pub http_ui_listener_address: Option, /// Template config pub templates: TemplateConfig, - /// Dont charge fees - pub no_fees: bool, /// Fee claim public key pub fee_claim_public_key: RistrettoPublicKey, /// Create identity file if not exists @@ -146,7 +144,6 @@ impl Default for ValidatorNodeConfig { json_rpc_public_address: None, http_ui_listener_address: Some("127.0.0.1:5001".parse().unwrap()), templates: TemplateConfig::default(), - no_fees: false, // Burn your fees fee_claim_public_key: RistrettoPublicKey::default(), dont_create_id: false, diff --git a/applications/tari_validator_node/src/consensus/mod.rs b/applications/tari_validator_node/src/consensus/mod.rs index edfab7026..f0d22bf67 100644 --- a/applications/tari_validator_node/src/consensus/mod.rs +++ b/applications/tari_validator_node/src/consensus/mod.rs @@ -39,7 +39,6 @@ use crate::{ TransactionValidationError, }, validator::{BoxedValidator, Validator}, - ValidatorNodeConfig, }; mod block_transaction_executor; @@ -130,26 +129,18 @@ pub async fn spawn( } pub fn create_transaction_validator( - config: &ValidatorNodeConfig, template_manager: TemplateManager, -) -> ConsensusTransactionValidator { - let mut validator = WithContext::::new() +) -> impl Validator { + WithContext::::new() .map_context( |_| (), - TransactionSignatureValidator.and_then(TemplateExistsValidator::new(template_manager)), + HasInputs::new() + .and_then(TransactionSignatureValidator) + .and_then(TemplateExistsValidator::new(template_manager)), ) .map_context( |c| c.current_epoch, EpochRangeValidator::new().and_then(ClaimFeeTransactionValidator::new()), ) - .boxed(); - if !config.no_fees { - // A transaction without fee payment may have 0 inputs. - validator = WithContext::::new() - .map_context(|_| (), HasInputs::new()) - .and_then(validator) - .map_context(|_| (), FeeTransactionValidator) - .boxed(); - } - validator + .map_context(|_| (), FeeTransactionValidator) } diff --git a/bindings/dist/types/wallet-daemon-client/CallInstructionRequest.d.ts b/bindings/dist/types/wallet-daemon-client/CallInstructionRequest.d.ts index a689f89be..23110a298 100644 --- a/bindings/dist/types/wallet-daemon-client/CallInstructionRequest.d.ts +++ b/bindings/dist/types/wallet-daemon-client/CallInstructionRequest.d.ts @@ -9,7 +9,6 @@ export interface CallInstructionRequest { inputs: Array; override_inputs: boolean | null; new_outputs: number | null; - is_dry_run: boolean; proof_ids: Array; min_epoch: number | null; max_epoch: number | null; diff --git a/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.d.ts b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.d.ts new file mode 100644 index 000000000..17c81c729 --- /dev/null +++ b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.d.ts @@ -0,0 +1,9 @@ +import type { SubstateRequirement } from "../SubstateRequirement"; +import type { UnsignedTransaction } from "../UnsignedTransaction"; +export interface TransactionSubmitDryRunRequest { + transaction: UnsignedTransaction; + signing_key_index: number | null; + autofill_inputs: Array; + detect_inputs: boolean; + proof_ids: Array; +} diff --git a/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.js b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.js new file mode 100644 index 000000000..cb0ff5c3b --- /dev/null +++ b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunRequest.js @@ -0,0 +1 @@ +export {}; diff --git a/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.d.ts b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.d.ts new file mode 100644 index 000000000..b2232efcc --- /dev/null +++ b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.d.ts @@ -0,0 +1,6 @@ +import type { ExecuteResult } from "../ExecuteResult"; +export interface TransactionSubmitDryRunResponse { + transaction_id: string; + result: ExecuteResult; + json_result: Array; +} diff --git a/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.js b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.js new file mode 100644 index 000000000..cb0ff5c3b --- /dev/null +++ b/bindings/dist/types/wallet-daemon-client/TransactionSubmitDryRunResponse.js @@ -0,0 +1 @@ +export {}; diff --git a/bindings/dist/types/wallet-daemon-client/TransactionSubmitRequest.d.ts b/bindings/dist/types/wallet-daemon-client/TransactionSubmitRequest.d.ts index fb666306e..178068063 100644 --- a/bindings/dist/types/wallet-daemon-client/TransactionSubmitRequest.d.ts +++ b/bindings/dist/types/wallet-daemon-client/TransactionSubmitRequest.d.ts @@ -1,16 +1,9 @@ -import type { Epoch } from "../Epoch"; -import type { Instruction } from "../Instruction"; import type { SubstateRequirement } from "../SubstateRequirement"; import type { UnsignedTransaction } from "../UnsignedTransaction"; export interface TransactionSubmitRequest { - transaction: UnsignedTransaction | null; + transaction: UnsignedTransaction; signing_key_index: number | null; - inputs: Array; - override_inputs: boolean; - is_dry_run: boolean; + autofill_inputs: Array; + detect_inputs: boolean; proof_ids: Array; - fee_instructions: Array; - instructions: Array; - min_epoch: Epoch | null; - max_epoch: Epoch | null; } diff --git a/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.d.ts b/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.d.ts index 4d2971ad3..f27eb6e91 100644 --- a/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.d.ts +++ b/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.d.ts @@ -1,8 +1,3 @@ -import type { ExecuteResult } from "../ExecuteResult"; -import type { SubstateRequirement } from "../SubstateRequirement"; export interface TransactionSubmitResponse { transaction_id: string; - inputs: Array; - result: ExecuteResult | null; - json_result: Array | null; } diff --git a/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.js b/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.js index cb0ff5c3b..e5b481d1e 100644 --- a/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.js +++ b/bindings/dist/types/wallet-daemon-client/TransactionSubmitResponse.js @@ -1 +1,2 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export {}; diff --git a/bindings/dist/wallet-daemon-client.d.ts b/bindings/dist/wallet-daemon-client.d.ts index 6e37656ae..72464d0b9 100644 --- a/bindings/dist/wallet-daemon-client.d.ts +++ b/bindings/dist/wallet-daemon-client.d.ts @@ -56,6 +56,7 @@ export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofRequest"; export * from "./types/wallet-daemon-client/KeysCreateRequest"; export * from "./types/wallet-daemon-client/SettingsGetResponse"; +export * from "./types/wallet-daemon-client/TransactionSubmitDryRunResponse"; export * from "./types/wallet-daemon-client/ClaimBurnResponse"; export * from "./types/wallet-daemon-client/WebRtcStartRequest"; export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; @@ -79,6 +80,7 @@ export * from "./types/wallet-daemon-client/SubstatesListResponse"; export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; export * from "./types/wallet-daemon-client/ComponentAddressOrName"; export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; +export * from "./types/wallet-daemon-client/TransactionSubmitDryRunRequest"; export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; diff --git a/bindings/dist/wallet-daemon-client.js b/bindings/dist/wallet-daemon-client.js index 3c54e676a..389f4730a 100644 --- a/bindings/dist/wallet-daemon-client.js +++ b/bindings/dist/wallet-daemon-client.js @@ -58,6 +58,7 @@ export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofRequest"; export * from "./types/wallet-daemon-client/KeysCreateRequest"; export * from "./types/wallet-daemon-client/SettingsGetResponse"; +export * from "./types/wallet-daemon-client/TransactionSubmitDryRunResponse"; export * from "./types/wallet-daemon-client/ClaimBurnResponse"; export * from "./types/wallet-daemon-client/WebRtcStartRequest"; export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; @@ -81,6 +82,7 @@ export * from "./types/wallet-daemon-client/SubstatesListResponse"; export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; export * from "./types/wallet-daemon-client/ComponentAddressOrName"; export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; +export * from "./types/wallet-daemon-client/TransactionSubmitDryRunRequest"; export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; diff --git a/bindings/package.json b/bindings/package.json index 4a0c9b3fc..f3e188ef0 100644 --- a/bindings/package.json +++ b/bindings/package.json @@ -1,6 +1,6 @@ { "name": "@tari-project/typescript-bindings", - "version": "1.0.6", + "version": "1.1.0", "description": "", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/bindings/src/types/wallet-daemon-client/CallInstructionRequest.ts b/bindings/src/types/wallet-daemon-client/CallInstructionRequest.ts index 6e74b7657..720aea6ae 100644 --- a/bindings/src/types/wallet-daemon-client/CallInstructionRequest.ts +++ b/bindings/src/types/wallet-daemon-client/CallInstructionRequest.ts @@ -11,7 +11,6 @@ export interface CallInstructionRequest { inputs: Array; override_inputs: boolean | null; new_outputs: number | null; - is_dry_run: boolean; proof_ids: Array; min_epoch: number | null; max_epoch: number | null; diff --git a/bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunRequest.ts b/bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunRequest.ts new file mode 100644 index 000000000..6d9106d47 --- /dev/null +++ b/bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunRequest.ts @@ -0,0 +1,11 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SubstateRequirement } from "../SubstateRequirement"; +import type { UnsignedTransaction } from "../UnsignedTransaction"; + +export interface TransactionSubmitDryRunRequest { + transaction: UnsignedTransaction; + signing_key_index: number | null; + autofill_inputs: Array; + detect_inputs: boolean; + proof_ids: Array; +} diff --git a/bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunResponse.ts b/bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunResponse.ts new file mode 100644 index 000000000..39090a692 --- /dev/null +++ b/bindings/src/types/wallet-daemon-client/TransactionSubmitDryRunResponse.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ExecuteResult } from "../ExecuteResult"; + +export interface TransactionSubmitDryRunResponse { + transaction_id: string; + result: ExecuteResult; + json_result: Array; +} diff --git a/bindings/src/types/wallet-daemon-client/TransactionSubmitRequest.ts b/bindings/src/types/wallet-daemon-client/TransactionSubmitRequest.ts index 6650403c4..24f718330 100644 --- a/bindings/src/types/wallet-daemon-client/TransactionSubmitRequest.ts +++ b/bindings/src/types/wallet-daemon-client/TransactionSubmitRequest.ts @@ -1,18 +1,11 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { Epoch } from "../Epoch"; -import type { Instruction } from "../Instruction"; import type { SubstateRequirement } from "../SubstateRequirement"; import type { UnsignedTransaction } from "../UnsignedTransaction"; export interface TransactionSubmitRequest { - transaction: UnsignedTransaction | null; + transaction: UnsignedTransaction; signing_key_index: number | null; - inputs: Array; - override_inputs: boolean; - is_dry_run: boolean; + autofill_inputs: Array; + detect_inputs: boolean; proof_ids: Array; - fee_instructions: Array; - instructions: Array; - min_epoch: Epoch | null; - max_epoch: Epoch | null; } diff --git a/bindings/src/types/wallet-daemon-client/TransactionSubmitResponse.ts b/bindings/src/types/wallet-daemon-client/TransactionSubmitResponse.ts index 36cc91cbd..4dfd072cb 100644 --- a/bindings/src/types/wallet-daemon-client/TransactionSubmitResponse.ts +++ b/bindings/src/types/wallet-daemon-client/TransactionSubmitResponse.ts @@ -1,10 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ExecuteResult } from "../ExecuteResult"; -import type { SubstateRequirement } from "../SubstateRequirement"; export interface TransactionSubmitResponse { transaction_id: string; - inputs: Array; - result: ExecuteResult | null; - json_result: Array | null; } diff --git a/bindings/src/wallet-daemon-client.ts b/bindings/src/wallet-daemon-client.ts index 140f65141..7c10e0ad2 100644 --- a/bindings/src/wallet-daemon-client.ts +++ b/bindings/src/wallet-daemon-client.ts @@ -59,6 +59,7 @@ export * from "./types/wallet-daemon-client/ClaimValidatorFeesResponse"; export * from "./types/wallet-daemon-client/ConfidentialCreateOutputProofRequest"; export * from "./types/wallet-daemon-client/KeysCreateRequest"; export * from "./types/wallet-daemon-client/SettingsGetResponse"; +export * from "./types/wallet-daemon-client/TransactionSubmitDryRunResponse"; export * from "./types/wallet-daemon-client/ClaimBurnResponse"; export * from "./types/wallet-daemon-client/WebRtcStartRequest"; export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceResponse"; @@ -82,6 +83,7 @@ export * from "./types/wallet-daemon-client/SubstatesListResponse"; export * from "./types/wallet-daemon-client/KeysSetActiveResponse"; export * from "./types/wallet-daemon-client/ComponentAddressOrName"; export * from "./types/wallet-daemon-client/AuthLoginAcceptRequest"; +export * from "./types/wallet-daemon-client/TransactionSubmitDryRunRequest"; export * from "./types/wallet-daemon-client/KeysSetActiveRequest"; export * from "./types/wallet-daemon-client/ConfidentialViewVaultBalanceRequest"; export * from "./types/wallet-daemon-client/ConfidentialTransferRequest"; diff --git a/clients/wallet_daemon_client/src/lib.rs b/clients/wallet_daemon_client/src/lib.rs index 4f5c816ad..e063f04e5 100644 --- a/clients/wallet_daemon_client/src/lib.rs +++ b/clients/wallet_daemon_client/src/lib.rs @@ -53,7 +53,6 @@ use types::{ AuthLoginDenyResponse, AuthLoginRequest, AuthLoginResponse, - CallInstructionRequest, ClaimBurnRequest, ClaimBurnResponse, GetAccountNftRequest, @@ -115,6 +114,8 @@ use crate::{ TransactionGetResponse, TransactionGetResultRequest, TransactionGetResultResponse, + TransactionSubmitDryRunRequest, + TransactionSubmitDryRunResponse, TransactionSubmitRequest, TransactionSubmitResponse, TransactionWaitResultRequest, @@ -274,12 +275,11 @@ impl WalletDaemonClient { self.send_request("transactions.submit", request.borrow()).await } - pub async fn submit_instruction>( + pub async fn submit_transaction_dry_run>( &mut self, request: T, - ) -> Result { - self.send_request("transactions.submit_instruction", request.borrow()) - .await + ) -> Result { + self.send_request("transactions.submit_dry_run", request.borrow()).await } pub async fn create_account>( diff --git a/clients/wallet_daemon_client/src/types.rs b/clients/wallet_daemon_client/src/types.rs index 673eaa17d..dc7acf506 100644 --- a/clients/wallet_daemon_client/src/types.rs +++ b/clients/wallet_daemon_client/src/types.rs @@ -79,8 +79,6 @@ pub struct CallInstructionRequest { #[serde(default)] pub new_outputs: Option, #[serde(default)] - pub is_dry_run: bool, - #[serde(default)] #[cfg_attr(feature = "ts", ts(type = "Array"))] pub proof_ids: Vec, #[serde(default)] @@ -98,20 +96,13 @@ pub struct CallInstructionRequest { ts(export, export_to = "../../bindings/src/types/wallet-daemon-client/") )] pub struct TransactionSubmitRequest { - // TODO: make this mandatory once we remove the rest of the deprecated fields - pub transaction: Option, + pub transaction: UnsignedTransaction, #[cfg_attr(feature = "ts", ts(type = "number | null"))] pub signing_key_index: Option, - pub inputs: Vec, - pub override_inputs: bool, - pub is_dry_run: bool, + pub autofill_inputs: Vec, + pub detect_inputs: bool, #[cfg_attr(feature = "ts", ts(type = "Array"))] pub proof_ids: Vec, - // TODO: remove the following fields - pub fee_instructions: Vec, - pub instructions: Vec, - pub min_epoch: Option, - pub max_epoch: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -123,10 +114,36 @@ pub struct TransactionSubmitRequest { pub struct TransactionSubmitResponse { #[cfg_attr(feature = "ts", ts(type = "string"))] pub transaction_id: TransactionId, - pub inputs: Vec, - pub result: Option, - #[cfg_attr(feature = "ts", ts(type = "Array | null"))] - pub json_result: Option>, +} + +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +#[cfg_attr( + feature = "ts", + derive(TS), + ts(export, export_to = "../../bindings/src/types/wallet-daemon-client/") +)] +pub struct TransactionSubmitDryRunRequest { + pub transaction: UnsignedTransaction, + #[cfg_attr(feature = "ts", ts(type = "number | null"))] + pub signing_key_index: Option, + pub autofill_inputs: Vec, + pub detect_inputs: bool, + #[cfg_attr(feature = "ts", ts(type = "Array"))] + pub proof_ids: Vec, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[cfg_attr( + feature = "ts", + derive(TS), + ts(export, export_to = "../../bindings/src/types/wallet-daemon-client/") +)] +pub struct TransactionSubmitDryRunResponse { + #[cfg_attr(feature = "ts", ts(type = "string"))] + pub transaction_id: TransactionId, + pub result: ExecuteResult, + #[cfg_attr(feature = "ts", ts(type = "Array"))] + pub json_result: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/dan_layer/transaction/src/builder.rs b/dan_layer/transaction/src/builder.rs index d62a9a5a7..76b0f3d62 100644 --- a/dan_layer/transaction/src/builder.rs +++ b/dan_layer/transaction/src/builder.rs @@ -95,7 +95,7 @@ impl TransactionBuilder { }) } - pub fn call_function(self, template_address: TemplateAddress, function: &str, args: Vec) -> Self { + pub fn call_function(self, template_address: TemplateAddress, function: T, args: Vec) -> Self { self.add_instruction(Instruction::CallFunction { template_address, function: function.to_string(), diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index cf96cfd75..b375fffbf 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -96,7 +96,6 @@ pub struct TariWorld { /// A receiver wallet address that is used for default one-sided coinbase payments pub default_payment_address: TariAddress, pub consensus_manager: ConsensusManager, - pub fees_enabled: bool, } impl TariWorld { @@ -211,7 +210,6 @@ impl TariWorld { self.commitments.clear(); self.commitment_ownership_proofs.clear(); self.miners.clear(); - self.fees_enabled = true; } pub async fn wait_until_base_nodes_have_transaction_in_mempool(&self, min_tx_count: usize, timeout: Duration) { @@ -289,7 +287,6 @@ impl Default for TariWorld { wallet_private_key, default_payment_address, consensus_manager: ConsensusManager::builder(Network::LocalNet).build().unwrap(), - fees_enabled: true, } } } @@ -317,7 +314,6 @@ impl Debug for TariWorld { .field("wallet_keys", &self.wallet_keys.keys()) .field("claim_public_keys", &self.claim_public_keys.keys()) .field("wallet_daemons", &self.wallet_daemons.keys()) - .field("fees_enabled", &self.fees_enabled) .finish() } } diff --git a/integration_tests/src/validator_node.rs b/integration_tests/src/validator_node.rs index 26f4b3f4d..ac482fcf1 100644 --- a/integration_tests/src/validator_node.rs +++ b/integration_tests/src/validator_node.rs @@ -120,7 +120,6 @@ pub async fn spawn_validator_node( &validator_node_name, ); let temp_dir_path = temp_dir.clone(); - let enable_fees = world.fees_enabled; let handle = task::spawn(async move { let mut config = ApplicationConfig { common: CommonConfig::default(), @@ -145,7 +144,6 @@ pub async fn spawn_validator_node( config.validator_node.http_ui_listener_address = Some(format!("127.0.0.1:{}", http_ui_port).parse().unwrap()); config.validator_node.p2p.listener_port = port; - config.validator_node.no_fees = !enable_fees; config.validator_node.fee_claim_public_key = key.public_key; // Add all other VNs as peer seeds diff --git a/integration_tests/src/wallet_daemon_cli.rs b/integration_tests/src/wallet_daemon_cli.rs index 93dfd3974..0724ae929 100644 --- a/integration_tests/src/wallet_daemon_cli.rs +++ b/integration_tests/src/wallet_daemon_cli.rs @@ -134,7 +134,7 @@ pub async fn reveal_burned_funds(world: &mut TariWorld, account_name: String, am let request = RevealFundsRequest { account: Some(ComponentAddressOrName::Name(account_name)), amount_to_reveal: Amount(amount as i64), - max_fee: Some(Amount(1)), + max_fee: Some(Amount(2000)), pay_fee_from_reveal: true, }; @@ -207,26 +207,23 @@ pub async fn transfer_confidential( let proof_id = transfer_proof_resp.proof_id; let transaction = Transaction::builder() - .fee_transaction_pay_from_component(source_component_address, Amount(1)) + .fee_transaction_pay_from_component(source_component_address, Amount(2000)) .call_method(source_component_address, "withdraw_confidential", args![ resource_address, withdraw_proof ]) .put_last_instruction_output_on_workspace(b"bucket") .call_method(destination_account, "deposit", args![Variable("bucket")]) + .with_min_epoch(min_epoch) + .with_max_epoch(max_epoch) .build_unsigned_transaction(); let submit_req = TransactionSubmitRequest { - transaction: Some(transaction), + transaction, signing_key_index: Some(signing_key_index), - fee_instructions: vec![], proof_ids: vec![proof_id], - is_dry_run: false, - override_inputs: false, - instructions: vec![], - inputs: vec![source_account_addr, dest_account_addr], - min_epoch, - max_epoch, + detect_inputs: true, + autofill_inputs: vec![source_account_addr, dest_account_addr], }; let submit_resp = client.submit_transaction(submit_req).await.unwrap(); @@ -466,21 +463,20 @@ pub async fn submit_manifest_with_signing_keys( let AccountGetResponse { account, .. } = client.accounts_get(account_name).await.unwrap(); let instructions = parse_manifest(&manifest_content, globals, HashMap::new()).unwrap(); + + let transaction = Transaction::builder() + .fee_transaction_pay_from_component(account.address.as_component_address().unwrap(), Amount(2000)) + .with_instructions(instructions.instructions) + .with_min_epoch(min_epoch) + .with_max_epoch(max_epoch) + .build_unsigned_transaction(); + let transaction_submit_req = TransactionSubmitRequest { - transaction: None, + transaction, signing_key_index: Some(account.key_index), - instructions: instructions.instructions, - fee_instructions: vec![Instruction::CallMethod { - component_address: account.address.as_component_address().unwrap(), - method: "pay_fee".to_string(), - args: args![Amount(2000)], - }], - override_inputs: false, - is_dry_run: false, + detect_inputs: true, proof_ids: vec![], - inputs, - min_epoch, - max_epoch, + autofill_inputs: inputs, }; let resp = client.submit_transaction(transaction_submit_req).await.unwrap(); @@ -544,20 +540,25 @@ pub async fn submit_manifest( let instructions = parse_manifest(&manifest_content, globals, HashMap::new()) .unwrap_or_else(|_| panic!("Attempted to parse manifest but failed")); + let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await; + + let AccountGetResponse { account, .. } = client.accounts_get_default().await.unwrap(); + + let transaction = Transaction::builder() + .fee_transaction_pay_from_component(account.address.as_component_address().unwrap(), Amount(2000)) + .with_instructions(instructions.instructions) + .with_min_epoch(min_epoch) + .with_max_epoch(max_epoch) + .build_unsigned_transaction(); + let transaction_submit_req = TransactionSubmitRequest { - transaction: None, - signing_key_index: None, - instructions: instructions.instructions, - fee_instructions: vec![], - override_inputs: false, - is_dry_run: false, + transaction, + signing_key_index: Some(account.key_index), + detect_inputs: true, proof_ids: vec![], - inputs, - min_epoch, - max_epoch, + autofill_inputs: inputs, }; - let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await; let resp = client.submit_transaction(transaction_submit_req).await.unwrap(); let wait_req = TransactionWaitResultRequest { @@ -592,17 +593,19 @@ pub async fn submit_transaction( ) -> TransactionWaitResultResponse { let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await; + let transaction = Transaction::builder() + .with_fee_instructions(fee_instructions) + .with_instructions(instructions) + .with_min_epoch(min_epoch) + .with_max_epoch(max_epoch) + .build_unsigned_transaction(); + let transaction_submit_req = TransactionSubmitRequest { - transaction: None, + transaction, signing_key_index: None, - instructions, - fee_instructions, - override_inputs: false, - is_dry_run: false, - inputs, + detect_inputs: true, + autofill_inputs: inputs, proof_ids: vec![], - min_epoch, - max_epoch, }; let resp = client.submit_transaction(transaction_submit_req).await.unwrap(); @@ -647,25 +650,19 @@ pub async fn create_component( .await .unwrap(); + let transaction = Transaction::builder() + .fee_transaction_pay_from_component(account.address.as_component_address().unwrap(), Amount(2000)) + .call_function(template_address, &function_call, args) + .with_min_epoch(min_epoch) + .with_max_epoch(max_epoch) + .build_unsigned_transaction(); + let transaction_submit_req = TransactionSubmitRequest { - transaction: None, + transaction, signing_key_index: Some(account.key_index), - instructions: vec![Instruction::CallFunction { - template_address, - function: function_call, - args, - }], - fee_instructions: vec![Instruction::CallMethod { - component_address: account.address.as_component_address().unwrap(), - method: "pay_fee".to_string(), - args: args![Amount(2000)], - }], - override_inputs: false, - is_dry_run: false, + detect_inputs: true, proof_ids: vec![], - inputs: vec![], - min_epoch, - max_epoch, + autofill_inputs: vec![], }; let resp = client.submit_transaction(transaction_submit_req).await.unwrap(); @@ -676,15 +673,20 @@ pub async fn create_component( }; let wait_resp = client.wait_transaction_result(wait_req).await.unwrap(); + if wait_resp.timed_out { + panic!("No result after 120s. Time out."); + } + if let Some(reason) = wait_resp.result.as_ref().and_then(|finalize| finalize.full_reject()) { panic!("Create component tx failed: {}", reason); } + add_substate_ids( world, outputs_name, &wait_resp .result - .unwrap() + .expect("No result") .result .expect("Failed to obtain substate diffs"), ); @@ -714,7 +716,7 @@ pub async fn call_component( .call_method(source_component_address, &function_call, vec![]) .build_unsigned_transaction(); - let resp = submit_unsigned_tx_and_wait_for_response(client, tx, vec![], account).await; + let resp = submit_unsigned_tx_and_wait_for_response(client, tx, account).await; add_substate_ids( world, @@ -757,10 +759,10 @@ pub async fn concurrent_call_component( let acc = account.clone(); let clt = client.clone(); let tx = Transaction::builder() - .fee_transaction_pay_from_component(account_component_address, Amount(1)) + .fee_transaction_pay_from_component(account_component_address, Amount(2000)) .call_method(source_component_address, &function_call, vec![]) .build_unsigned_transaction(); - let handle = tokio::spawn(submit_unsigned_tx_and_wait_for_response(clt, tx, vec![], acc)); + let handle = tokio::spawn(submit_unsigned_tx_and_wait_for_response(clt, tx, acc)); handles.push(handle); } @@ -795,7 +797,7 @@ pub async fn transfer( let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await; let account = Some(ComponentAddressOrName::Name(account_name)); - let max_fee = Some(Amount(1)); + let max_fee = Some(Amount(2000)); let request = AccountsTransferRequest { account, @@ -866,22 +868,15 @@ async fn get_account_from_name(client: &mut WalletDaemonClient, account_name: St async fn submit_unsigned_tx_and_wait_for_response( mut client: WalletDaemonClient, - tx: UnsignedTransaction, - inputs: Vec, + transaction: UnsignedTransaction, account: Account, ) -> anyhow::Result { let submit_req = TransactionSubmitRequest { - transaction: Some(tx), + transaction, signing_key_index: Some(account.key_index), - inputs, - override_inputs: false, - is_dry_run: false, + autofill_inputs: vec![], + detect_inputs: true, proof_ids: vec![], - // TODO: remove - fee_instructions: vec![], - instructions: vec![], - min_epoch: None, - max_epoch: None, }; let submit_resp = client.submit_transaction(submit_req).await?; diff --git a/integration_tests/tests/cucumber.rs b/integration_tests/tests/cucumber.rs index 50abc04d5..2a48a6e61 100644 --- a/integration_tests/tests/cucumber.rs +++ b/integration_tests/tests/cucumber.rs @@ -76,7 +76,6 @@ async fn main() { log::info!(target: LOG_TARGET, "-------------------------------------------------------"); log::info!(target: LOG_TARGET, "\n\n\n"); world.current_scenario_name = Some(scenario.name.clone()); - world.fees_enabled = true; Box::pin(async move { // Each scenario gets a mock connection. As each connection is dropped after the scenario, all the mock // urls are deregistered @@ -103,11 +102,6 @@ async fn main() { shutdown.trigger(); } -#[given(expr = "fees are disabled")] -async fn fees_are_enabled(world: &mut TariWorld) { - world.fees_enabled = false; -} - #[when(expr = "I stop validator node {word}")] async fn stop_validator_node(world: &mut TariWorld, vn_name: String) { let vn_ps = world.validator_nodes.get_mut(&vn_name).unwrap(); diff --git a/integration_tests/tests/features/committee.feature b/integration_tests/tests/features/committee.feature index d06b13987..3da1f24c3 100644 --- a/integration_tests/tests/features/committee.feature +++ b/integration_tests/tests/features/committee.feature @@ -5,7 +5,6 @@ Feature: Committee scenarios @serial @fixed Scenario: Template registration and invocation in a 2-VN committee - Given fees are disabled # Initialize a base node, wallet and miner Given a base node BASE Given a wallet WALLET connected to base node BASE @@ -60,7 +59,6 @@ Feature: Committee scenarios # TODO: Ignored, investigate flakiness # @serial # Scenario: Template registration and invocation in a 4-VN committee -# Given fees are disabled # # Initialize a base node, wallet and miner # Given a base node BASE # Given a wallet WALLET connected to base node BASE diff --git a/integration_tests/tests/features/counter.feature b/integration_tests/tests/features/counter.feature index e93f75ee0..925ed2329 100644 --- a/integration_tests/tests/features/counter.feature +++ b/integration_tests/tests/features/counter.feature @@ -8,7 +8,6 @@ Feature: Counter template Scenario: Counter template registration and invocation # Initialize a base node, wallet, miner and VN - Given fees are disabled Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET diff --git a/integration_tests/tests/features/fungible.feature b/integration_tests/tests/features/fungible.feature index a9bbbe91f..dd88e432d 100644 --- a/integration_tests/tests/features/fungible.feature +++ b/integration_tests/tests/features/fungible.feature @@ -9,7 +9,6 @@ Feature: Fungible tokens ##### Setup # Initialize a base node, wallet, miner and VN - Given fees are disabled Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET @@ -44,25 +43,25 @@ Feature: Fungible tokens # Deposit tokens in first account When I submit a transaction manifest via wallet daemon WALLET_D with inputs "FAUCET, ACC1" named "TX1" - ``` - let faucet = global!["FAUCET/components/TestFaucet"]; - let mut acc1 = global!["ACC1/components/Account"]; + ``` + let faucet = global!["FAUCET/components/TestFaucet"]; + let mut acc1 = global!["ACC1/components/Account"]; - // get tokens from the faucet - let faucet_bucket = faucet.take_free_coins(); - acc1.deposit(faucet_bucket); - ``` + // get tokens from the faucet + let faucet_bucket = faucet.take_free_coins(); + acc1.deposit(faucet_bucket); + ``` # Move tokens from first to second account When I submit a transaction manifest via wallet daemon WALLET_D with inputs "FAUCET, TX1, ACC2" named "TX2" - ``` - let mut acc1 = global!["TX1/components/Account"]; - let mut acc2 = global!["ACC2/components/Account"]; - let faucet_resource = global!["FAUCET/resources/0"]; + ``` + let mut acc1 = global!["TX1/components/Account"]; + let mut acc2 = global!["ACC2/components/Account"]; + let faucet_resource = global!["FAUCET/resources/0"]; - // Withdraw 50 of the tokens and send them to acc2 - let tokens = acc1.withdraw(faucet_resource, Amount(50)); - acc2.deposit(tokens); - acc2.balance(faucet_resource); - acc1.balance(faucet_resource); - ``` + // Withdraw 50 of the tokens and send them to acc2 + let tokens = acc1.withdraw(faucet_resource, Amount(50)); + acc2.deposit(tokens); + acc2.balance(faucet_resource); + acc1.balance(faucet_resource); + ``` diff --git a/integration_tests/tests/features/nft.feature b/integration_tests/tests/features/nft.feature index 742f40b68..3aeacbaf5 100644 --- a/integration_tests/tests/features/nft.feature +++ b/integration_tests/tests/features/nft.feature @@ -9,7 +9,6 @@ Feature: NFTs ###### Setup # Initialize a base node, wallet, miner and VN - Given fees are disabled Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET @@ -51,40 +50,39 @@ Feature: NFTs # Submit a transaction with NFT operations When I submit a transaction manifest via wallet daemon WALLET_D with inputs "NFT, ACC1, ACC2" named "TX1" - ``` - let sparkle_nft = global!["NFT/components/SparkleNft"]; - let sparkle_res = global!["NFT/resources/0"]; - let mut acc1 = global!["ACC1/components/Account"]; - let mut acc2 = global!["ACC2/components/Account"]; - - // mint a new nft with random id - let nft_bucket = sparkle_nft.mint("NFT1", "http://example.com"); - acc1.deposit(nft_bucket); - - // mint a new nft with specific id - let nft_bucket = sparkle_nft.mint_specific(NonFungibleId("SpecialNft"), "NFT2", "http://example.com"); - acc1.deposit(nft_bucket); - - // transfer nft between accounts - let acc_bucket = acc1.withdraw_non_fungible(sparkle_res, NonFungibleId("SpecialNft")); - acc2.deposit(acc_bucket); - - // mutate a nft - sparkle_nft.inc_brightness(NonFungibleId("SpecialNft"), 10u32); - - // burn a nft - let nft_bucket = sparkle_nft.mint_specific(NonFungibleId("Burn!"), "NFT3", "http://example.com"); - acc1.deposit(nft_bucket); - let acc_bucket = acc1.withdraw_non_fungible(sparkle_res, NonFungibleId("Burn!")); - sparkle_nft.burn(acc_bucket); - ``` + ``` + let sparkle_nft = global!["NFT/components/SparkleNft"]; + let sparkle_res = global!["NFT/resources/0"]; + let mut acc1 = global!["ACC1/components/Account"]; + let mut acc2 = global!["ACC2/components/Account"]; + + // mint a new nft with random id + let nft_bucket = sparkle_nft.mint("NFT1", "http://example.com"); + acc1.deposit(nft_bucket); + + // mint a new nft with specific id + let nft_bucket = sparkle_nft.mint_specific(NonFungibleId("SpecialNft"), "NFT2", "http://example.com"); + acc1.deposit(nft_bucket); + + // transfer nft between accounts + let acc_bucket = acc1.withdraw_non_fungible(sparkle_res, NonFungibleId("SpecialNft")); + acc2.deposit(acc_bucket); + + // mutate a nft + sparkle_nft.inc_brightness(NonFungibleId("SpecialNft"), 10u32); + + // burn a nft + let nft_bucket = sparkle_nft.mint_specific(NonFungibleId("Burn!"), "NFT3", "http://example.com"); + acc1.deposit(nft_bucket); + let acc_bucket = acc1.withdraw_non_fungible(sparkle_res, NonFungibleId("Burn!")); + sparkle_nft.burn(acc_bucket); + ``` @serial Scenario: Create resource and mint in one transaction ##### Setup # Initialize a base node, wallet, miner and VN - Given fees are disabled Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET @@ -121,11 +119,11 @@ Feature: NFTs # Check that the initial NFT was actually minted by trying to deposit it into an account When I submit a transaction manifest via wallet daemon WALLET_D with inputs "NFT, ACC1" named "TX1" - ``` - let sparkle_nft = global!["NFT/components/SparkleNft"]; - let mut acc1 = global!["ACC1/components/Account"]; - - // get the initailly NFT from the component's vault - let nft_bucket = sparkle_nft.take_initial_nft(); - acc1.deposit(nft_bucket); - ``` + ``` + let sparkle_nft = global!["NFT/components/SparkleNft"]; + let mut acc1 = global!["ACC1/components/Account"]; + + // get the initailly NFT from the component's vault + let nft_bucket = sparkle_nft.take_initial_nft(); + acc1.deposit(nft_bucket); + ``` diff --git a/integration_tests/tests/features/transfer.feature b/integration_tests/tests/features/transfer.feature index 9f9934b44..e76a923b6 100644 --- a/integration_tests/tests/features/transfer.feature +++ b/integration_tests/tests/features/transfer.feature @@ -6,7 +6,6 @@ Feature: Account transfers @serial Scenario: Transfer tokens to unexisting account - Given fees are disabled # Initialize a base node, wallet, miner and VN Given a base node BASE Given a wallet WALLET connected to base node BASE @@ -85,7 +84,6 @@ Feature: Account transfers @serial Scenario: Transfer tokens to existing account - Given fees are disabled # Initialize a base node, wallet, miner and VN Given a base node BASE Given a wallet WALLET connected to base node BASE diff --git a/integration_tests/tests/features/wallet_daemon.feature b/integration_tests/tests/features/wallet_daemon.feature index 8a0eedd80..dc44d4f5d 100644 --- a/integration_tests/tests/features/wallet_daemon.feature +++ b/integration_tests/tests/features/wallet_daemon.feature @@ -6,8 +6,7 @@ Feature: Wallet Daemon @serial Scenario: Create account and transfer faucets via wallet daemon - Given fees are disabled - # Initialize a base node, wallet, miner and VN + # Initialize a base node, wallet, miner and VN Given a base node BASE Given a wallet WALLET connected to base node BASE Given a miner MINER connected to base node BASE and wallet WALLET @@ -81,14 +80,13 @@ Feature: Wallet Daemon # TODO: remove the wait When I wait 5 seconds # Check balances - # Notice that `take_free_coins` extracts precisely 10000 faucet tokens - When I check the balance of ACC_1 on wallet daemon WALLET_D the amount is at least 10000 + # `take_free_coins` deposits 10000 faucet tokens, allow up to 2000 in fees + When I check the balance of ACC_1 on wallet daemon WALLET_D the amount is at least 8000 # TODO: Figure out why this is taking more than 10 seconds to update # When I wait for ACC_2 on wallet daemon WALLET_D to have balance eq 50 @serial Scenario: Claim and transfer confidential assets via wallet daemon - Given fees are disabled # Initialize a base node, wallet, miner and VN Given a base node BASE Given a wallet WALLET connected to base node BASE @@ -132,7 +130,6 @@ Feature: Wallet Daemon @serial Scenario: Create and mint account NFT - Given fees are disabled # Initialize a base node, wallet, miner and VN Given a network with registered validator VAL_1 and wallet daemon WALLET_D