Skip to content

Commit

Permalink
feat: script and predicate blobs (#1520)
Browse files Browse the repository at this point in the history
closes: #1519 

Scripts and predicates can be pre-uploaded with blobs and then run with loader code.
---------

Co-authored-by: Oleksii Filonenko <12615679+Br1ght0ne@users.noreply.github.com>
Co-authored-by: hal3e <git@hal3e.io>
Co-authored-by: Rodrigo Araújo <rod.dearaujo@gmail.com>
  • Loading branch information
4 people authored Oct 6, 2024
1 parent 84266d2 commit 2a67e3b
Show file tree
Hide file tree
Showing 32 changed files with 1,174 additions and 113 deletions.
61 changes: 49 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ env:
CARGO_TERM_COLOR: always
DASEL_VERSION: https://github.com/TomWright/dasel/releases/download/v2.3.6/dasel_linux_amd64
RUSTFLAGS: "-D warnings"
FUEL_CORE_VERSION: 0.36.0
FUEL_CORE_PATCH_BRANCH:
FUEL_CORE_VERSION: 0.37.0
FUEL_CORE_PATCH_BRANCH: ""
FUEL_CORE_PATCH_REVISION: ""
RUST_VERSION: 1.79.0
FORC_VERSION: 0.64.0
FORC_PATCH_BRANCH: ""
Expand Down Expand Up @@ -99,6 +100,42 @@ jobs:
echo "Comparing minimum supported toolchain ($MIN_VERSION) with ci toolchain (RUST_VERSION)"
test "$MIN_VERSION" == "$RUST_VERSION"
# Fetch Fuel Core and upload as artifact, useful when we build the core from a
# revision so that we can repeat flaky tests without rebuilding the core.
fetch-fuel-core:
runs-on: ubuntu-latest
steps:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
targets: wasm32-unknown-unknown

# selecting a toolchain either by action or manual `rustup` calls should happen
# before the cache plugin, as it uses the current rustc version as its cache key
- uses: Swatinem/rust-cache@v2.7.3
continue-on-error: true
with:
key: "fuel-core-build"
- name: Install Fuel Core
run: |
if [[ -n $FUEL_CORE_PATCH_BRANCH ]]; then
cargo install --locked fuel-core-bin --git https://github.com/FuelLabs/fuel-core --branch "$FUEL_CORE_PATCH_BRANCH" --root fuel-core-install
elif [[ -n $FUEL_CORE_PATCH_REVISION ]]; then
cargo install --locked fuel-core-bin --git https://github.com/FuelLabs/fuel-core --rev "$FUEL_CORE_PATCH_REVISION" --root fuel-core-install
else
curl -sSLf https://github.com/FuelLabs/fuel-core/releases/download/v${{ env.FUEL_CORE_VERSION }}/fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu.tar.gz -L -o fuel-core.tar.gz
tar -xvf fuel-core.tar.gz
chmod +x fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core
mkdir -p fuel-core-install/bin
mv fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core fuel-core-install/bin/fuel-core
fi
- uses: actions/upload-artifact@v4
with:
name: fuel-core
path: fuel-core-install/bin/fuel-core

# Ensure workspace is publishable
publish-crates-check:
runs-on: ubuntu-latest
Expand All @@ -123,6 +160,7 @@ jobs:
- verify-rust-version
- get-workspace-members
- publish-crates-check
- fetch-fuel-core
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -136,11 +174,11 @@ jobs:
args: --all-targets
download_sway_artifacts: sway-examples
- cargo_command: nextest
args: run --all-targets --features "default fuel-core-lib coin-cache" --workspace --cargo-quiet
args: run --all-targets --features "default fuel-core-lib coin-cache" --workspace --cargo-quiet --no-fail-fast
download_sway_artifacts: sway-examples
install_fuel_core: true
- cargo_command: nextest
args: run --all-targets --workspace --cargo-quiet
args: run --all-targets --workspace --cargo-quiet --no-fail-fast
download_sway_artifacts: sway-examples
install_fuel_core: true
- cargo_command: test
Expand Down Expand Up @@ -175,17 +213,16 @@ jobs:
with:
key: "${{ matrix.cargo_command }} ${{ matrix.args }} ${{ matrix.package }}"

- name: Download Fuel Core
if: ${{ matrix.install_fuel_core }}
uses: actions/download-artifact@v4
with:
name: fuel-core
- name: Install Fuel Core
if: ${{ matrix.install_fuel_core }}
run: |
if [[ -n $FUEL_CORE_PATCH_BRANCH ]]; then
cargo install --locked fuel-core-bin --git https://github.com/FuelLabs/fuel-core --branch "$FUEL_CORE_PATCH_BRANCH"
else
curl -sSLf https://github.com/FuelLabs/fuel-core/releases/download/v${{ env.FUEL_CORE_VERSION }}/fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu.tar.gz -L -o fuel-core.tar.gz
tar -xvf fuel-core.tar.gz
chmod +x fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core
mv fuel-core-${{ env.FUEL_CORE_VERSION }}-x86_64-unknown-linux-gnu/fuel-core /usr/local/bin/fuel-core
fi
chmod +x fuel-core
mv fuel-core /usr/local/bin/fuel-core
- name: Download sway example artifacts
if: ${{ matrix.download_sway_artifacts }}
Expand Down
26 changes: 13 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,23 @@ octocrab = { version = "0.39", default-features = false }
dotenv = { version = "0.15", default-features = false }

# Dependencies from the `fuel-core` repository:
fuel-core = { version = "0.36.0", default-features = false, features = [
fuel-core = { version = "0.37.0", default-features = false, features = [
"wasm-executor",
] }
fuel-core-chain-config = { version = "0.36.0", default-features = false }
fuel-core-client = { version = "0.36.0", default-features = false }
fuel-core-poa = { version = "0.36.0", default-features = false }
fuel-core-services = { version = "0.36.0", default-features = false }
fuel-core-types = { version = "0.36.0", default-features = false }
fuel-core-chain-config = { version = "0.37.0", default-features = false }
fuel-core-client = { version = "0.37.0", default-features = false }
fuel-core-poa = { version = "0.37.0", default-features = false }
fuel-core-services = { version = "0.37.0", default-features = false }
fuel-core-types = { version = "0.37.0", default-features = false }

# Dependencies from the `fuel-vm` repository:
fuel-asm = { version = "0.57.0" }
fuel-crypto = { version = "0.57.0" }
fuel-merkle = { version = "0.57.0" }
fuel-storage = { version = "0.57.0" }
fuel-tx = { version = "0.57.0" }
fuel-types = { version = "0.57.0" }
fuel-vm = { version = "0.57.0" }
fuel-asm = { version = "0.58.0" }
fuel-crypto = { version = "0.58.0" }
fuel-merkle = { version = "0.58.0" }
fuel-storage = { version = "0.58.0" }
fuel-tx = { version = "0.58.0" }
fuel-types = { version = "0.58.0" }
fuel-vm = { version = "0.58.0" }

# Workspace projects
fuels = { version = "0.66.5", path = "./packages/fuels", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
- [Running scripts](./running-scripts.md)
- [Predicates](./predicates/index.md)
- [Signatures example](./predicates/send-spend-predicate.md)
- [Pre-uploading code](./preuploading-code.md)
- [Custom transactions](./custom-transactions/index.md)
- [Transaction builders](./custom-transactions/transaction-builders.md)
- [Custom contract and script calls](./custom-transactions/custom-calls.md)
Expand Down
42 changes: 42 additions & 0 deletions docs/src/preuploading-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Pre-uploading code

If you have a script or predicate that is larger than normal or which you plan
on calling often, you can pre-upload its code as a blob to the network and run a
loader script/predicate instead. The loader can be configured with the
script/predicate configurables, so you can change how the script/predicate is
configured on each run without having large transactions due to the code
duplication.

## Scripts

A high level pre-upload:

```rust,ignore
{{#include ../../e2e/tests/scripts.rs:preload_high_level}}
```

The upload of the blob is handled inside of the `convert_into_loader` method. If you
want more fine-grained control over it, you can create the script transaction
manually:

```rust,ignore
{{#include ../../e2e/tests/scripts.rs:preload_low_level}}
```

## Predicates

You can prepare a predicate for pre-uploading without doing network requests:

```rust,ignore
{{#include ../../e2e/tests/predicates.rs:preparing_the_predicate}}
```

Once you want to execute the predicate, you must beforehand upload the blob
containing its code:

```rust,ignore
{{#include ../../e2e/tests/predicates.rs:uploading_the_blob}}
```

By pre-uploading the predicate code, you allow for cheaper calls to the predicate
from subsequent callers.
4 changes: 4 additions & 0 deletions e2e/Forc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,23 @@ members = [
'sway/logs/script_needs_custom_decoder_logging',
'sway/logs/script_with_contract_logs',
'sway/predicates/basic_predicate',
'sway/predicates/predicate_blobs',
'sway/predicates/predicate_configurables',
'sway/predicates/predicate_witnesses',
'sway/predicates/signatures',
'sway/predicates/swap',
'sway/scripts/arguments',
'sway/scripts/basic_script',
'sway/scripts/empty',
'sway/scripts/require_from_contract',
'sway/scripts/reverting',
'sway/scripts/script_array',
'sway/scripts/script_asserts',
'sway/scripts/script_blobs',
'sway/scripts/script_configurables',
'sway/scripts/script_enum',
'sway/scripts/script_needs_custom_decoder',
'sway/scripts/script_proxy',
'sway/scripts/script_require',
'sway/scripts/script_struct',
'sway/scripts/transfer_script',
Expand Down
5 changes: 5 additions & 0 deletions e2e/sway/predicates/predicate_blobs/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "predicate_blobs"
9 changes: 9 additions & 0 deletions e2e/sway/predicates/predicate_blobs/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
predicate;

configurable {
SECRET_NUMBER: u64 = 9000,
}

fn main(arg1: u8, arg2: u8) -> bool {
arg1 == 1 && arg2 == 19 && SECRET_NUMBER == 10001
}
6 changes: 5 additions & 1 deletion e2e/sway/predicates/predicate_configurables/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ struct StructWithGeneric<D> {
configurable {
BOOL: bool = true,
U8: u8 = 8,
TUPLE: (u8, bool) = (8, true),
ARRAY: [u32; 3] = [253, 254, 255],
STRUCT: StructWithGeneric<u8> = StructWithGeneric {
field_1: 8,
field_2: 16,
Expand All @@ -41,9 +43,11 @@ configurable {
fn main(
switch: bool,
u_8: u8,
some_tuple: (u8, bool),
some_array: [u32; 3],
some_struct: StructWithGeneric<u8>,
some_enum: EnumWithGeneric<bool>,
) -> bool {
switch == BOOL && u_8 == U8 && some_struct == STRUCT && some_enum == ENUM
switch == BOOL && u_8 == U8 && some_tuple.0 == TUPLE.0 && some_tuple.1 == TUPLE.1 && some_array[0] == ARRAY[0] && some_array[1] == ARRAY[1] && some_array[2] == ARRAY[2] && some_struct == STRUCT && some_enum == ENUM
}
// ANCHOR_END: predicate_configurables
7 changes: 7 additions & 0 deletions e2e/sway/scripts/empty/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "empty"

[dependencies]
3 changes: 3 additions & 0 deletions e2e/sway/scripts/empty/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
script;

fn main() {}
5 changes: 5 additions & 0 deletions e2e/sway/scripts/script_blobs/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "script_blobs"
36 changes: 36 additions & 0 deletions e2e/sway/scripts/script_blobs/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
script;

configurable {
SECRET_NUMBER: u64 = 9000,
}

enum MyEnum {
A: u64,
B: u8,
C: (),
}

struct MyStruct {
field_a: MyEnum,
field_b: b256,
}

fn main(arg1: MyStruct) -> u64 {
assert_eq(SECRET_NUMBER, 10001);

match arg1.field_a {
MyEnum::B(value) => {
assert_eq(value, 99);
}
_ => {
assert(false)
}
}

assert_eq(
arg1.field_b,
0x1111111111111111111111111111111111111111111111111111111111111111,
);

return SECRET_NUMBER;
}
5 changes: 5 additions & 0 deletions e2e/sway/scripts/script_proxy/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "script_proxy"
24 changes: 24 additions & 0 deletions e2e/sway/scripts/script_proxy/src/main.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
script;

abi Proxy {
#[storage(write)]
fn set_target_contract(id: ContractId);

// methods of the `huge_contract` in our e2e sway contracts
#[storage(read)]
fn something() -> u64;

#[storage(read)]
fn write_some_u64(some: u64);

#[storage(read)]
fn read_some_u64() -> u64;
}

fn main(proxy_contract_id: ContractId) -> bool {
let proxy_instance = abi(Proxy, proxy_contract_id.into());
let _ = proxy_instance.something();
proxy_instance.write_some_u64(10001);
let read_u_64 = proxy_instance.read_some_u64();
return read_u_64 == 10001;
}
3 changes: 3 additions & 0 deletions e2e/tests/configurables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ async fn script_default_configurables() -> Result<()> {
)
);

let mut script_instance = script_instance;
script_instance.convert_into_loader().await?;

let response = script_instance.main().call().await?;

let expected_value = (
Expand Down
6 changes: 6 additions & 0 deletions e2e/tests/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::time::Duration;

use fuel_tx::{
consensus_parameters::{ConsensusParametersV1, FeeParametersV1},
ConsensusParameters, FeeParameters,
Expand Down Expand Up @@ -1476,6 +1478,7 @@ async fn test_contract_submit_and_response() -> Result<()> {
let contract_methods = contract_instance.methods();

let submitted_tx = contract_methods.get(1, 2).submit().await?;
tokio::time::sleep(Duration::from_millis(500)).await;
let value = submitted_tx.response().await?.value;

assert_eq!(value, 3);
Expand All @@ -1489,6 +1492,7 @@ async fn test_contract_submit_and_response() -> Result<()> {
.add_call(call_handler_2);

let handle = multi_call_handler.submit().await?;
tokio::time::sleep(Duration::from_millis(500)).await;
let (val_1, val_2): (u64, u64) = handle.response().await?.value;

assert_eq!(val_1, 7);
Expand Down Expand Up @@ -1683,6 +1687,8 @@ async fn contract_custom_call_no_signatures_strategy() -> Result<()> {
// ANCHOR_END: tb_no_signatures_strategy

let tx_id = provider.send_transaction(tx).await?;
tokio::time::sleep(Duration::from_millis(500)).await;

let tx_status = provider.tx_status(&tx_id).await?;

let response = call_handler.get_response_from(tx_status)?;
Expand Down
Loading

0 comments on commit 2a67e3b

Please sign in to comment.