Skip to content

Commit

Permalink
feat: experimental encoding for contracts, scripts and predicates (#1303
Browse files Browse the repository at this point in the history
)

closes: #1278,
#1279,
#1046

This PR adds support for the new encoding scheme for contracts, scripts
and predicates.

I have added a new `ExperimentalBoundedEncoder` which can be activated
with the `experimental` cfg flag.
I have tried to minimize the impact of the new encoder as much as
possible to make it easier for review. A full refactor of the whole sdk
is necessary once the new encoding becomes the default one.

- The function selector changed and now it is the name of the method.
- The `CALL` opcode changed with the new encoding and is expecting the
following call data: ContractID, pointer to fn_selector (name of the
method), pointer to encoded arguments, number of coins, asset_id,
gas_forwarded.
  • Loading branch information
hal3e committed Apr 2, 2024
1 parent 96585fe commit 0e99f41
Show file tree
Hide file tree
Showing 44 changed files with 1,657 additions and 1,261 deletions.
21 changes: 8 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ env:
FUEL_CORE_VERSION: 0.23.0
FUEL_CORE_PATCH_BRANCH:
RUST_VERSION: 1.76.0
FORC_VERSION: 0.51.1
FORC_VERSION: 0.52.0
FORC_PATCH_BRANCH: ""
FORC_PATCH_REVISION: ""

Expand Down Expand Up @@ -98,14 +98,14 @@ jobs:
!packages/fuels/tests/.gitignore
# TODO: To be removed once experimental encoding is the default
- name: Build Sway test projects w experimental logs
- name: Build Sway test projects w experimental encoding
run: forc build --terse --error-on-warnings --json-abi-with-callpaths --experimental-new-encoding
working-directory: packages/fuels

- uses: actions/upload-artifact@v2
with:
retention-days: 2
name: sway-examples-w-experimental-logs
name: sway-examples-w-experimental-encoding
# cache only the sway build artifacts, skip all src files
path: |
packages/fuels/tests
Expand Down Expand Up @@ -202,9 +202,9 @@ jobs:
- command: check_doc_unresolved_links
args:
# TODO: To be removed once experimental encoding is the default
- command: test_experimental_logs
args:
download_sway_artifacts: sway-examples-w-experimental-logs
- cargo_command: nextest
args: run --all-targets --features "experimental" --workspace
download_sway_artifacts: sway-examples-w-experimental-encoding
install_fuel_core: true
steps:
- name: Checkout repository
Expand Down Expand Up @@ -242,9 +242,8 @@ jobs:
name: ${{ matrix.download_sway_artifacts }}
path: packages/fuels/tests/

# TODO: `test_experimental_logs` to be removed once experimental encoding is the default.
- name: Install nextest
if: ${{ matrix.cargo_command == 'nextest' || matrix.command == 'test_experimental_logs' }}
if: ${{ matrix.cargo_command == 'nextest' }}
uses: taiki-e/install-action@nextest

- name: Install cargo-machete
Expand Down Expand Up @@ -272,6 +271,7 @@ jobs:
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
cd packages/wasm-tests
wasm-pack test --node
wasm-pack test --node --features experimental
- name: Check for invalid documentation anchors
if: ${{ matrix.command == 'check_doc_anchors_valid' }}
Expand All @@ -282,11 +282,6 @@ jobs:
run: |
! cargo doc --document-private-items |& grep -A 6 "warning: unresolved link to"
# TODO: To be removed once experimental encoding is the default.
- name: Test experimental logs
if: ${{ matrix.command == 'test_experimental_logs' }}
run: RUSTFLAGS='--cfg experimental' cargo nextest run --test logs

