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
-
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; -
Define the Application Structure: Create a structure to represent your application. For this example, we’ll use a simple
MyApp
structure.struct MyApp; -
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}} -
Implement the Application Trait: The core of your dApp is defined by implementing the
Application
trait. This trait requires you to define methods likeadvance
andinspect
, 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 applicationenv: &impl Environment, // Environment is used to interact with the applicationmetadata: Metadata, // Metadata contains information like sender address, block number, etc.payload: &[u8], // Payload is the data to be processed by the applicationdeposit: 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 applicationenv: &impl Environment, // Environment is used to interact with the applicationpayload: &[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
andinspect
methods are asynchronous and return aResult
with aFinishStatus
or an error response,FinishStatus
can beAccept
orReject
. 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 thepayload
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 occurenv.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 thepayload
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 occurenv.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 theaddress
andpayload
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 functionasync 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 functionasync 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:
-
Build the project:
Terminal cartesi build -
Run the project:
Terminal cartesi runThe terminal will show the output of the application like:
Terminal prompt-1 | Anvil running at http://localhost:8545prompt-1 | GraphQL running at http://localhost:8080/graphqlprompt-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/rpcprompt-1 | Paymaster running at http://localhost:8080/paymaster/prompt-1 | Press Ctrl+C to stop the node -
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 🎉!