From 1eec3f4be7dd91f54dbb44a4e99c55c724c585a1 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 5 Sep 2024 11:59:47 +0800 Subject: [PATCH 01/14] Support new transaction types --- sway-lib-std/src/inputs.sw | 9 ++++--- sway-lib-std/src/outputs.sw | 2 ++ sway-lib-std/src/tx.sw | 49 ++++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/sway-lib-std/src/inputs.sw b/sway-lib-std/src/inputs.sw index fd5c28530fc..33a816437c3 100644 --- a/sway-lib-std/src/inputs.sw +++ b/sway-lib-std/src/inputs.sw @@ -18,11 +18,11 @@ use ::tx::{ tx_type, }; use core::ops::Eq; +use ::revert::revert; -const GTF_INPUT_TYPE = 0x200; +pub const GTF_INPUT_TYPE = 0x200; // GTF Opcode const selectors -// // pub const GTF_INPUT_COIN_TX_ID = 0x201; // pub const GTF_INPUT_COIN_OUTPUT_INDEX = 0x202; pub const GTF_INPUT_COIN_OWNER = 0x203; @@ -33,7 +33,7 @@ pub const GTF_INPUT_COIN_PREDICATE_LENGTH = 0x209; pub const GTF_INPUT_COIN_PREDICATE_DATA_LENGTH = 0x20A; pub const GTF_INPUT_COIN_PREDICATE = 0x20B; pub const GTF_INPUT_COIN_PREDICATE_DATA = 0x20C; - +// pub const GTF_INPUT_COIN_PREDICATE_GAS_USED = 0x20D; // pub const GTF_INPUT_CONTRACT_CONTRACT_ID = 0x225; pub const GTF_INPUT_MESSAGE_SENDER = 0x240; pub const GTF_INPUT_MESSAGE_RECIPIENT = 0x241; @@ -46,6 +46,7 @@ pub const GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH = 0x247; pub const GTF_INPUT_MESSAGE_DATA = 0x248; pub const GTF_INPUT_MESSAGE_PREDICATE = 0x249; pub const GTF_INPUT_MESSAGE_PREDICATE_DATA = 0x24A; +// pub const GTF_INPUT_MESSAGE_PREDICATE_GAS_USED = 0x24B; /// The input type for a transaction. pub enum Input { @@ -127,6 +128,7 @@ pub fn input_count() -> u16 { match tx_type() { Transaction::Script => __gtf::(0, GTF_SCRIPT_INPUTS_COUNT), Transaction::Create => __gtf::(0, GTF_CREATE_INPUTS_COUNT), + _ => revert(0), } } @@ -158,6 +160,7 @@ fn input_pointer(index: u64) -> Option { match tx_type() { Transaction::Script => Some(__gtf::(index, GTF_SCRIPT_INPUT_AT_INDEX)), Transaction::Create => Some(__gtf::(index, GTF_CREATE_INPUT_AT_INDEX)), + _ => None, } } diff --git a/sway-lib-std/src/outputs.sw b/sway-lib-std/src/outputs.sw index 140fdd09ae3..baa828fc2e3 100644 --- a/sway-lib-std/src/outputs.sw +++ b/sway-lib-std/src/outputs.sw @@ -111,6 +111,7 @@ fn output_pointer(index: u64) -> Option { match tx_type() { Transaction::Script => Some(__gtf::(index, GTF_SCRIPT_OUTPUT_AT_INDEX)), Transaction::Create => Some(__gtf::(index, GTF_CREATE_OUTPUT_AT_INDEX)), + _ => None, } } @@ -139,6 +140,7 @@ pub fn output_count() -> u16 { match tx_type() { Transaction::Script => __gtf::(0, GTF_SCRIPT_OUTPUTS_COUNT), Transaction::Create => __gtf::(0, GTF_CREATE_OUTPUTS_COUNT), + _ => revert(0), } } diff --git a/sway-lib-std/src/tx.sw b/sway-lib-std/src/tx.sw index 3ee245f806c..e3216fba431 100644 --- a/sway-lib-std/src/tx.sw +++ b/sway-lib-std/src/tx.sw @@ -18,6 +18,7 @@ pub const GTF_SCRIPT_SCRIPT_DATA = 0x00A; pub const GTF_SCRIPT_INPUT_AT_INDEX = 0x00B; pub const GTF_SCRIPT_OUTPUT_AT_INDEX = 0x00C; pub const GTF_SCRIPT_WITNESS_AT_INDEX = 0x00D; + pub const GTF_TX_LENGTH = 0x00E; // pub const GTF_CREATE_BYTECODE_WITNESS_INDEX = 0x101; @@ -46,10 +47,35 @@ pub enum Transaction { Script: (), /// A contract deployment transaction. Create: (), + /// The transaction is created by the block producer and is not signed. + /// + /// # Additional Information + /// + /// NOTE: This should never be valid in execution but it provided for congruency to the FuelVM specs. + Mint: (), + /// The Upgrade transaction allows upgrading either consensus parameters or state transition function used by the network to produce future blocks. + Upgrade: (), + ///The Upload transaction allows the huge bytecode to be divided into subsections and uploaded slowly to the chain. + Upload: (), + /// The Blob inserts a simple binary blob in the chain. It's raw immutable data that can be cheaply loaded by the VM and used as instructions or just data. + Blob: (), +} + +impl core::ops::Eq for Transaction { + fn eq(self, other: Self) -> bool { + match (self, other) { + (Transaction::Script, Transaction::Script) => true, + (Transaction::Create, Transaction::Create) => true, + (Transaction::Mint, Transaction::Mint) => true, + (Transaction::Upgrade, Transaction::Upgrade) => true, + (Transaction::Upload, Transaction::Upload) => true, + (Transaction::Blob, Transaction::Blob) => true, + _ => false, + } + } } /// Get the type of the current transaction. -/// Either `Transaction::Script` or `Transaction::Create`. /// /// # Returns /// @@ -73,6 +99,18 @@ pub enum Transaction { /// Transaction::Create => { /// log("Contract deployment transaction"); /// }, +/// Transaction::Mint => { +/// log("This should never happen"); +/// }, +/// Transaction::Upgrade => { +/// log("Upgrade transaction"); +/// }, +/// Transaction::Upload => { +/// log("Upload transaction"); +/// }, +/// Transaction::Blob => { +/// log("Blob transaction"); +/// }, /// } /// } /// ``` @@ -80,6 +118,9 @@ pub fn tx_type() -> Transaction { match __gtf::(0, GTF_TYPE) { 0u8 => Transaction::Script, 1u8 => Transaction::Create, + 3u8 => Transaction::Upgrade, + 4u8 => Transaction::Upload, + 5u8 => Transaction::Blob, _ => revert(0), } } @@ -233,7 +274,7 @@ pub fn tx_max_fee() -> Option { pub fn tx_script_length() -> Option { match tx_type() { Transaction::Script => Some(__gtf::(0, GTF_SCRIPT_SCRIPT_LENGTH)), - Transaction::Create => None, + _ => None, } } @@ -256,7 +297,7 @@ pub fn tx_script_length() -> Option { pub fn tx_script_data_length() -> Option { match tx_type() { Transaction::Script => Some(__gtf::(0, GTF_SCRIPT_SCRIPT_DATA_LENGTH)), - Transaction::Create => None, + _ => None, } } @@ -280,6 +321,7 @@ pub fn tx_witnesses_count() -> u64 { match tx_type() { Transaction::Script => __gtf::(0, GTF_SCRIPT_WITNESSES_COUNT), Transaction::Create => __gtf::(0, GTF_CREATE_WITNESSES_COUNT), + _ => revert(0), } } @@ -311,6 +353,7 @@ fn tx_witness_pointer(index: u64) -> Option { match tx_type() { Transaction::Script => Some(__gtf::(index, GTF_SCRIPT_WITNESS_AT_INDEX)), Transaction::Create => Some(__gtf::(index, GTF_CREATE_WITNESS_AT_INDEX)), + _ => None, } } From dec08b267f21f110426663d86c270262ed8485da Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 5 Sep 2024 12:00:16 +0800 Subject: [PATCH 02/14] Add Eq tests for Transaction --- test/src/in_language_tests/Forc.toml | 1 + .../test_programs/tx_inline_tests/Forc.toml | 8 ++ .../test_programs/tx_inline_tests/src/main.sw | 129 ++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 test/src/in_language_tests/test_programs/tx_inline_tests/Forc.toml create mode 100644 test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw diff --git a/test/src/in_language_tests/Forc.toml b/test/src/in_language_tests/Forc.toml index eaa9393b936..b2f198bf2f7 100644 --- a/test/src/in_language_tests/Forc.toml +++ b/test/src/in_language_tests/Forc.toml @@ -21,6 +21,7 @@ members = [ "test_programs/revert_inline_tests", "test_programs/storage_key_inline_tests", "test_programs/string_inline_tests", + "test_programs/tx_inline_tests", "test_programs/u128_inline_tests", "test_programs/vec_inline_tests", "test_programs/array_conversions_b256_inline_tests", diff --git a/test/src/in_language_tests/test_programs/tx_inline_tests/Forc.toml b/test/src/in_language_tests/test_programs/tx_inline_tests/Forc.toml new file mode 100644 index 00000000000..fa38e2fc473 --- /dev/null +++ b/test/src/in_language_tests/test_programs/tx_inline_tests/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "tx_inline_tests" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } diff --git a/test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw b/test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw new file mode 100644 index 00000000000..fdd61cd7b6b --- /dev/null +++ b/test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw @@ -0,0 +1,129 @@ +library; + +use std::tx::Transaction; + +#[test] +fn tx_transaction_eq() { + let transaction_1 = Transaction::Script; + let transaction_2 = Transaction::Script; + let transaction_3 = Transaction::Create; + let transaction_4 = Transaction::Create; + let transaction_5 = Transaction::Mint; + let transaction_6 = Transaction::Mint; + let transaction_7 = Transaction::Upgrade; + let transaction_8 = Transaction::Upgrade; + let transaction_9 = Transaction::Upload; + let transaction_10 = Transaction::Upload; + let transaction_11 = Transaction::Blob; + let transaction_12 = Transaction::Blob; + + assert(transaction_1 == transaction_1); + assert(transaction_1 == transaction_2); + assert(transaction_2 == transaction_2); + + assert(transaction_3 == transaction_3); + assert(transaction_3 == transaction_4); + assert(transaction_4 == transaction_4); + + assert(transaction_5 == transaction_5); + assert(transaction_5 == transaction_6); + assert(transaction_6 == transaction_6); + + assert(transaction_7 == transaction_7); + assert(transaction_7 == transaction_8); + assert(transaction_8 == transaction_8); + + assert(transaction_9 == transaction_9); + assert(transaction_9 == transaction_10); + assert(transaction_10 == transaction_10); + + assert(transaction_11 == transaction_11); + assert(transaction_11 == transaction_12); + assert(transaction_12 == transaction_12); +} + +#[test] +fn tx_transaction_ne() { + let transaction_1 = Transaction::Script; + let transaction_2 = Transaction::Script; + let transaction_3 = Transaction::Create; + let transaction_4 = Transaction::Create; + let transaction_5 = Transaction::Mint; + let transaction_6 = Transaction::Mint; + let transaction_7 = Transaction::Upgrade; + let transaction_8 = Transaction::Upgrade; + let transaction_9 = Transaction::Upload; + let transaction_10 = Transaction::Upload; + let transaction_11 = Transaction::Blob; + let transaction_12 = Transaction::Blob; + + assert(transaction_1 != transaction_3); + assert(transaction_1 != transaction_4); + assert(transaction_1 != transaction_5); + assert(transaction_1 != transaction_6); + assert(transaction_1 != transaction_7); + assert(transaction_1 != transaction_8); + assert(transaction_1 != transaction_9); + assert(transaction_1 != transaction_10); + assert(transaction_1 != transaction_11); + assert(transaction_1 != transaction_12); + + assert(transaction_2 != transaction_3); + assert(transaction_2 != transaction_4); + assert(transaction_2 != transaction_5); + assert(transaction_2 != transaction_6); + assert(transaction_2 != transaction_7); + assert(transaction_2 != transaction_8); + assert(transaction_2 != transaction_9); + assert(transaction_2 != transaction_10); + assert(transaction_2 != transaction_11); + assert(transaction_2 != transaction_12); + + assert(transaction_3 != transaction_5); + assert(transaction_3 != transaction_6); + assert(transaction_3 != transaction_7); + assert(transaction_3 != transaction_8); + assert(transaction_3 != transaction_9); + assert(transaction_3 != transaction_10); + assert(transaction_3 != transaction_11); + assert(transaction_3 != transaction_12); + + assert(transaction_4 != transaction_5); + assert(transaction_4 != transaction_6); + assert(transaction_4 != transaction_7); + assert(transaction_4 != transaction_8); + assert(transaction_4 != transaction_9); + assert(transaction_4 != transaction_10); + assert(transaction_4 != transaction_11); + assert(transaction_4 != transaction_12); + + assert(transaction_5 != transaction_7); + assert(transaction_5 != transaction_8); + assert(transaction_5 != transaction_9); + assert(transaction_5 != transaction_10); + assert(transaction_5 != transaction_11); + assert(transaction_5 != transaction_12); + + assert(transaction_6 != transaction_7); + assert(transaction_6 != transaction_8); + assert(transaction_6 != transaction_9); + assert(transaction_6 != transaction_10); + assert(transaction_6 != transaction_11); + assert(transaction_6 != transaction_12); + + assert(transaction_7 != transaction_9); + assert(transaction_7 != transaction_10); + assert(transaction_7 != transaction_11); + assert(transaction_7 != transaction_12); + + assert(transaction_8 != transaction_9); + assert(transaction_8 != transaction_10); + assert(transaction_8 != transaction_11); + assert(transaction_8 != transaction_12); + + assert(transaction_9 != transaction_11); + assert(transaction_9 != transaction_12); + + assert(transaction_10 != transaction_11); + assert(transaction_10 != transaction_12); +} \ No newline at end of file From 41a52401e8fc51b105be4249e26fa21c85f61902 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 5 Sep 2024 12:01:10 +0800 Subject: [PATCH 03/14] Add tx tests for transaction Upload --- test/src/sdk-harness/Forc.lock | 5 + test/src/sdk-harness/Forc.toml | 3 +- .../tx_type_predicate/Forc.toml | 8 + .../tx_type_predicate/src/main.sw | 7 + .../src/sdk-harness/test_projects/auth/mod.rs | 4 +- .../test_projects/tx_fields/mod.rs | 137 ++++++++++++++++-- 6 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 test/src/sdk-harness/test_artifacts/tx_type_predicate/Forc.toml create mode 100644 test/src/sdk-harness/test_artifacts/tx_type_predicate/src/main.sw diff --git a/test/src/sdk-harness/Forc.lock b/test/src/sdk-harness/Forc.lock index 1bd0f8e296a..248e0e3622f 100644 --- a/test/src/sdk-harness/Forc.lock +++ b/test/src/sdk-harness/Forc.lock @@ -414,6 +414,11 @@ name = "tx_output_predicate" source = "member" dependencies = ["std"] +[[package]] +name = "tx_type_predicate" +source = "member" +dependencies = ["std"] + [[package]] name = "type_aliases" source = "member" diff --git a/test/src/sdk-harness/Forc.toml b/test/src/sdk-harness/Forc.toml index 954b0427ce7..02ad7f4c4a6 100644 --- a/test/src/sdk-harness/Forc.toml +++ b/test/src/sdk-harness/Forc.toml @@ -75,6 +75,7 @@ members = [ "test_artifacts/storage_vec/svec_u16", "test_artifacts/storage_vec/svec_u64", "test_artifacts/tx_contract", - "test_artifacts/tx_output_predicate", "test_artifacts/tx_output_contract_creation_predicate", + "test_artifacts/tx_output_predicate", + "test_artifacts/tx_type_predicate", ] diff --git a/test/src/sdk-harness/test_artifacts/tx_type_predicate/Forc.toml b/test/src/sdk-harness/test_artifacts/tx_type_predicate/Forc.toml new file mode 100644 index 00000000000..23ca628cedf --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_type_predicate/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "tx_type_predicate" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } diff --git a/test/src/sdk-harness/test_artifacts/tx_type_predicate/src/main.sw b/test/src/sdk-harness/test_artifacts/tx_type_predicate/src/main.sw new file mode 100644 index 00000000000..59bd4f10714 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_type_predicate/src/main.sw @@ -0,0 +1,7 @@ +predicate; + +use std::tx::{tx_type, Transaction}; + +fn main(expected_type: Transaction) -> bool { + tx_type() == expected_type +} diff --git a/test/src/sdk-harness/test_projects/auth/mod.rs b/test/src/sdk-harness/test_projects/auth/mod.rs index 5072b1c1a86..bead3968ea3 100644 --- a/test/src/sdk-harness/test_projects/auth/mod.rs +++ b/test/src/sdk-harness/test_projects/auth/mod.rs @@ -196,7 +196,7 @@ async fn can_get_predicate_address() { // Setup predicate. let hex_predicate_address: &str = - "0x188fb1615bacd24d52b1339fcfeed1ecf555d0afde44a087eebf98381c64db1b"; + "0x8896a8f961e31af598faa2c4041d4d2745b0a11c57f8bb8bae65cff290076a74"; let predicate_address = Address::from_str(hex_predicate_address).expect("failed to create Address from string"); let predicate_bech32_address = Bech32Address::from(predicate_address); @@ -322,7 +322,7 @@ async fn when_incorrect_predicate_address_passed() { async fn can_get_predicate_address_in_message() { // Setup predicate address. let hex_predicate_address: &str = - "0x188fb1615bacd24d52b1339fcfeed1ecf555d0afde44a087eebf98381c64db1b"; + "0x8896a8f961e31af598faa2c4041d4d2745b0a11c57f8bb8bae65cff290076a74"; let predicate_address = Address::from_str(hex_predicate_address).expect("failed to create Address from string"); let predicate_bech32_address = Bech32Address::from(predicate_address); diff --git a/test/src/sdk-harness/test_projects/tx_fields/mod.rs b/test/src/sdk-harness/test_projects/tx_fields/mod.rs index aace3c9fa38..bfa1b452423 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -16,6 +16,10 @@ const TX_OUTPUT_PREDICATE_BYTECODE_PATH: &str = const TX_FIELDS_PREDICATE_BYTECODE_PATH: &str = "test_projects/tx_fields/out/release/tx_fields.bin"; const TX_CONTRACT_CREATION_PREDICATE_BYTECODE_PATH: &str = "test_artifacts/tx_output_contract_creation_predicate/out/release/tx_output_contract_creation_predicate.bin"; +const TX_TYPE_PREDICATE_BYTECODE_PATH: &str = + "test_artifacts/tx_type_predicate/out/release/tx_type_predicate.bin"; + +use crate::tx_fields::Transaction as SwayTransaction; abigen!( Contract( @@ -29,6 +33,10 @@ abigen!( Predicate( name = "TestOutputPredicate", abi = "test_artifacts/tx_output_predicate/out/release/tx_output_predicate-abi.json" + ), + Predicate( + name = "TestTxTypePredicate", + abi = "test_artifacts/tx_type_predicate/out/release/tx_type_predicate-abi.json" ) ); @@ -455,6 +463,100 @@ mod tx { assert_eq!(receipts[1].data().unwrap(), byte_array); } + + #[tokio::test] + async fn can_get_tx_upload() { + let mut wallet = WalletUnlocked::new_random(None); + + let num_coins = 100; + let coins = setup_single_asset_coins( + wallet.address(), + AssetId::zeroed(), + num_coins, + DEFAULT_COIN_AMOUNT, + ); + + let provider = setup_test_provider(coins, vec![], None, None) + .await + .unwrap(); + wallet.set_provider(provider.clone()); + + // Get the predicate + let predicate_data = TestTxTypePredicateEncoder::default() + .encode_data(SwayTransaction::Upload) + .unwrap(); + let predicate: Predicate = Predicate::load_from(TX_TYPE_PREDICATE_BYTECODE_PATH) + .unwrap() + .with_provider(provider.clone()) + .with_data(predicate_data); + let predicate_coin_amount = 100; + + // Predicate has no funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); + + let bytecode = fs::read(TX_CONTRACT_BYTECODE_PATH).unwrap(); + let subsection_size = 65536; + let subsections = UploadSubsection::split_bytecode(&bytecode, subsection_size).unwrap(); + + for _ in subsections.clone() { + // Transfer enough funds to predicate for each subsection + wallet + .transfer( + predicate.address(), + predicate_coin_amount, + *provider.base_asset_id(), + TxPolicies::default(), + ) + .await + .unwrap(); + } + + // Predicate has funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!( + predicate_balance as usize, + predicate_coin_amount as usize * subsections.len() + ); + + for subsection in subsections { + let mut builder = UploadTransactionBuilder::prepare_subsection_upload( + subsection, + TxPolicies::default(), + ); + + // Inputs + let predicate_input = predicate + .get_asset_inputs_for_amount(*provider.base_asset_id(), 1, None) + .await + .unwrap(); + + // Outputs + let predicate_output = wallet.get_asset_outputs_for_amount( + &wallet.address(), + *provider.base_asset_id(), + 1, + ); + + builder.inputs.push(predicate_input.get(0).unwrap().clone()); + builder + .outputs + .push(predicate_output.get(0).unwrap().clone()); + + wallet.add_witnesses(&mut builder).unwrap(); + wallet.adjust_for_fee(&mut builder, 0).await.unwrap(); + + let tx = builder.build(&provider).await.unwrap(); + + provider.send_transaction(tx).await.unwrap(); + } + } } mod inputs { @@ -720,7 +822,7 @@ mod inputs { .call() .await .unwrap(); - + assert_eq!(none_result.value, None); } @@ -743,7 +845,7 @@ mod inputs { .call() .await .unwrap(); - + assert_eq!(none_result.value, None); } @@ -779,7 +881,10 @@ mod inputs { .take_receipts_checked(None) .unwrap(); - assert_eq!(receipts[1].data(), Some(&[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3][..])); + assert_eq!( + receipts[1].data(), + Some(&[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3][..]) + ); // Assert none returned when transaction type is not a message let none_result = contract_instance @@ -788,7 +893,7 @@ mod inputs { .call() .await .unwrap(); - + assert_eq!(none_result.value, None); } @@ -825,7 +930,10 @@ mod inputs { .unwrap(); let len = predicate_bytecode.len() as u64; - assert_eq!(receipts[1].data().unwrap()[8..16], *len.to_be_bytes().as_slice()); + assert_eq!( + receipts[1].data().unwrap()[8..16], + *len.to_be_bytes().as_slice() + ); // Assert none returned when index is invalid let none_result = contract_instance @@ -834,7 +942,7 @@ mod inputs { .call() .await .unwrap(); - + assert_eq!(none_result.value, None); } @@ -869,7 +977,10 @@ mod inputs { .take_receipts_checked(None) .unwrap(); - assert_eq!(receipts[1].data().unwrap()[8..16], *0u64.to_le_bytes().as_slice()); + assert_eq!( + receipts[1].data().unwrap()[8..16], + *0u64.to_le_bytes().as_slice() + ); // Assert none returned when transaction type is not a message let none_result = contract_instance @@ -878,7 +989,7 @@ mod inputs { .call() .await .unwrap(); - + assert_eq!(none_result.value, None); } @@ -924,7 +1035,7 @@ mod inputs { .call() .await .unwrap(); - + assert_eq!(none_result.value, false); } @@ -972,7 +1083,11 @@ mod inputs { let mut builder = contract_instance .methods() - .get_input_message_data(3, (MESSAGE_DATA.len() + 1) as u64, Bytes(MESSAGE_DATA.into())) + .get_input_message_data( + 3, + (MESSAGE_DATA.len() + 1) as u64, + Bytes(MESSAGE_DATA.into()), + ) .transaction_builder() .await .unwrap(); @@ -1039,7 +1154,7 @@ mod inputs { .call() .await .unwrap(); - + assert_eq!(none_result.value, false); } } From 0e47a6fec7d4b6180ebdeee35c46daea0625ea14 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 5 Sep 2024 12:08:52 +0800 Subject: [PATCH 04/14] Fix ordering of GTF opcodes in input.sw --- sway-lib-std/src/inputs.sw | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway-lib-std/src/inputs.sw b/sway-lib-std/src/inputs.sw index 33a816437c3..19c3e7cdefd 100644 --- a/sway-lib-std/src/inputs.sw +++ b/sway-lib-std/src/inputs.sw @@ -20,9 +20,8 @@ use ::tx::{ use core::ops::Eq; use ::revert::revert; -pub const GTF_INPUT_TYPE = 0x200; - // GTF Opcode const selectors +pub const GTF_INPUT_TYPE = 0x200; // pub const GTF_INPUT_COIN_TX_ID = 0x201; // pub const GTF_INPUT_COIN_OUTPUT_INDEX = 0x202; pub const GTF_INPUT_COIN_OWNER = 0x203; From 0790b65c563a2ce05c7bf807076998f7eb74daef Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 5 Sep 2024 12:25:56 +0800 Subject: [PATCH 05/14] Add check to ensure predicate has spent all funds --- .../test_projects/tx_fields/mod.rs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/src/sdk-harness/test_projects/tx_fields/mod.rs b/test/src/sdk-harness/test_projects/tx_fields/mod.rs index bfa1b452423..5b508018b80 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -466,8 +466,8 @@ mod tx { #[tokio::test] async fn can_get_tx_upload() { + // Prepare wallet and provider let mut wallet = WalletUnlocked::new_random(None); - let num_coins = 100; let coins = setup_single_asset_coins( wallet.address(), @@ -475,7 +475,6 @@ mod tx { num_coins, DEFAULT_COIN_AMOUNT, ); - let provider = setup_test_provider(coins, vec![], None, None) .await .unwrap(); @@ -498,12 +497,13 @@ mod tx { .unwrap(); assert_eq!(predicate_balance, 0); + // Prepare bytecode and subsections let bytecode = fs::read(TX_CONTRACT_BYTECODE_PATH).unwrap(); let subsection_size = 65536; let subsections = UploadSubsection::split_bytecode(&bytecode, subsection_size).unwrap(); + // Transfer enough funds to the predicate for each subsection for _ in subsections.clone() { - // Transfer enough funds to predicate for each subsection wallet .transfer( predicate.address(), @@ -525,25 +525,27 @@ mod tx { predicate_coin_amount as usize * subsections.len() ); + // Upload each sub section in a seperate transaction and include the predicate with the transaction. for subsection in subsections { let mut builder = UploadTransactionBuilder::prepare_subsection_upload( subsection, TxPolicies::default(), ); - // Inputs + // Inputs for predicate let predicate_input = predicate .get_asset_inputs_for_amount(*provider.base_asset_id(), 1, None) .await .unwrap(); - // Outputs + // Outputs for predicate let predicate_output = wallet.get_asset_outputs_for_amount( &wallet.address(), *provider.base_asset_id(), 1, ); + // Append the predicate to the transaction builder.inputs.push(predicate_input.get(0).unwrap().clone()); builder .outputs @@ -552,10 +554,17 @@ mod tx { wallet.add_witnesses(&mut builder).unwrap(); wallet.adjust_for_fee(&mut builder, 0).await.unwrap(); + // Submit the transaction let tx = builder.build(&provider).await.unwrap(); - provider.send_transaction(tx).await.unwrap(); } + + // The predicate has spent it's funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); } } From 7f8ffe01779e841ba67194bc134f8ff92803fdc5 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 5 Sep 2024 12:27:18 +0800 Subject: [PATCH 06/14] Fix asset ops test --- .../require_contract_deployment/asset_ops_test/src/main.sw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw index 02567676e67..c84d1c235c4 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw @@ -9,7 +9,7 @@ use test_fuel_coin_abi::*; #[cfg(experimental_new_encoding = false)] const FUEL_COIN_CONTRACT_ID = 0xec2277ebe007ade87e3d797c3b1e070dcd542d5ef8f038b471f262ef9cebc87c; #[cfg(experimental_new_encoding = true)] -const FUEL_COIN_CONTRACT_ID = 0xf8c7b4d09f9964ab4c437ac7f5cbd6dbad7e1f218fce452c5807aac3c67afa6f; +const FUEL_COIN_CONTRACT_ID = 0x4b651789d4867c17419e95cefd50f49bdc78e8778118373b7aab6bcab2ca3d1e; #[cfg(experimental_new_encoding = false)] const BALANCE_CONTRACT_ID = 0xf6cd545152ac83225e8e7df2efb5c6fa6e37bc9b9e977b5ea8103d28668925df; From 10507c29298c716f5ef46a3569d2e56224b7a08b Mon Sep 17 00:00:00 2001 From: bitzoic Date: Thu, 5 Sep 2024 12:29:33 +0800 Subject: [PATCH 07/14] Fix typo --- .../in_language_tests/test_programs/tx_inline_tests/src/main.sw | 2 +- test/src/sdk-harness/test_projects/tx_fields/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw b/test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw index fdd61cd7b6b..018763a31b8 100644 --- a/test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw +++ b/test/src/in_language_tests/test_programs/tx_inline_tests/src/main.sw @@ -126,4 +126,4 @@ fn tx_transaction_ne() { assert(transaction_10 != transaction_11); assert(transaction_10 != transaction_12); -} \ No newline at end of file +} diff --git a/test/src/sdk-harness/test_projects/tx_fields/mod.rs b/test/src/sdk-harness/test_projects/tx_fields/mod.rs index 5b508018b80..f75144c2682 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -525,7 +525,7 @@ mod tx { predicate_coin_amount as usize * subsections.len() ); - // Upload each sub section in a seperate transaction and include the predicate with the transaction. + // Upload each sub section in a separate transaction and include the predicate with the transaction. for subsection in subsections { let mut builder = UploadTransactionBuilder::prepare_subsection_upload( subsection, From f152281b041f1ebc9b5007e29b70f43b6844cc23 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Wed, 11 Sep 2024 16:06:00 +0545 Subject: [PATCH 08/14] Implement GTF opcodes for Upgrade, Upload, and Blob --- sway-lib-std/src/inputs.sw | 10 ++++++++++ sway-lib-std/src/outputs.sw | 6 ++++++ sway-lib-std/src/tx.sw | 39 ++++++++++++++++++++++++++++--------- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/sway-lib-std/src/inputs.sw b/sway-lib-std/src/inputs.sw index 19c3e7cdefd..f4db2869bb2 100644 --- a/sway-lib-std/src/inputs.sw +++ b/sway-lib-std/src/inputs.sw @@ -113,6 +113,10 @@ pub fn input_type(index: u64) -> Option { /// /// * [u16] - The number of inputs in the transaction. /// +/// # Reverts +/// +/// * When the input type is unrecognized. This should never happen. +/// /// # Examples /// /// ```sway @@ -127,6 +131,9 @@ pub fn input_count() -> u16 { match tx_type() { Transaction::Script => __gtf::(0, GTF_SCRIPT_INPUTS_COUNT), Transaction::Create => __gtf::(0, GTF_CREATE_INPUTS_COUNT), + Transaction::Upgrade => __gtf::(0, GTF_SCRIPT_INPUTS_COUNT), + Transaction::Upload => __gtf::(0, GTF_SCRIPT_INPUTS_COUNT), + Transaction::Blob => __gtf::(0, GTF_SCRIPT_INPUTS_COUNT), _ => revert(0), } } @@ -159,6 +166,9 @@ fn input_pointer(index: u64) -> Option { match tx_type() { Transaction::Script => Some(__gtf::(index, GTF_SCRIPT_INPUT_AT_INDEX)), Transaction::Create => Some(__gtf::(index, GTF_CREATE_INPUT_AT_INDEX)), + Transaction::Upgrade => Some(__gtf::(index, GTF_SCRIPT_INPUT_AT_INDEX)), + Transaction::Upload => Some(__gtf::(index, GTF_SCRIPT_INPUT_AT_INDEX)), + Transaction::Blob => Some(__gtf::(index, GTF_SCRIPT_INPUT_AT_INDEX)), _ => None, } } diff --git a/sway-lib-std/src/outputs.sw b/sway-lib-std/src/outputs.sw index baa828fc2e3..34f0c55eb13 100644 --- a/sway-lib-std/src/outputs.sw +++ b/sway-lib-std/src/outputs.sw @@ -111,6 +111,9 @@ fn output_pointer(index: u64) -> Option { match tx_type() { Transaction::Script => Some(__gtf::(index, GTF_SCRIPT_OUTPUT_AT_INDEX)), Transaction::Create => Some(__gtf::(index, GTF_CREATE_OUTPUT_AT_INDEX)), + Transaction::Upgrade => Some(__gtf::(index, GTF_SCRIPT_OUTPUT_AT_INDEX)), + Transaction::Upload => Some(__gtf::(index, GTF_SCRIPT_OUTPUT_AT_INDEX)), + Transaction::Blob => Some(__gtf::(index, GTF_SCRIPT_OUTPUT_AT_INDEX)), _ => None, } } @@ -140,6 +143,9 @@ pub fn output_count() -> u16 { match tx_type() { Transaction::Script => __gtf::(0, GTF_SCRIPT_OUTPUTS_COUNT), Transaction::Create => __gtf::(0, GTF_CREATE_OUTPUTS_COUNT), + Transaction::Upgrade => __gtf::(0, GTF_SCRIPT_OUTPUTS_COUNT), + Transaction::Upload => __gtf::(0, GTF_SCRIPT_OUTPUTS_COUNT), + Transaction::Blob => __gtf::(0, GTF_SCRIPT_OUTPUTS_COUNT), _ => revert(0), } } diff --git a/sway-lib-std/src/tx.sw b/sway-lib-std/src/tx.sw index e3216fba431..15f1db12eb1 100644 --- a/sway-lib-std/src/tx.sw +++ b/sway-lib-std/src/tx.sw @@ -3,6 +3,7 @@ library; use ::revert::revert; use ::option::Option::{self, *}; +use ::alloc::alloc_bytes; // GTF Opcode const selectors // @@ -307,6 +308,10 @@ pub fn tx_script_data_length() -> Option { /// /// * [u64] - The witnesses count for the transaction. /// +/// # Reverts +/// +/// * When the transaction type is unrecognized. This should never happen. +/// /// # Examples /// /// ```sway @@ -321,6 +326,9 @@ pub fn tx_witnesses_count() -> u64 { match tx_type() { Transaction::Script => __gtf::(0, GTF_SCRIPT_WITNESSES_COUNT), Transaction::Create => __gtf::(0, GTF_CREATE_WITNESSES_COUNT), + Transaction::Upgrade => __gtf::(0, GTF_SCRIPT_WITNESSES_COUNT), + Transaction::Upload => __gtf::(0, GTF_SCRIPT_WITNESSES_COUNT), + Transaction::Blob => __gtf::(0, GTF_SCRIPT_WITNESSES_COUNT), _ => revert(0), } } @@ -344,7 +352,6 @@ pub fn tx_witnesses_count() -> u64 { /// let witness_pointer = tx_witness_pointer(0).unwrap(); /// } /// ``` -#[allow(dead_code)] fn tx_witness_pointer(index: u64) -> Option { if index >= tx_witnesses_count() { return None @@ -353,6 +360,9 @@ fn tx_witness_pointer(index: u64) -> Option { match tx_type() { Transaction::Script => Some(__gtf::(index, GTF_SCRIPT_WITNESS_AT_INDEX)), Transaction::Create => Some(__gtf::(index, GTF_CREATE_WITNESS_AT_INDEX)), + Transaction::Upgrade => Some(__gtf::(index, GTF_SCRIPT_WITNESS_AT_INDEX)), + Transaction::Upload => Some(__gtf::(index, GTF_SCRIPT_WITNESS_AT_INDEX)), + Transaction::Blob => Some(__gtf::(index, GTF_SCRIPT_WITNESS_AT_INDEX)), _ => None, } } @@ -387,6 +397,11 @@ pub fn tx_witness_data_length(index: u64) -> Option { /// Get the witness data at `index`. /// +/// # Additional Information +/// +/// **Unsafe. Assumes the type is correct.** +/// This function does not support ownership types(Vec, Bytes, String, etc). +/// /// # Arguments /// /// * `index` - The index of the witness to get the data for. @@ -410,11 +425,18 @@ pub fn tx_witness_data(index: u64) -> Option { return None } - if __size_of::() == 1 { - Some(__gtf::(index, GTF_WITNESS_DATA).add::(7).read::()) - } else { - Some(__gtf::(index, GTF_WITNESS_DATA).read::()) - } + let length = match tx_witness_data_length(index) { + Some(len) => len, + None => return None, + }; + + let witness_data_ptr = __gtf::(index, GTF_WITNESS_DATA); + let new_ptr = alloc_bytes(length); + witness_data_ptr.copy_bytes_to(new_ptr, length); + + Some(asm(ptr: new_ptr) { + ptr: T + }) } /// Get the transaction script start pointer. @@ -467,8 +489,8 @@ fn tx_script_data_start_pointer() -> Option { /// /// # Additional Information /// -/// **Unsafe.** -/// **Assumes the type is correct.** +/// **Unsafe. Assumes the type is correct.** +/// This function does not support ownership types(Vec, Bytes, String, etc). /// /// # Returns /// @@ -523,7 +545,6 @@ pub fn tx_script_bytecode() -> Option { } /// Get the hash of the script bytecode. -/// Reverts if not a transaction-script. /// /// # Returns /// From e3b736a671c390fabca0c79b237a62f8fb01fdff Mon Sep 17 00:00:00 2001 From: bitzoic Date: Wed, 11 Sep 2024 16:06:49 +0545 Subject: [PATCH 09/14] Add predicates for transaction tests --- .../tx_input_count_predicate/Forc.toml | 8 +++++++ .../tx_input_count_predicate/src/main.sw | 7 ++++++ .../tx_output_count_predicate/Forc.toml | 8 +++++++ .../tx_output_count_predicate/src/main.sw | 7 ++++++ .../tx_witness_predicate/Forc.toml | 8 +++++++ .../tx_witness_predicate/src/main.sw | 22 +++++++++++++++++++ 6 files changed, 60 insertions(+) create mode 100644 test/src/sdk-harness/test_artifacts/tx_input_count_predicate/Forc.toml create mode 100644 test/src/sdk-harness/test_artifacts/tx_input_count_predicate/src/main.sw create mode 100644 test/src/sdk-harness/test_artifacts/tx_output_count_predicate/Forc.toml create mode 100644 test/src/sdk-harness/test_artifacts/tx_output_count_predicate/src/main.sw create mode 100644 test/src/sdk-harness/test_artifacts/tx_witness_predicate/Forc.toml create mode 100644 test/src/sdk-harness/test_artifacts/tx_witness_predicate/src/main.sw diff --git a/test/src/sdk-harness/test_artifacts/tx_input_count_predicate/Forc.toml b/test/src/sdk-harness/test_artifacts/tx_input_count_predicate/Forc.toml new file mode 100644 index 00000000000..2081295b12e --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_input_count_predicate/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "tx_input_count_predicate" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } diff --git a/test/src/sdk-harness/test_artifacts/tx_input_count_predicate/src/main.sw b/test/src/sdk-harness/test_artifacts/tx_input_count_predicate/src/main.sw new file mode 100644 index 00000000000..18ece1aba67 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_input_count_predicate/src/main.sw @@ -0,0 +1,7 @@ +predicate; + +use std::inputs::input_count; + +fn main(expected_count: u16) -> bool { + input_count() == expected_count +} diff --git a/test/src/sdk-harness/test_artifacts/tx_output_count_predicate/Forc.toml b/test/src/sdk-harness/test_artifacts/tx_output_count_predicate/Forc.toml new file mode 100644 index 00000000000..16b4e270afd --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_output_count_predicate/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "tx_output_count_predicate" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } diff --git a/test/src/sdk-harness/test_artifacts/tx_output_count_predicate/src/main.sw b/test/src/sdk-harness/test_artifacts/tx_output_count_predicate/src/main.sw new file mode 100644 index 00000000000..26264019a30 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_output_count_predicate/src/main.sw @@ -0,0 +1,7 @@ +predicate; + +use std::outputs::output_count; + +fn main(expected_count: u16) -> bool { + output_count() == expected_count +} diff --git a/test/src/sdk-harness/test_artifacts/tx_witness_predicate/Forc.toml b/test/src/sdk-harness/test_artifacts/tx_witness_predicate/Forc.toml new file mode 100644 index 00000000000..d7aa6cdc427 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_witness_predicate/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "tx_witness_predicate" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } diff --git a/test/src/sdk-harness/test_artifacts/tx_witness_predicate/src/main.sw b/test/src/sdk-harness/test_artifacts/tx_witness_predicate/src/main.sw new file mode 100644 index 00000000000..adb0ea9dbc9 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/tx_witness_predicate/src/main.sw @@ -0,0 +1,22 @@ +predicate; + +use std::tx::{tx_witnesses_count, tx_witness_data_length, tx_witness_data}; + +fn main(index: u64, expected_count: u64, expected_length: u64, expected_data: [u8; 64]) -> bool { + let count: u64 = tx_witnesses_count(); + let length: Option = tx_witness_data_length(index); + let data: Option<[u8; 64]> = tx_witness_data(index); + + assert(count == expected_count); + assert(length.is_some() && length.unwrap() == expected_length); + + assert(data.is_some()); + let data = data.unwrap(); + let mut iter = 0; + while iter < 64 { + assert(data[iter] == expected_data[iter]); + iter += 1; + } + + true +} From 0df73edc21d3a421e367e8694edd2b4f46907a98 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Wed, 11 Sep 2024 16:07:16 +0545 Subject: [PATCH 10/14] Add additional tests for witness data, inputs, and outputs --- test/src/sdk-harness/Forc.lock | 15 + test/src/sdk-harness/Forc.toml | 3 + .../src/sdk-harness/test_projects/auth/mod.rs | 4 +- .../test_projects/tx_fields/mod.rs | 325 ++++++++++++++++++ 4 files changed, 345 insertions(+), 2 deletions(-) diff --git a/test/src/sdk-harness/Forc.lock b/test/src/sdk-harness/Forc.lock index 248e0e3622f..37d81fdd61b 100644 --- a/test/src/sdk-harness/Forc.lock +++ b/test/src/sdk-harness/Forc.lock @@ -404,11 +404,21 @@ name = "tx_fields" source = "member" dependencies = ["std"] +[[package]] +name = "tx_input_count_predicate" +source = "member" +dependencies = ["std"] + [[package]] name = "tx_output_contract_creation_predicate" source = "member" dependencies = ["std"] +[[package]] +name = "tx_output_count_predicate" +source = "member" +dependencies = ["std"] + [[package]] name = "tx_output_predicate" source = "member" @@ -419,6 +429,11 @@ name = "tx_type_predicate" source = "member" dependencies = ["std"] +[[package]] +name = "tx_witness_predicate" +source = "member" +dependencies = ["std"] + [[package]] name = "type_aliases" source = "member" diff --git a/test/src/sdk-harness/Forc.toml b/test/src/sdk-harness/Forc.toml index 02ad7f4c4a6..f2a1df25d47 100644 --- a/test/src/sdk-harness/Forc.toml +++ b/test/src/sdk-harness/Forc.toml @@ -75,7 +75,10 @@ members = [ "test_artifacts/storage_vec/svec_u16", "test_artifacts/storage_vec/svec_u64", "test_artifacts/tx_contract", + "test_artifacts/tx_input_count_predicate", "test_artifacts/tx_output_contract_creation_predicate", + "test_artifacts/tx_output_count_predicate", "test_artifacts/tx_output_predicate", "test_artifacts/tx_type_predicate", + "test_artifacts/tx_witness_predicate", ] diff --git a/test/src/sdk-harness/test_projects/auth/mod.rs b/test/src/sdk-harness/test_projects/auth/mod.rs index 6fbe9c6502c..45441e38582 100644 --- a/test/src/sdk-harness/test_projects/auth/mod.rs +++ b/test/src/sdk-harness/test_projects/auth/mod.rs @@ -196,7 +196,7 @@ async fn can_get_predicate_address() { // Setup predicate. let hex_predicate_address: &str = - "0x8896a8f961e31af598faa2c4041d4d2745b0a11c57f8bb8bae65cff290076a74"; + "0x0c07e9f6e71da8855fb65a38f299a993aeba71fd0eef6c1ac4c79beff09cd6f7"; let predicate_address = Address::from_str(hex_predicate_address).expect("failed to create Address from string"); let predicate_bech32_address = Bech32Address::from(predicate_address); @@ -322,7 +322,7 @@ async fn when_incorrect_predicate_address_passed() { async fn can_get_predicate_address_in_message() { // Setup predicate address. let hex_predicate_address: &str = - "0x8896a8f961e31af598faa2c4041d4d2745b0a11c57f8bb8bae65cff290076a74"; + "0x0c07e9f6e71da8855fb65a38f299a993aeba71fd0eef6c1ac4c79beff09cd6f7"; let predicate_address = Address::from_str(hex_predicate_address).expect("failed to create Address from string"); let predicate_bech32_address = Bech32Address::from(predicate_address); diff --git a/test/src/sdk-harness/test_projects/tx_fields/mod.rs b/test/src/sdk-harness/test_projects/tx_fields/mod.rs index f75144c2682..6a4624f3d86 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -1,5 +1,6 @@ use fuel_vm::fuel_crypto::Hasher; use fuel_vm::fuel_tx::{ContractId, Input as TxInput}; +use fuels::core::codec::EncoderConfig; use fuels::types::transaction_builders::TransactionBuilder; use fuels::{ accounts::{predicate::Predicate, wallet::WalletUnlocked, Account}, @@ -18,6 +19,12 @@ const TX_CONTRACT_CREATION_PREDICATE_BYTECODE_PATH: &str = "test_artifacts/tx_output_contract_creation_predicate/out/release/tx_output_contract_creation_predicate.bin"; const TX_TYPE_PREDICATE_BYTECODE_PATH: &str = "test_artifacts/tx_type_predicate/out/release/tx_type_predicate.bin"; +const TX_WITNESS_PREDICATE_BYTECODE_PATH: &str = + "test_artifacts/tx_witness_predicate/out/release/tx_witness_predicate.bin"; +const TX_INPUT_COUNT_PREDICATE_BYTECODE_PATH: &str = + "test_artifacts/tx_input_count_predicate/out/release/tx_input_count_predicate.bin"; +const TX_OUTPUT_COUNT_PREDICATE_BYTECODE_PATH: &str = + "test_artifacts/tx_output_count_predicate/out/release/tx_output_count_predicate.bin"; use crate::tx_fields::Transaction as SwayTransaction; @@ -37,6 +44,18 @@ abigen!( Predicate( name = "TestTxTypePredicate", abi = "test_artifacts/tx_type_predicate/out/release/tx_type_predicate-abi.json" + ), + Predicate( + name = "TestTxWitnessPredicate", + abi = "test_artifacts/tx_witness_predicate/out/release/tx_witness_predicate-abi.json" + ), + Predicate( + name = "TestTxInputCountPredicate", + abi = "test_artifacts/tx_input_count_predicate/out/release/tx_input_count_predicate-abi.json" + ), + Predicate( + name = "TestTxOutputCountPredicate", + abi = "test_artifacts/tx_output_count_predicate/out/release/tx_output_count_predicate-abi.json" ) ); @@ -566,6 +585,114 @@ mod tx { .unwrap(); assert_eq!(predicate_balance, 0); } + + #[tokio::test] + async fn can_get_witness_in_tx_upload() { + // Prepare wallet and provider + let mut wallet = WalletUnlocked::new_random(None); + let num_coins = 100; + let coins = setup_single_asset_coins( + wallet.address(), + AssetId::zeroed(), + num_coins, + DEFAULT_COIN_AMOUNT, + ); + let provider = setup_test_provider(coins, vec![], None, None) + .await + .unwrap(); + wallet.set_provider(provider.clone()); + + // Prepare bytecode and subsections + let bytecode = fs::read(TX_CONTRACT_BYTECODE_PATH).unwrap(); + let subsection_size = 65536; + let subsections = UploadSubsection::split_bytecode(&bytecode, subsection_size).unwrap(); + + // Upload each sub section in a separate transaction and include the predicate with the transaction. + for subsection in subsections.clone() { + let mut builder = UploadTransactionBuilder::prepare_subsection_upload( + subsection, + TxPolicies::default(), + ); + + // Prepare the predicate + let witnesses = builder.witnesses().clone(); + let predicate_data = TestTxWitnessPredicateEncoder::new(EncoderConfig { + max_depth: 10, + max_tokens: 100_000, + }) + .encode_data( + 0, + witnesses.len() as u64 + 1, + witnesses[0].as_vec().len() as u64, + witnesses[0].as_vec().as_slice()[0..64].try_into().unwrap(), + ) + .unwrap(); + let predicate: Predicate = Predicate::load_from(TX_WITNESS_PREDICATE_BYTECODE_PATH) + .unwrap() + .with_provider(provider.clone()) + .with_data(predicate_data); + let predicate_coin_amount = 100; + + // Predicate has no funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); + wallet + .transfer( + predicate.address(), + predicate_coin_amount, + *provider.base_asset_id(), + TxPolicies::default(), + ) + .await + .unwrap(); + + // Predicate has funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!( + predicate_balance as usize, + predicate_coin_amount as usize * subsections.len() + ); + + // Inputs for predicate + let predicate_input = predicate + .get_asset_inputs_for_amount(*provider.base_asset_id(), 1, None) + .await + .unwrap(); + + // Outputs for predicate + let predicate_output = wallet.get_asset_outputs_for_amount( + &wallet.address(), + *provider.base_asset_id(), + 1, + ); + + // Append the predicate to the transaction + builder.inputs.push(predicate_input.get(0).unwrap().clone()); + builder + .outputs + .push(predicate_output.get(0).unwrap().clone()); + + wallet.add_witnesses(&mut builder).unwrap(); + wallet.adjust_for_fee(&mut builder, 0).await.unwrap(); + + // Submit the transaction + let tx = builder.build(&provider).await.unwrap(); + provider.send_transaction(tx).await.unwrap(); + + // The predicate has spent it's funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); + } + } } mod inputs { @@ -693,6 +820,105 @@ mod inputs { assert_eq!(result.value, false); } + #[tokio::test] + async fn can_get_input_count_in_tx_upload() { + // Prepare wallet and provider + let mut wallet = WalletUnlocked::new_random(None); + let num_coins = 100; + let coins = setup_single_asset_coins( + wallet.address(), + AssetId::zeroed(), + num_coins, + DEFAULT_COIN_AMOUNT, + ); + let provider = setup_test_provider(coins, vec![], None, None) + .await + .unwrap(); + wallet.set_provider(provider.clone()); + + // Prepare bytecode and subsections + let bytecode = fs::read(TX_CONTRACT_BYTECODE_PATH).unwrap(); + let subsection_size = 65536; + let subsections = UploadSubsection::split_bytecode(&bytecode, subsection_size).unwrap(); + + // Upload each sub section in a separate transaction and include the predicate with the transaction. + for subsection in subsections.clone() { + let mut builder = UploadTransactionBuilder::prepare_subsection_upload( + subsection, + TxPolicies::default(), + ); + + // Prepare the predicate + let predicate_data = TestTxInputCountPredicateEncoder::default() + .encode_data(builder.inputs().len() as u16 + 1u16) // Add one for this predicate + .unwrap(); + let predicate: Predicate = Predicate::load_from(TX_INPUT_COUNT_PREDICATE_BYTECODE_PATH) + .unwrap() + .with_provider(provider.clone()) + .with_data(predicate_data); + let predicate_coin_amount = 100; + + // Predicate has no funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); + wallet + .transfer( + predicate.address(), + predicate_coin_amount, + *provider.base_asset_id(), + TxPolicies::default(), + ) + .await + .unwrap(); + + // Predicate has funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!( + predicate_balance as usize, + predicate_coin_amount as usize * subsections.len() + ); + + // Inputs for predicate + let predicate_input = predicate + .get_asset_inputs_for_amount(*provider.base_asset_id(), 1, None) + .await + .unwrap(); + + // Outputs for predicate + let predicate_output = wallet.get_asset_outputs_for_amount( + &wallet.address(), + *provider.base_asset_id(), + 1, + ); + + // Append the predicate to the transaction + builder.inputs.push(predicate_input.get(0).unwrap().clone()); + builder + .outputs + .push(predicate_output.get(0).unwrap().clone()); + + wallet.add_witnesses(&mut builder).unwrap(); + wallet.adjust_for_fee(&mut builder, 0).await.unwrap(); + + // Submit the transaction + let tx = builder.build(&provider).await.unwrap(); + provider.send_transaction(tx).await.unwrap(); + + // The predicate has spent it's funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); + } + } + mod message { use fuels::types::{coin_type::CoinType, transaction_builders::TransactionBuilder}; @@ -1344,6 +1570,105 @@ mod outputs { .unwrap(); assert_eq!(result.value, None); } + + #[tokio::test] + async fn can_get_output_count_in_tx_upload() { + // Prepare wallet and provider + let mut wallet = WalletUnlocked::new_random(None); + let num_coins = 100; + let coins = setup_single_asset_coins( + wallet.address(), + AssetId::zeroed(), + num_coins, + DEFAULT_COIN_AMOUNT, + ); + let provider = setup_test_provider(coins, vec![], None, None) + .await + .unwrap(); + wallet.set_provider(provider.clone()); + + // Prepare bytecode and subsections + let bytecode = fs::read(TX_CONTRACT_BYTECODE_PATH).unwrap(); + let subsection_size = 65536; + let subsections = UploadSubsection::split_bytecode(&bytecode, subsection_size).unwrap(); + + // Upload each sub section in a separate transaction and include the predicate with the transaction. + for subsection in subsections.clone() { + let mut builder = UploadTransactionBuilder::prepare_subsection_upload( + subsection, + TxPolicies::default(), + ); + + // Prepare the predicate + let predicate_data = TestTxOutputCountPredicateEncoder::default() + .encode_data(builder.inputs().len() as u16 + 1u16) // Add one for this predicate + .unwrap(); + let predicate: Predicate = Predicate::load_from(TX_OUTPUT_COUNT_PREDICATE_BYTECODE_PATH) + .unwrap() + .with_provider(provider.clone()) + .with_data(predicate_data); + let predicate_coin_amount = 100; + + // Predicate has no funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); + wallet + .transfer( + predicate.address(), + predicate_coin_amount, + *provider.base_asset_id(), + TxPolicies::default(), + ) + .await + .unwrap(); + + // Predicate has funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!( + predicate_balance as usize, + predicate_coin_amount as usize * subsections.len() + ); + + // Inputs for predicate + let predicate_input = predicate + .get_asset_inputs_for_amount(*provider.base_asset_id(), 1, None) + .await + .unwrap(); + + // Outputs for predicate + let predicate_output = wallet.get_asset_outputs_for_amount( + &wallet.address(), + *provider.base_asset_id(), + 1, + ); + + // Append the predicate to the transaction + builder.inputs.push(predicate_input.get(0).unwrap().clone()); + builder + .outputs + .push(predicate_output.get(0).unwrap().clone()); + + wallet.add_witnesses(&mut builder).unwrap(); + wallet.adjust_for_fee(&mut builder, 0).await.unwrap(); + + // Submit the transaction + let tx = builder.build(&provider).await.unwrap(); + provider.send_transaction(tx).await.unwrap(); + + // The predicate has spent it's funds + let predicate_balance = predicate + .get_asset_balance(&provider.base_asset_id()) + .await + .unwrap(); + assert_eq!(predicate_balance, 0); + } + } } mod revert { From 4168a881ce81589e3faa722cb93fceb2b1f13c77 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Wed, 11 Sep 2024 16:11:53 +0545 Subject: [PATCH 11/14] Run formatter --- sway-lib-std/src/tx.sw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway-lib-std/src/tx.sw b/sway-lib-std/src/tx.sw index 15f1db12eb1..8d29be80bdf 100644 --- a/sway-lib-std/src/tx.sw +++ b/sway-lib-std/src/tx.sw @@ -433,7 +433,7 @@ pub fn tx_witness_data(index: u64) -> Option { let witness_data_ptr = __gtf::(index, GTF_WITNESS_DATA); let new_ptr = alloc_bytes(length); witness_data_ptr.copy_bytes_to(new_ptr, length); - + Some(asm(ptr: new_ptr) { ptr: T }) From 4b80f1317dce3d3f7418fe69352df02623d40853 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Wed, 11 Sep 2024 16:52:15 +0545 Subject: [PATCH 12/14] Handle value types --- sway-lib-std/src/tx.sw | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/sway-lib-std/src/tx.sw b/sway-lib-std/src/tx.sw index 8d29be80bdf..bc31fbab304 100644 --- a/sway-lib-std/src/tx.sw +++ b/sway-lib-std/src/tx.sw @@ -352,6 +352,7 @@ pub fn tx_witnesses_count() -> u64 { /// let witness_pointer = tx_witness_pointer(0).unwrap(); /// } /// ``` +#[allow(dead_code)] fn tx_witness_pointer(index: u64) -> Option { if index >= tx_witnesses_count() { return None @@ -430,13 +431,21 @@ pub fn tx_witness_data(index: u64) -> Option { None => return None, }; - let witness_data_ptr = __gtf::(index, GTF_WITNESS_DATA); - let new_ptr = alloc_bytes(length); - witness_data_ptr.copy_bytes_to(new_ptr, length); + if __is_reference_type::() { + let witness_data_ptr = __gtf::(index, GTF_WITNESS_DATA); + let new_ptr = alloc_bytes(length); + witness_data_ptr.copy_bytes_to(new_ptr, length); - Some(asm(ptr: new_ptr) { - ptr: T - }) + Some(asm(ptr: new_ptr) { + ptr: T + }) + } else { + Some( + __gtf::(index, GTF_WITNESS_DATA) + .add::(8 - __size_of::()) + .read::(), + ) + } } /// Get the transaction script start pointer. From 547785e886fe8bf9ce23f9168d5b9f8ec6ad0328 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Wed, 11 Sep 2024 16:56:14 +0545 Subject: [PATCH 13/14] Handle case for u8 --- sway-lib-std/src/tx.sw | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sway-lib-std/src/tx.sw b/sway-lib-std/src/tx.sw index bc31fbab304..033b937ec50 100644 --- a/sway-lib-std/src/tx.sw +++ b/sway-lib-std/src/tx.sw @@ -440,11 +440,12 @@ pub fn tx_witness_data(index: u64) -> Option { ptr: T }) } else { - Some( - __gtf::(index, GTF_WITNESS_DATA) - .add::(8 - __size_of::()) - .read::(), - ) + // u8 is the only value type that is less than 8 bytes and should be handled separately + if __size_of::() == 1 { + Some(__gtf::(index, GTF_WITNESS_DATA).add::(7).read::()) + } else { + Some(__gtf::(index, GTF_WITNESS_DATA).read::()) + } } } From 5453923f5b55289d108548d7812b0fb9f9523181 Mon Sep 17 00:00:00 2001 From: bitzoic Date: Wed, 11 Sep 2024 17:11:24 +0545 Subject: [PATCH 14/14] Fix e2e tests --- .../require_contract_deployment/asset_ops_test/src/main.sw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw index c84d1c235c4..f1458b54293 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw @@ -9,7 +9,7 @@ use test_fuel_coin_abi::*; #[cfg(experimental_new_encoding = false)] const FUEL_COIN_CONTRACT_ID = 0xec2277ebe007ade87e3d797c3b1e070dcd542d5ef8f038b471f262ef9cebc87c; #[cfg(experimental_new_encoding = true)] -const FUEL_COIN_CONTRACT_ID = 0x4b651789d4867c17419e95cefd50f49bdc78e8778118373b7aab6bcab2ca3d1e; +const FUEL_COIN_CONTRACT_ID = 0xf2fecff29038dab2ef571397ea5507359265c9154608e7de36ccbea20ed5c8aa; #[cfg(experimental_new_encoding = false)] const BALANCE_CONTRACT_ID = 0xf6cd545152ac83225e8e7df2efb5c6fa6e37bc9b9e977b5ea8103d28668925df;