Skip to content

Environment

Creating a application environment is the first step for developing and testing your applications(dApps) on Cartesi Machine using this HLF. This guide will demonstrate how to set up an environment using CrabRolls, along with example code to get you started.

Setting Up an Application Environment

  1. Import Required Dependencies: Start by importing the necessary dependencies from CrabRolls and other standard libraries to implement your application.

    use crabrolls::prelude::*;
    use std::error::Error;
  2. Define the Application Structure: Create a structure to represent your application. For this example, we’ll use a simple MyApp structure.

    struct MyApp;
  3. Implement the Application Constructor: Implement a constructor for your application. This constructor will be used to create instances of your application and you can use it to initialize any required state or configurations.

    impl MyApp {
    fn new() -> Self {
    Self
    }
    }
  4. Implement the Application Trait: The core of your dApp is defined by implementing the Application trait. This trait requires you to define methods like advance and inspect, which dictate how your application will process and respond to inputs.

    impl Application for MyApp {
    async fn advance( // Implement the advance method
    &self, // Self refers to the instance of the application
    env: &impl Environment, // Environment is used to interact with the application
    metadata: Metadata, // Metadata contains information like sender address, block number, etc.
    payload: &[u8], // Payload is the data to be processed by the application
    deposit: Option<Deposit>, // Deposit is some when the application receives a deposit
    ) -> Result<FinishStatus, Box<dyn Error>> {
    println!(
    "Advance method called with payload: {:?}",
    String::from_utf8_lossy(payload)
    );
    Ok(FinishStatus::Accept)
    }
    async fn inspect( // Implement the inspect method
    &self, // Self refers to the instance of the application
    env: &impl Environment, // Environment is used to interact with the application
    payload: &[u8] // Payload is the data to be processed by the application
    ) -> Result<FinishStatus, Box<dyn Error>> {
    println!(
    "Inspect method called with payload: {:?}",
    String::from_utf8_lossy(payload)
    );
    Ok(FinishStatus::Accept)
    }
    }

    Both the advance and inspect methods are asynchronous and return a Result with a FinishStatus or an error response, FinishStatus can be Accept or Reject. This allows you to handle the application’s state changes and responses effectively and return the appropriate status based on the processing outcome. If any error occurs during processing and not handled, the input will be rejected. Rejecting an input will make the Cartesi Machine ignore any Notice and Voucher generated by the application for that input, Report will still be generated because they are used for debugging and monitoring.

    In the advance method, we handle the payload received by the application, and can send notices, reports, and vouchers based on the processing outcome.

    The inspect method allows you to review the state of your application. This can be useful for debugging and monitoring the application’s behavior, only reports can be generated in this method.

Sending Notices, Reports, and Vouchers

