From dd2104ee19b5988dc39f30d4471f050ce95746cf Mon Sep 17 00:00:00 2001 From: Nick Furfaro Date: Thu, 20 Oct 2022 07:44:35 -0600 Subject: [PATCH] Revamped implementation of `send_message` (#2796) This updates the wrapper for the `smo` opcode to follow the example of the wrapper for the `tro` opcode (send_message_output()`). closes #2899 Co-authored-by: Braqzen Co-authored-by: Braqzen <103777923+Braqzen@users.noreply.github.com> Co-authored-by: Mohammad Fawaz --- sway-lib-std/src/lib.sw | 1 + sway-lib-std/src/message.sw | 50 +++++++++++ .../language/logging/json_abi_oracle.json | 20 ++--- .../stdlib/require/json_abi_oracle.json | 12 +-- .../multiple_impl/json_abi_oracle.json | 4 +- .../test_projects/token_ops/mod.rs | 86 ++++++++++++++++++- .../test_projects/token_ops/src/main.sw | 10 ++- 7 files changed, 160 insertions(+), 23 deletions(-) create mode 100644 sway-lib-std/src/message.sw diff --git a/sway-lib-std/src/lib.sw b/sway-lib-std/src/lib.sw index bf177bbf91d..2e1b1a403c6 100644 --- a/sway-lib-std/src/lib.sw +++ b/sway-lib-std/src/lib.sw @@ -30,6 +30,7 @@ dep flags; dep u128; dep u256; dep vec; +dep message; dep prelude; use core::*; diff --git a/sway-lib-std/src/message.sw b/sway-lib-std/src/message.sw new file mode 100644 index 00000000000..6a9595185d5 --- /dev/null +++ b/sway-lib-std/src/message.sw @@ -0,0 +1,50 @@ +library message; + +use ::alloc::alloc; +use ::outputs::{Output, output_count, output_type}; +use ::mem::{addr_of, copy}; +use ::revert::revert; +use ::vec::Vec; + +const FAILED_SEND_MESSAGE_SIGNAL = 0xffff_ffff_ffff_0002; + +/// Sends a message to `recipient` of length `msg_len` through `output` with amount of `coins` +/// +/// # Arguments +/// +/// * `recipient` - The address of the message recipient +/// * `msg_data` - arbitrary length message data +/// * `coins` - Amount of base asset sent +pub fn send_message(recipient: b256, msg_data: Vec, coins: u64) { + let mut recipient_heap_buffer = 0; + let mut data_heap_buffer = 0; + let mut size = 0; + + // If msg_data is empty, we just ignore it and pass `smo` a pointer to the inner value of recipient. + // Otherwise, we allocate adjacent space on the heap for the data and the recipient and copy the + // data and recipient values there + if msg_data.is_empty() { + recipient_heap_buffer = addr_of(recipient); + } else { + size = msg_data.len() * 8; + data_heap_buffer = alloc(size); + recipient_heap_buffer = alloc(32); + copy(msg_data.buf.ptr, data_heap_buffer, size); + copy(addr_of(recipient), recipient_heap_buffer, 32); + }; + + let mut index = 0; + let outputs = output_count(); + + while index < outputs { + let type_of_output = output_type(index); + if let Output::Message = type_of_output { + asm(r1: recipient_heap_buffer, r2: size, r3: index, r4: coins) { + smo r1 r2 r3 r4; + }; + return; + } + index += 1; + } + revert(FAILED_SEND_MESSAGE_SIGNAL); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/logging/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/logging/json_abi_oracle.json index dde78f8487c..1073305464a 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/logging/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/logging/json_abi_oracle.json @@ -12,7 +12,7 @@ ], "loggedTypes": [ { - "logId": 165743, + "logId": 167472, "loggedType": { "name": "", "type": 2, @@ -20,7 +20,7 @@ } }, { - "logId": 165749, + "logId": 167478, "loggedType": { "name": "", "type": 11, @@ -28,7 +28,7 @@ } }, { - "logId": 165757, + "logId": 167486, "loggedType": { "name": "", "type": 10, @@ -36,7 +36,7 @@ } }, { - "logId": 165764, + "logId": 167493, "loggedType": { "name": "", "type": 9, @@ -44,7 +44,7 @@ } }, { - "logId": 165771, + "logId": 167500, "loggedType": { "name": "", "type": 12, @@ -52,7 +52,7 @@ } }, { - "logId": 165723, + "logId": 167452, "loggedType": { "name": "", "type": 7, @@ -60,7 +60,7 @@ } }, { - "logId": 165783, + "logId": 167512, "loggedType": { "name": "", "type": 1, @@ -68,7 +68,7 @@ } }, { - "logId": 165737, + "logId": 167466, "loggedType": { "name": "", "type": 8, @@ -82,7 +82,7 @@ } }, { - "logId": 165741, + "logId": 167470, "loggedType": { "name": "", "type": 5, @@ -90,7 +90,7 @@ } }, { - "logId": 165805, + "logId": 167534, "loggedType": { "name": "", "type": 4, diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/require/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/require/json_abi_oracle.json index 281961d9b1f..acba1ddf6be 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/require/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/require/json_abi_oracle.json @@ -12,7 +12,7 @@ ], "loggedTypes": [ { - "logId": 165711, + "logId": 167440, "loggedType": { "name": "", "type": 3, @@ -20,7 +20,7 @@ } }, { - "logId": 165737, + "logId": 167466, "loggedType": { "name": "", "type": 3, @@ -28,7 +28,7 @@ } }, { - "logId": 165757, + "logId": 167486, "loggedType": { "name": "", "type": 3, @@ -36,7 +36,7 @@ } }, { - "logId": 165777, + "logId": 167506, "loggedType": { "name": "", "type": 3, @@ -44,7 +44,7 @@ } }, { - "logId": 165799, + "logId": 167528, "loggedType": { "name": "", "type": 3, @@ -52,7 +52,7 @@ } }, { - "logId": 165815, + "logId": 167544, "loggedType": { "name": "", "type": 2, diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json index 55b50e32e62..8bed9ad5fb7 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_abi_oracle.json @@ -12,7 +12,7 @@ ], "loggedTypes": [ { - "logId": 165697, + "logId": 167426, "loggedType": { "name": "", "type": 1, @@ -20,7 +20,7 @@ } }, { - "logId": 165711, + "logId": 167440, "loggedType": { "name": "", "type": 1, diff --git a/test/src/sdk-harness/test_projects/token_ops/mod.rs b/test/src/sdk-harness/test_projects/token_ops/mod.rs index 86898a05d42..6987dce942b 100644 --- a/test/src/sdk-harness/test_projects/token_ops/mod.rs +++ b/test/src/sdk-harness/test_projects/token_ops/mod.rs @@ -1,5 +1,7 @@ +use fuel_gql_client::fuel_tx::Receipt; use fuels::prelude::*; -use fuels::tx::{AssetId, ContractId}; +use fuels::tx::AssetId; +use std::str::FromStr; abigen!( TestFuelCoinContract, @@ -352,6 +354,88 @@ async fn can_perform_generic_transfer_to_contract() { assert_eq!(result.value, amount) } +#[tokio::test] +async fn can_send_message_output_with_data() { + let num_wallets = 1; + let coins_per_wallet = 1; + let amount_per_coin = 1_000_000; + + let config = WalletsConfig::new( + Some(num_wallets), + Some(coins_per_wallet), + Some(amount_per_coin), + ); + + let wallets = launch_custom_provider_and_get_wallets(config, None).await; + let (fuelcoin_instance, fuelcoin_id) = get_fuelcoin_instance(wallets[0].clone()).await; + + let amount = 33u64; + let recipient_address: Address = wallets[0].address().into(); + + let call_response = fuelcoin_instance + .methods() + .send_message(Bits256(*recipient_address), vec![100, 75, 50], amount) + .append_message_outputs(1) + .call() + .await + .unwrap(); + + let message_receipt = call_response + .receipts + .iter() + .find(|&r| matches!(r, Receipt::MessageOut { .. })) + .unwrap(); + + assert_eq!(*fuelcoin_id, **message_receipt.sender().unwrap()); + assert_eq!(&recipient_address, message_receipt.recipient().unwrap()); + assert_eq!(amount, message_receipt.amount().unwrap()); + assert_eq!(24, message_receipt.len().unwrap()); + assert_eq!( + vec![0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 0, 0, 0, 50], + message_receipt.data().unwrap() + ); +} + +#[tokio::test] +async fn can_send_message_output_without_data() { + let num_wallets = 1; + let coins_per_wallet = 1; + let amount_per_coin = 1_000_000; + + let config = WalletsConfig::new( + Some(num_wallets), + Some(coins_per_wallet), + Some(amount_per_coin), + ); + + let wallets = launch_custom_provider_and_get_wallets(config, None).await; + let (fuelcoin_instance, fuelcoin_id) = get_fuelcoin_instance(wallets[0].clone()).await; + + let amount = 33u64; + let recipient_hex = "0x000000000000000000000000b46a7a1a23f3897cc83a94521a96da5c23bc58db"; + let recipient_address = Address::from_str(&recipient_hex).unwrap(); + + let call_response = fuelcoin_instance + .methods() + .send_message(Bits256(*recipient_address), Vec::::new(), amount) + .append_message_outputs(1) + .call() + .await + .unwrap(); + + let message_receipt = call_response + .receipts + .iter() + .find(|&r| matches!(r, Receipt::MessageOut { .. })) + .unwrap(); + + assert_eq!(*fuelcoin_id, **message_receipt.sender().unwrap()); + assert_eq!(&recipient_address, message_receipt.recipient().unwrap()); + assert_eq!(amount, message_receipt.amount().unwrap()); + assert_eq!(0, message_receipt.len().unwrap()); + assert_eq!(Vec::::new(), message_receipt.data().unwrap()); +} + async fn get_fuelcoin_instance(wallet: WalletUnlocked) -> (TestFuelCoinContract, ContractId) { let fuelcoin_id = Contract::deploy( "test_projects/token_ops/out/debug/token_ops.bin", diff --git a/test/src/sdk-harness/test_projects/token_ops/src/main.sw b/test/src/sdk-harness/test_projects/token_ops/src/main.sw index 9224b26061b..732e328d89c 100644 --- a/test/src/sdk-harness/test_projects/token_ops/src/main.sw +++ b/test/src/sdk-harness/test_projects/token_ops/src/main.sw @@ -1,9 +1,6 @@ contract; -use std::{ - context::balance_of, - token::*, -}; +use std::{context::balance_of, message::send_message, token::*}; abi TestFuelCoin { fn mint_coins(mint_amount: u64); @@ -15,6 +12,7 @@ abi TestFuelCoin { fn mint_and_send_to_address(amount: u64, to: Address); fn generic_mint_to(amount: u64, to: Identity); fn generic_transfer(amount: u64, asset_id: ContractId, to: Identity); + fn send_message(recipient: b256, msg_data: Vec, coins: u64); } impl TestFuelCoin for Contract { @@ -53,4 +51,8 @@ impl TestFuelCoin for Contract { fn generic_transfer(amount: u64, asset_id: ContractId, to: Identity) { transfer(amount, asset_id, to) } + + fn send_message(recipient: b256, msg_data: Vec, coins: u64) { + send_message(recipient, msg_data, coins); + } }