Bytecode Library

The Bytecode Library allows for on-chain verification and computation of bytecode roots for contracts and predicates.

A bytecode root for a contract and predicate is the Merkle root of the binary Merkle tree with each leaf being 16KiB of instructions. This library will compute any contract's or predicate's bytecode root/address allowing for the verification of deployed contracts and generation of predicate addresses on-chain.

For implementation details on the Bytecode Library please see the Sway Libs Docs.

Importing the Bytecode Library

In order to use the Bytecode Library, Sway Libs must be added to the Forc.toml file and then imported into your Sway project. To add Sway Libs as a dependency to the Forc.toml file in your project please see the Getting Started.

To import the Bytecode Library to your Sway Smart Contract, add the following to your Sway file:

use sway_libs::bytecode::*;

Using the Bytecode Library In Sway

Once imported, using the Bytecode Library is as simple as calling the desired function. Here is a list of function definitions that you may use.

  • compute_bytecode_root()
  • compute_bytecode_root_with_configurables()
  • compute_predicate_address()
  • compute_predicate_address_with_configurables()
  • predicate_address_from_root()
  • swap_configurables()
  • verify_contract_bytecode()
  • verify_contract_bytecode_with_configurables()
  • verify_predicate_address()
  • verify_predicate_address_with_configurables()

Known Issues

Please note that if you are passing the bytecode from the SDK and are including configurable values, the Vec<u8> bytecode provided must be copied to be mutable. The following can be added to make your bytecode mutable:

fn make_mutable(not_mutable_bytecode: Vec<u8>) {
    // Copy the bytecode to a newly allocated memory to avoid memory ownership error.
    let mut bytecode_slice = raw_slice::from_parts::<u8>(
        alloc_bytes(not_mutable_bytecode.len()),
        not_mutable_bytecode
            .len(),
    );
    not_mutable_bytecode
        .ptr()
        .copy_bytes_to(bytecode_slice.ptr(), not_mutable_bytecode.len());
    let mut bytecode_vec = Vec::from(bytecode_slice);
    // You may now use `bytecode_vec` in your computation and verification function calls
}

Basic Functionality

The examples below are intended for internal contract calls. If you are passing bytecode from the SDK, please follow the steps listed above in known issues to avoid the memory ownership error.

Swapping Configurables

Given some bytecode, you may swap the configurables of both Contracts and Predicates by calling the swap_configurables() function.

fn swap(my_bytecode: Vec<u8>, my_configurables: Vec<(u64, Vec<u8>)>) {
    let mut my_bytecode = my_bytecode;
    let resulting_bytecode: Vec<u8> = swap_configurables(my_bytecode, my_configurables);
}

Contracts

Computing the Bytecode Root

To compute a contract's bytecode root you may call the compute_bytecode_root() or compute_bytecode_root_with_configurables() functions.

fn compute_bytecode(my_bytecode: Vec<u8>) {
    let root: b256 = compute_bytecode_root(my_bytecode);
}

fn compute_bytecode_configurables(my_bytecode: Vec<u8>, my_configurables: Vec<(u64, Vec<u8>)>) {
    let mut my_bytecode = my_bytecode;
    let root: b256 = compute_bytecode_root_with_configurables(my_bytecode, my_configurables);
}

Verifying a Contract's Bytecode Root

To verify a contract's bytecode root you may call verify_bytecode_root() or verify_contract_bytecode_with_configurables() functions.

fn verify_contract(my_contract: ContractId, my_bytecode: Vec<u8>) {
    verify_contract_bytecode(my_contract, my_bytecode);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

fn verify_contract_configurables(
    my_contract: ContractId,
    my_bytecode: Vec<u8>,
    my_configurables: Vec<(u64, Vec<u8>)>,
) {
    let mut my_bytecode = my_bytecode;
    verify_contract_bytecode_with_configurables(my_contract, my_bytecode, my_configurables);
    // By reaching this line the contract has been verified to match the bytecode provided.
}

Predicates

Computing the Address from Bytecode

To compute a predicates's address you may call the compute_predicate_address() or compute_predicate_address_with_configurables() functions.

fn compute_predicate(my_bytecode: Vec<u8>) {
    let address: Address = compute_predicate_address(my_bytecode);
}

fn compute_predicate_configurables(my_bytecode: Vec<u8>, my_configurables: Vec<(u64, Vec<u8>)>) {
    let mut my_bytecode = my_bytecode;
    let address: Address = compute_predicate_address_with_configurables(my_bytecode, my_configurables);
}

Computing the Address from a Root

If you have the root of a predicate, you may compute it's corresponding predicate address by calling the predicate_address_from_root() function.

fn predicate_address(my_root: b256) {
    let address: Address = predicate_address_from_root(my_root);
}

Verifying the Address

To verify a predicates's address you may call verify_predicate_address() or verify_predicate_address_with_configurables() functions.

fn verify_predicate(my_predicate: Address, my_bytecode: Vec<u8>) {
    verify_predicate_address(my_predicate, my_bytecode);
    // By reaching this line the predicate bytecode matches the address provided.
}

fn verify_predicate_configurables(
    my_predicate: Address,
    my_bytecode: Vec<u8>,
    my_configurables: Vec<(u64, Vec<u8>)>,
) {
    let mut my_bytecode = my_bytecode;
    verify_predicate_address_with_configurables(my_predicate, my_bytecode, my_configurables);
    // By reaching this line the predicate bytecode matches the address provided.
}