To send notices, reports, and vouchers, you can use the Environment trait provided by CrabRolls. This trait allows you to interact with the application and send the necessary data to the Cartesi Machine.

  • Send a Notice:

    What is a Notice? A notice is a verifiable data declaration that attests to off-chain events or conditions and is accompanied by proof. - Cartesi Docs.

    Use the send_notice method to send a notice to the Cartesi Machine. This method requires the payload to be sent, this payload can be any data that the application needs to process and be validated by a proof.

    let notice_payload = b"Hi, Cartesi! This is a notice payload from CrabRolls.";
    // Send a notice to the Cartesi Machine, wait for the response and handle any errors that occur
    env.send_notice(notice_payload).await?;
  • Send a Report:

    What is a Report? Reports are stateless logs, offering a means to record read-only information without changing the state. Primarily used for logging and diagnostic purposes, reports provide valuable insights into the operation and performance of a dApp. - Cartesi Docs.

    Use the send_report method to send a report to the Cartesi Machine. This method requires the payload to be sent, this payload can be any data that the application needs to debug or monitor the application’s behavior, performance, state, etc.

    let report_payload = b"Hi, Cartesi! This is a report payload from CrabRolls.";
    // Send a report to the Cartesi Machine, wait for the response and handle any errors that occur
    env.send_report(report_payload).await?;
  • Send a Voucher:

    What is a Voucher? Vouchers serve as a mechanism for facilitating on-chain actions initiated in the execution layer. - Cartesi Docs.

    Use the send_voucher method to send a voucher to the Cartesi Machine. This method requires the address and payload to be sent, this payload is the data ABI encoded that will be executed on-chain when the voucher is redeemed (can be manual executed after epoch length is reached), for example, a smart contract function call.

    use ethabi::{Token, Uint};
    let abi_json = r#"
    [
    {
    "name": "testFunction",
    "inputs": [
    {
    "internalType": "uint256",
    "name": "_value",
    "type": "uint256"
    }
    ],
    "outputs": [],
    "type": "function"
    }
    ]"#;
    let value = uint!(144);
    let params = vec![Token::Uint(uint!(value))];
    let voucher_payload = abi::encode::function_call(abi_json, "testFunction", params)?;
    let contract_address = address!("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
    // Send a voucher to the Cartesi Machine, wait for the response and handle any errors that occur
    // When the voucher is redeemed, the smart contract from address `contract_address` will
    // execute the function `testFunction` with the parameters `params`
    env.send_voucher(contract_address, voucher_payload).await?;

    You can see more about the Utils and Macros provided on Crabrolls to help you with the development of your dApp and the interactions.

Retrieving Metadata

To retrieve metadata from the application, you can use the Metadata struct provided by CrabRolls. This struct contains information like the input_index, sender, block_number, and timestamp of the application. Only on the advance method you can receive the Metadata struct.

use crabrolls::prelude::*;
impl Application for MyApp {
async fn advance(
&self,
env: &impl Environment,
metadata: Metadata,
payload: &[u8],
deposit: Option<Deposit>,
) -> Result<FinishStatus, Box<dyn Error>> {
// Extract all metadata information from advance input
println!("Input Index: {}", metadata.input_index);
println!("Sender address: {}", metadata.sender);
println!("Block Number: {}", metadata.block_number);
println!("Timestamp: {}", metadata.timestamp);
Ok(FinishStatus::Accept)
}
... // Rest of the implementation
}

Running the Application

To run the application, you can use the Supervisor struct provided by CrabRolls. This struct allows you to run your application and specify the options for the execution.

use crabrolls::prelude::*;
... //Your application implementation...
#[async_std::main] // Use the async_std::main attribute to run the async main function
async fn main() {
let app = MyApp::new(); // Create a new instance of your application
let options = RunOptions::default(); // Create a new instance of the RunOptions struct
if let Err(e) = Supervisor::run(app, options).await { // Run the application
eprintln!("Error: {}", e); // Handle any errors that occur during execution
}
}

The RunOptions struct allows you to specify the options for the execution of your application. You can set the rollup_url, address_book, and portal_config to customize the execution environment. The Supervisor struct will run your application using the specified options. For example:

use crabrolls::prelude::*;
... //Your application implementation...
#[async_std::main] // Use the async_std::main attribute to run the async main function
async fn main() {
let app = MyApp::new(); // Create a new instance of your application
let options = RunOptions::builder() // Create a new instance of the RunOptionsBuilder struct
.rollup_url("http://localhost:8080/host-runner") // Set the rollup URL to `cartesi run --no-backend` mode
.build(); // Create a new instance of the RunOptions struct with custom options
if let Err(e) = Supervisor::run(app, options).await { // Run the application
eprintln!("Error: {}", e); // Handle any errors that occur during execution
}
}

To use the Cartesi CLI to run the application, you can follow the steps below:

  1. Build the project:

    Terminal
    cartesi build
  2. Run the project:

    Terminal
    cartesi run

    The terminal will show the output of the application like:

    Terminal
    prompt-1 | Anvil running at http://localhost:8545
    prompt-1 | GraphQL running at http://localhost:8080/graphql
    prompt-1 | Inspect running at http://localhost:8080/inspect/
    prompt-1 | Explorer running at http://localhost:8080/explorer/
    prompt-1 | Bundler running at http://localhost:8080/bundler/rpc
    prompt-1 | Paymaster running at http://localhost:8080/paymaster/
    prompt-1 | Press Ctrl+C to stop the node
  3. You can use your application!

Now you have a basic understanding of how to set up an environment for developing and running your dApp using CrabRolls 🎉!