publish:
needs:
- cargo-verifications
Expand Down
13 changes: 12 additions & 1 deletion docs/src/types/custom_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,18 @@ impl MyContract for Contract {
Your Rust code would look like this:

```rust,ignore
{{#include ../../../packages/fuels/tests/types_contracts.rs:generic}}
// simple struct with a single generic param
let arg1 = SimpleGeneric {
single_generic_param: 123u64,
};
let result = contract_methods
.struct_w_generic(arg1.clone())
.call()
.await?
.value;
assert_eq!(result, arg1);
```

### Unused generic type parameters
Expand Down
1 change: 1 addition & 0 deletions examples/contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ tokio = { workspace = true, features = ["full"] }
[features]
fuel-core-lib = ["fuels/fuel-core-lib"]
rocksdb = ["fuels/rocksdb"]
experimental = ["fuels/experimental"]
16 changes: 14 additions & 2 deletions examples/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ mod tests {
.await?;
// ANCHOR_END: contract_call_cost_estimation

assert_eq!(transaction_cost.gas_used, 791);
let expected_gas = if cfg!(feature = "experimental") {
2087
} else {
796
};

assert_eq!(transaction_cost.gas_used, expected_gas);

Ok(())
}
Expand Down Expand Up @@ -602,7 +608,12 @@ mod tests {
.await?;
// ANCHOR_END: multi_call_cost_estimation

assert_eq!(transaction_cost.gas_used, 1162);
#[cfg(not(feature = "experimental"))]
let expected_gas = 1172;
#[cfg(feature = "experimental")]
let expected_gas = 3513;

assert_eq!(transaction_cost.gas_used, expected_gas);

Ok(())
}
Expand Down Expand Up @@ -677,6 +688,7 @@ mod tests {
}

#[tokio::test]
#[cfg(not(feature = "experimental"))]
async fn low_level_call_example() -> Result<()> {
use fuels::{
core::codec::{calldata, fn_selector},
Expand Down
3 changes: 3 additions & 0 deletions examples/debugging/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ fuels = { workspace = true }
rand = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["full"] }

[features]
experimental = ["fuels/experimental"]
120 changes: 13 additions & 107 deletions examples/debugging/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
#[cfg(test)]
mod tests {
#[cfg(not(feature = "experimental"))]
use std::collections::HashMap;

#[cfg(not(feature = "experimental"))]
use fuel_abi_types::abi::program::ProgramABI;
use fuels::{
core::{
codec::{calldata, fn_selector, resolve_fn_selector, ABIDecoder},
traits::Parameterize,
},
macros::abigen,
types::{errors::Result, param_types::ParamType, SizedAsciiString},
};
#[cfg(not(feature = "experimental"))]
use fuels::core::codec::{calldata, fn_selector};
#[cfg(not(feature = "experimental"))]
use fuels::types::{errors::Result, param_types::ParamType, SizedAsciiString};

#[cfg(not(feature = "experimental"))]
#[test]
fn get_a_fn_selector() {
use fuels::core::{codec::resolve_fn_selector, traits::Parameterize};

// ANCHOR: example_fn_selector
// fn some_fn_name(arg1: Vec<str[3]>, arg2: u8)
let fn_name = "some_fn_name";
Expand All @@ -25,8 +26,11 @@ mod tests {
// ANCHOR_END: example_fn_selector
}

#[cfg(not(feature = "experimental"))]
#[test]
fn a_fn_selector_from_json_abi() -> Result<()> {
use fuels::core::codec::resolve_fn_selector;

let json_abi_file =
"../../packages/fuels/tests/contracts/contract_test/out/debug/contract_test-abi.json";
let abi_file_contents = std::fs::read_to_string(json_abi_file)?;
Expand Down Expand Up @@ -60,6 +64,7 @@ mod tests {
Ok(())
}

#[cfg(not(feature = "experimental"))]
#[test]
fn test_macros() -> Result<()> {
let function_selector = fn_selector!(initialize_counter(u64));
Expand All @@ -70,103 +75,4 @@ mod tests {

Ok(())
}

#[test]
fn decoded_debug_matches_rust_debug() -> Result<()> {
abigen!(Contract(
name = "MyContract",
abi = "packages/fuels/tests/types/contracts/generics/out/debug/generics-abi.json"
));

let json_abi_file =
"../../packages/fuels/tests/types/contracts/generics/out/debug/generics-abi.json";
let abi_file_contents = std::fs::read_to_string(json_abi_file)?;

let parsed_abi: ProgramABI = serde_json::from_str(&abi_file_contents)?;

let type_lookup = parsed_abi
.types
.into_iter()
.map(|decl| (decl.type_id, decl))
.collect::<HashMap<_, _>>();

let get_first_fn_argument = |fn_name: &str| {
parsed_abi
.functions
.iter()
.find(|abi_fun| abi_fun.name == fn_name)
.expect("should be there")
.inputs
.first()
.expect("should be there")
};
let decoder = ABIDecoder::default();

{
// simple struct with a single generic parameter
let type_application = get_first_fn_argument("struct_w_generic");
let param_type = ParamType::try_from_type_application(type_application, &type_lookup)?;

let expected_struct = SimpleGeneric {
single_generic_param: 123u64,
};

assert_eq!(
format!("{expected_struct:?}"),
decoder.decode_as_debug_str(&param_type, &[0, 0, 0, 0, 0, 0, 0, 123])?
);
}
{
// struct that delegates the generic param internally
let type_application = get_first_fn_argument("struct_delegating_generic");
let param_type = ParamType::try_from_type_application(type_application, &type_lookup)?;

let expected_struct = PassTheGenericOn {
one: SimpleGeneric {
single_generic_param: SizedAsciiString::<3>::try_from("abc")?,
},
};

assert_eq!(
format!("{expected_struct:?}"),
decoder.decode_as_debug_str(&param_type, &[97, 98, 99])?
);
}
{
// enum with generic in variant
let type_application = get_first_fn_argument("enum_w_generic");
let param_type = ParamType::try_from_type_application(type_application, &type_lookup)?;

let expected_enum = EnumWGeneric::B(10u64);

assert_eq!(
format!("{expected_enum:?}"),
decoder.decode_as_debug_str(
&param_type,
&[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10]
)?
);
}
{
// logged type
let logged_type = parsed_abi
.logged_types
.as_ref()
.expect("has logs")
.first()
.expect("has log");

let param_type =
ParamType::try_from_type_application(&logged_type.application, &type_lookup)?;

let expected_u8 = 1;

assert_eq!(
format!("{expected_u8}"),
decoder.decode_as_debug_str(&param_type, &[0, 0, 0, 0, 0, 0, 0, 1])?
);
}

Ok(())
}
}
1 change: 1 addition & 0 deletions packages/fuels-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ tokio = { workspace = true, features = ["test-util", "macros"] }
[features]
default = ["std"]
std = ["dep:fuel-core-client"]
experimental = []
3 changes: 3 additions & 0 deletions packages/fuels-core/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ mod tests {

#[test]
fn can_convert_bytes_into_tuple() -> Result<()> {
#[cfg(not(feature = "experimental"))]
let tuple_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2];
#[cfg(feature = "experimental")]
let tuple_in_bytes: Vec<u8> = vec![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2];

let the_tuple: (u64, u32) = try_from_bytes(&tuple_in_bytes, DecoderConfig::default())?;

Expand Down

1 comment on commit 0e99f41

@Dtpsptms15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sound good

Please sign in to comment.