Skip to content

Commit

Permalink
Revamped implementation of send_message (FuelLabs#2796)
Browse files Browse the repository at this point in the history
This updates the wrapper for the `smo` opcode to follow the example of
the wrapper for the `tro` opcode (send_message_output()`).

closes FuelLabs#2899

Co-authored-by: Braqzen <[email protected]>
Co-authored-by: Braqzen <[email protected]>
Co-authored-by: Mohammad Fawaz <[email protected]>
  • Loading branch information
4 people authored Oct 20, 2022
1 parent 438aa02 commit dd2104e
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 23 deletions.
1 change: 1 addition & 0 deletions sway-lib-std/src/lib.sw
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dep flags;
dep u128;
dep u256;
dep vec;
dep message;
dep prelude;

use core::*;
50 changes: 50 additions & 0 deletions sway-lib-std/src/message.sw
Original file line number Diff line number Diff line change
@@ -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<u64>, 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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,63 +12,63 @@
],
"loggedTypes": [
{
"logId": 165743,
"logId": 167472,
"loggedType": {
"name": "",
"type": 2,
"typeArguments": null
}
},
{
"logId": 165749,
"logId": 167478,
"loggedType": {
"name": "",
"type": 11,
"typeArguments": null
}
},
{
"logId": 165757,
"logId": 167486,
"loggedType": {
"name": "",
"type": 10,
"typeArguments": null
}
},
{
"logId": 165764,
"logId": 167493,
"loggedType": {
"name": "",
"type": 9,
"typeArguments": null
}
},
{
"logId": 165771,
"logId": 167500,
"loggedType": {
"name": "",
"type": 12,
"typeArguments": null
}
},
{
"logId": 165723,
"logId": 167452,
"loggedType": {
"name": "",
"type": 7,
"typeArguments": null
}
},
{
"logId": 165783,
"logId": 167512,
"loggedType": {
"name": "",
"type": 1,
"typeArguments": null
}
},
{
"logId": 165737,
"logId": 167466,
"loggedType": {
"name": "",
"type": 8,
Expand All @@ -82,15 +82,15 @@
}
},
{
"logId": 165741,
"logId": 167470,
"loggedType": {
"name": "",
"type": 5,
"typeArguments": []
}
},
{
"logId": 165805,
"logId": 167534,
"loggedType": {
"name": "",
"type": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,47 +12,47 @@
],
"loggedTypes": [
{
"logId": 165711,
"logId": 167440,
"loggedType": {
"name": "",
"type": 3,
"typeArguments": null
}
},
{
"logId": 165737,
"logId": 167466,
"loggedType": {
"name": "",
"type": 3,
"typeArguments": null
}
},
{
"logId": 165757,
"logId": 167486,
"loggedType": {
"name": "",
"type": 3,
"typeArguments": null
}
},
{
"logId": 165777,
"logId": 167506,
"loggedType": {
"name": "",
"type": 3,
"typeArguments": null
}
},
{
"logId": 165799,
"logId": 167528,
"loggedType": {
"name": "",
"type": 3,
"typeArguments": null
}
},
{
"logId": 165815,
"logId": 167544,
"loggedType": {
"name": "",
"type": 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
],
"loggedTypes": [
{
"logId": 165697,
"logId": 167426,
"loggedType": {
"name": "",
"type": 1,
"typeArguments": null
}
},
{
"logId": 165711,
"logId": 167440,
"loggedType": {
"name": "",
"type": 1,
Expand Down
86 changes: 85 additions & 1 deletion test/src/sdk-harness/test_projects/token_ops/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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::<u64>::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::<u8>::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",
Expand Down
10 changes: 6 additions & 4 deletions test/src/sdk-harness/test_projects/token_ops/src/main.sw
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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<u64>, coins: u64);
}

impl TestFuelCoin for Contract {
Expand Down Expand Up @@ -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<u64>, coins: u64) {
send_message(recipient, msg_data, coins);
}
}

0 comments on commit dd2104e

Please sign in to comment.