Skip to content

Commit

Permalink
fix: encode contract arguments with gas_forwarded (FuelLabs#1173)
Browse files Browse the repository at this point in the history
  • Loading branch information
hal3e authored Oct 30, 2023
1 parent 66aef68 commit 9095c1e
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 29 deletions.
51 changes: 22 additions & 29 deletions packages/fuels-programs/src/call_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 228,9 @@ pub(crate) fn get_instructions(
/// Returns script data, consisting of the following items in the given order:
/// 1. Amount to be forwarded `(1 * `[`WORD_SIZE`]`)`
/// 2. Asset ID to be forwarded ([`AssetId::LEN`])
/// 3. Contract ID ([`ContractId::LEN`]);
/// 4. Function selector `(1 * `[`WORD_SIZE`]`)`
/// 5. Gas to be forwarded `(1 * `[`WORD_SIZE`]`)`
/// 3. Gas to be forwarded `(1 * `[`WORD_SIZE`]`)` - Optional
/// 4. Contract ID ([`ContractId::LEN`]);
/// 5. Function selector `(1 * `[`WORD_SIZE`]`)`
/// 6. Calldata offset (optional) `(1 * `[`WORD_SIZE`]`)`
/// 7. Encoded arguments (optional) (variable length)
pub(crate) fn build_script_data_from_contract_calls(
Expand All @@ -246,28 246,28 @@ pub(crate) fn build_script_data_from_contract_calls(
for call in calls {
let gas_forwarded = call.call_parameters.gas_forwarded();

let call_param_offsets = CallOpcodeParamsOffset {
amount_offset: segment_offset,
asset_id_offset: segment_offset WORD_SIZE,
call_data_offset: segment_offset WORD_SIZE AssetId::LEN,
gas_forwarded_offset: gas_forwarded
.map(|_| segment_offset WORD_SIZE AssetId::LEN ContractId::LEN WORD_SIZE),
};
param_offsets.push(call_param_offsets);

script_data.extend(call.call_parameters.amount().to_be_bytes());
script_data.extend(call.call_parameters.asset_id().iter());
script_data.extend(call.contract_id.hash().as_ref());
script_data.extend(call.encoded_selector);

let gas_forwarded_size = gas_forwarded
.map(|gf| {
script_data.extend(gf.to_be_bytes());
script_data.extend((gf as Word).to_be_bytes());

WORD_SIZE
})
.unwrap_or_default();

script_data.extend(call.contract_id.hash().as_ref());
script_data.extend(call.encoded_selector);

let call_param_offsets = CallOpcodeParamsOffset {
amount_offset: segment_offset,
asset_id_offset: segment_offset WORD_SIZE,
gas_forwarded_offset: gas_forwarded.map(|_| segment_offset WORD_SIZE AssetId::LEN),
call_data_offset: segment_offset WORD_SIZE AssetId::LEN gas_forwarded_size,
};
param_offsets.push(call_param_offsets);

// If the method call takes custom inputs or has more than
// one argument, we need to calculate the `call_data_offset`,
// which points to where the data for the custom types start in the
Expand All @@ -278,9 278,9 @@ pub(crate) fn build_script_data_from_contract_calls(
let custom_input_offset = segment_offset
WORD_SIZE // amount size
AssetId::LEN
gas_forwarded_size
ContractId::LEN
WORD_SIZE // encoded_selector size
gas_forwarded_size
WORD_SIZE; // custom_input_offset size
script_data.extend((custom_input_offset as Word).to_be_bytes());

Expand All @@ -289,7 289,7 @@ pub(crate) fn build_script_data_from_contract_calls(
segment_offset
};

let bytes = call.encoded_args.resolve(encoded_args_start_offset as u64);
let bytes = call.encoded_args.resolve(encoded_args_start_offset as Word);
script_data.extend(bytes);

// the data segment that holds the parameters for the next call
Expand Down Expand Up @@ -609,7 609,6 @@ mod test {
async fn test_script_data() {
// Arrange
const SELECTOR_LEN: usize = WORD_SIZE;
const GAS_FORWARDED_SIZE: usize = WORD_SIZE;
const NUM_CALLS: usize = 3;

let contract_ids = vec![
Expand Down Expand Up @@ -678,31 677,25 @@ mod test {
}

// Calls 1 and 3 have their input arguments after the selector
let call_1_arg_offset =
param_offsets[0].call_data_offset ContractId::LEN SELECTOR_LEN GAS_FORWARDED_SIZE;
let call_1_arg_offset = param_offsets[0].call_data_offset ContractId::LEN SELECTOR_LEN;
let call_1_arg = script_data[call_1_arg_offset..call_1_arg_offset WORD_SIZE].to_vec();
assert_eq!(call_1_arg, args[0].resolve(0));

let call_3_arg_offset =
param_offsets[2].call_data_offset ContractId::LEN SELECTOR_LEN GAS_FORWARDED_SIZE;
let call_3_arg_offset = param_offsets[2].call_data_offset ContractId::LEN SELECTOR_LEN;
let call_3_arg = script_data[call_3_arg_offset..call_3_arg_offset WORD_SIZE].to_vec();
assert_eq!(call_3_arg, args[2].resolve(0));

// Call 2 has custom inputs and custom_input_offset
let call_2_arg_offset =
param_offsets[1].call_data_offset ContractId::LEN SELECTOR_LEN GAS_FORWARDED_SIZE;
let call_2_arg_offset = param_offsets[1].call_data_offset ContractId::LEN SELECTOR_LEN;
let custom_input_offset =
script_data[call_2_arg_offset..call_2_arg_offset WORD_SIZE].to_vec();
assert_eq!(
custom_input_offset,
(call_2_arg_offset WORD_SIZE).to_be_bytes()
);

let custom_input_offset = param_offsets[1].call_data_offset
ContractId::LEN
SELECTOR_LEN
GAS_FORWARDED_SIZE
WORD_SIZE;
let custom_input_offset =
param_offsets[1].call_data_offset ContractId::LEN SELECTOR_LEN WORD_SIZE;
let custom_input =
script_data[custom_input_offset..custom_input_offset WORD_SIZE].to_vec();
assert_eq!(custom_input, args[1].resolve(0));
Expand Down
64 changes: 64 additions & 0 deletions packages/fuels/tests/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1666,3 1666,67 @@ async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() ->

Ok(())
}

#[tokio::test]
async fn test_arguments_with_gas_forwarded() -> Result<()> {
setup_program_test!(
Wallets("wallet"),
Abigen(
Contract(
name = "TestContract",
project = "packages/fuels/tests/contracts/contract_test"
),
Contract(
name = "VectorOutputContract",
project = "packages/fuels/tests/types/contracts/vectors"
)
),
Deploy(
name = "contract_instance",
contract = "TestContract",
wallet = "wallet"
),
Deploy(
name = "contract_instance_2",
contract = "VectorOutputContract",
wallet = "wallet"
),
);

let x = 128;
let vec_input = vec![0, 1, 2];
{
let response = contract_instance
.methods()
.get_single(x)
.call_params(CallParameters::default().with_gas_forwarded(1024))?
.call()
.await?;

assert_eq!(response.value, x);
}
{
contract_instance_2
.methods()
.u32_vec(vec_input.clone())
.call_params(CallParameters::default().with_gas_forwarded(1024))?
.call()
.await?;
}
{
let call_handler_1 = contract_instance.methods().get_single(x);
let call_handler_2 = contract_instance_2.methods().u32_vec(vec_input);

let mut multi_call_handler = MultiContractCallHandler::new(wallet.clone());

multi_call_handler
.add_call(call_handler_1)
.add_call(call_handler_2);

let (value, _): (u64, ()) = multi_call_handler.call().await?.value;

assert_eq!(value, x);
}

Ok(())
}

0 comments on commit 9095c1e

Please sign in to comment.