Skip to content

Release v2 development updates #246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: Staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/ltex.hiddenFalsePositives.en-US.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{"rule":"BASE_BASIS","sentence":"^\\QThis guide covers the Cartesi+Espresso integration and how to upgrade Cartesi application such that inputs can be submitted via Espresso instead of the regular base layer.\\E$"}
{"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QIntegrating Cartesi and Chronicle offers Cartesi applications access to onchain and offcahin data like, price feed without developers having to set up additional systems or intermediaries.\\E$"}
{"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QPrevado Id:\\E$"}
{"rule":"MORFOLOGIK_RULE_EN_US","sentence":"^\\QThe Devnet environment functions similarly to a mainnet.\\E$"}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: introduction
title: Introduction
---

The backend of a Cartesi dApp retrieves a new request as follows:
The backend of a Cartesi application as mentioned in the [overview section](../index.md#backend-apis) contains the applications logic and state, it interacts with the Cartesi rollups framework by retrieving request and submitting structured outputs. The application backend retrieves a new request as follows:

- Finish — Communicates that any previous processing has been completed and that the backend is ready to handle the subsequent request. This following request is returned as the call's response and can be of the following types:

Expand Down Expand Up @@ -120,6 +120,81 @@ while True:
</code></pre>
</TabItem>

<TabItem value="Rust" label="Rust" default>
<pre><code>

```rust
use json::{object, JsonValue};
use std::env;

pub async fn handle_advance(
_client: &hyper::Client<hyper::client::HttpConnector>,
_server_addr: &str,
request: JsonValue,
) -> Result<&'static str, Box<dyn std::error::Error>> {
println!("Received advance request data {}", &request);
let _payload = request["data"]["payload"]
.as_str()
.ok_or("Missing payload")?;
// TODO: add application logic here
Ok("accept")
}

pub async fn handle_inspect(
_client: &hyper::Client<hyper::client::HttpConnector>,
_server_addr: &str,
request: JsonValue,
) -> Result<&'static str, Box<dyn std::error::Error>> {
println!("Received inspect request data {}", &request);
let _payload = request["data"]["payload"]
.as_str()
.ok_or("Missing payload")?;
// TODO: add application logic here
Ok("accept")
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = hyper::Client::new();
let server_addr = env::var("ROLLUP_HTTP_SERVER_URL")?;

let mut status = "accept";
loop {
println!("Sending finish");
let response = object! {"status" => status.clone()};
let request = hyper::Request::builder()
.method(hyper::Method::POST)
.header(hyper::header::CONTENT_TYPE, "application/json")
.uri(format!("{}/finish", &server_addr))
.body(hyper::Body::from(response.dump()))?;
let response = client.request(request).await?;
println!("Received finish status {}", response.status());

if response.status() == hyper::StatusCode::ACCEPTED {
println!("No pending rollup request, trying again");
} else {
let body = hyper::body::to_bytes(response).await?;
let utf = std::str::from_utf8(&body)?;
let req = json::parse(utf)?;

let request_type = req["request_type"]
.as_str()
.ok_or("request_type is not a string")?;
status = match request_type {
"advance_state" => handle_advance(&client, &server_addr[..], req).await?,
"inspect_state" => handle_inspect(&client, &server_addr[..], req).await?,
&_ => {
eprintln!("Unknown request type");
"reject"
}
};
}
}
}
```

</code></pre>
</TabItem>
</Tabs>

An **Advance** request involves sending input data to the base layer via JSON-RPC so they can reach the dApp backend to change the application's state.
Expand All @@ -140,10 +215,12 @@ An **Inspect** request involves making an external HTTP API call to the rollups

You can make a simple inspect call from your frontend client to retrieve reports.

To perform an Inspect call, use an HTTP GET request to `<address of the node>/inspect/<request path>`. For example:
To perform an Inspect call, use an HTTP POST request to `<address of the node>/inspect/<application name or address>` with a body containing the request payload. For example:

```shell
curl http://localhost:8080/inspect/mypath
curl -X POST http://localhost:8080/inspect/0xb483897a2790a5D1a1C5413690bC5933f269b3A9 \
-H "Content-Type: application/json" \
-d '"test"'
```

Once the call's response is received, the payload is extracted from the response data, allowing the backend code to examine it and produce outputs as **reports**.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Crucially, the base layer conducts on-chain validation of these notices through

This validation process ensures the integrity and authenticity of the submitted notices, enabling the blockchain to verify and authenticate the declared off-chain events or conditions.

Let's see how a Cartesi dApp's **Advance** request sends an output to the rollup server as a notice:
Here are sample functions you can add to your application, then call to send a notice to the rollup server:

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Expand All @@ -23,25 +23,30 @@ import TabItem from '@theme/TabItem';
<pre><code>

```javascript
async function handle_advance(data) {
console.log("Received advance request data " + JSON.stringify(data));

const inputPayload = data["payload"];
import { stringToHex, hexToString } from "viem";

const emitNotice = async (inputPayload) => {
let hexPayload = stringToHex(inputPayload); // convert payload from string to hex
try {
await fetch(rollup_server + "/notice", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ payload: inputPayload }),
body: JSON.stringify({ payload: hexPayload }),
});
} catch (error) {
//Do something when there is an error
}
}

async function handle_advance(data) {
console.log("Received advance request data " + JSON.stringify(data));
const payload = hexToString(data.payload); // convert input from hex to string for processing
await emitNotice(payload);
return "accept";
}

```

</code></pre>
Expand All @@ -51,24 +56,16 @@ async function handle_advance(data) {
<pre><code>

```python

def handle_advance(data):
logger.info(f"Received advance request data {data}")

status = "accept"
try:
inputPayload = data["payload"]
## Send the input payload as a notice
response = requests.post(
rollup_server + "/notice", json={"payload": inputPayload}
)
logger.info(
f"Received notice status {response.status_code} body {response.content}"
)
except Exception as e:
# Emits report with error message here
return status

# Notice creation Process from a message string
def emit_notice(message):
notice_payload = {"payload": "0x" + message.encode("utf-8").hex()}
response = requests.post(rollup_server + "/notice", json=notice_payload)
if response.status_code == 200 or response.status_code == 201:
logger.info(f"Notice emitted successfully with data: {notice_payload}")
else:
logger.error(f"Failed to emit Notice with data: {notice_payload}. Status code: {response.status_code}")

emit_notice("hello world")
```

</code></pre>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,89 @@ The [`CartesiDApp`](../contracts/application.md) contract is crucial in validati

The result of the voucher execution is recorded on the base layer. This recording typically involves submitting claims by a consensus contract, ensuring the integrity and transparency of the executed on-chain action.

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<Tabs>
<TabItem value="Python" label="Python" default>
<pre><code>

```python
# Voucher creation process
import requests
import json
from os import environ
from eth_utils import function_signature_to_4byte_selector
from eth_abi import decode, encode

rollup_server = environ["ROLLUP_HTTP_SERVER_URL"]

def emit_voucher(function_signature, destination, types, values):
selector = function_signature_to_4byte_selector(function_signature)
encoded_params = encode(types, values)
payload = "0x" + (selector + encoded_params).hex()
response = requests.post(rollup_server + "/voucher", json={"payload": payload, "destination": destination, "value": '0x' + encode(["uint256"], [0]).hex()})
if response.status_code == 200 or response.status_code == 201:
logger.info(f"Voucher emitted successfully with data: {payload}")
else:
logger.error(f"Failed to emit Voucher with data: {payload}. Status code: {response.status_code}")


emit_voucher("mint(address)", "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a", ["address"], [data["metadata"]["msg_sender"]])
```

</code></pre>
</TabItem>

<TabItem value="Javascript" label="Javascript" default>
<pre><code>

```javascript
import { stringToHex, encodeFunctionData, erc20Abi } from "viem";

async function handle_advance(data) {
console.log("Received advance request data " + JSON.stringify(data));
const sender = data["metadata"]["msg_sender"];
const erc20Token = "0x784f0c076CC55EAD0a585a9A13e57c467c91Dc3a"; // Sample ERC20 token address

const call = encodeFunctionData({
abi: erc20Abi,
functionName: "transfer",
args: [sender, BigInt(10)],
});

let voucher = {
destination: erc20Token,
payload: call,
value: '0x',
};

await emitVoucher(voucher);
return "accept";
}


const emitVoucher = async (voucher) => {
try {
await fetch(rollup_server + "/voucher", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(voucher),
});
} catch (error) {
//Do something when there is an error
}
};

```

</code></pre>
</TabItem>

</Tabs>

:::note create a voucher
[Refer to the documentation here](../../development/asset-handling.md) for asset handling and creating vouchers in your dApp.
:::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ resources:

Assets exist on the base layer, where they have actual meaning and value.

As with any execution layer solution, a Cartesi dApp that wants to manipulate assets needs a secure way of "teleporting" the assets from the base layer to the execution layer and then a way to "teleport" them back to the base layer.
As with any execution layer solution, a Cartesi Application that wants to manipulate assets needs a secure way of "teleporting" the assets from the base layer to the execution layer and when necessary, back to the base layer.

Currently, Cartesi Rollups support the following types of assets:

Expand All @@ -22,9 +22,9 @@ Currently, Cartesi Rollups support the following types of assets:

## Deposits

Portals enable the safe transfer of assets from the base layer to the execution layer. Users authorize portals to deduct assets from their accounts and initiate transfers to dApps.
Portals enable the safe transfer of assets from the base layer to the execution layer. Users authorize portals to deduct assets from their accounts and initiate transfers to the Application contract.

When an asset is deposited, it is on the base layer but gains a representation in the execution layer. The corresponding Portal contract sends an input via the `InputBox` contract describing the type of asset, amount, and some data the depositor might want the dApp to read. The off-chain machine will then interpret and validate the input payload.
When an asset is deposited, it is on the base layer but gains a representation in the execution layer. The corresponding Portal contract sends an input via the `InputBox` contract describing the type of asset, amount, and some data the depositor might want the application to read. The off-chain machine will then interpret and validate the input payload.

Deposit input payloads are always specified as packed ABI-encoded parameters, as detailed below.

Expand All @@ -42,26 +42,32 @@ Deposit input payloads are always specified as packed ABI-encoded parameters, as

## Withdrawing assets

Users can deposit assets to a Cartesi dApp, but only the dApp can initiate withdrawals. When a withdrawal request is made, it’s processed and interpreted off-chain by the Cartesi Machine running the dApp’s code. Subsequently, the Cartesi Machine creates a voucher containing the necessary instructions for withdrawal, which is executable when an epoch has settled.
Users can deposit assets to a Cartesi Application, but only the Application can initiate withdrawals. When a withdrawal request is made, it’s processed and interpreted off-chain by the Cartesi Machine running the application’s code. Subsequently, the Cartesi Machine creates a voucher containing the necessary instructions for withdrawal, which is executable when an epoch has settled.

Vouchers are crucial in allowing dApps in the execution layer to interact with contracts in the base layer through message calls. They are emitted by the off-chain machine and executed by any participant in the base layer. Each voucher includes a destination address and a payload, typically encoding a function call for Solidity contracts.
### Withdrawing Tokens

The dApp’s off-chain layer often requires knowledge of its address to facilitate on-chain interactions for withdrawals, for example: `transferFrom(sender, recipient, amount)`. In this case, the sender is the dApp itself.
Vouchers are crucial in allowing applications in the execution layer to interact with contracts in the base layer through message calls. They are emitted by the off-chain machine and executed by any participant in the base layer. Each voucher includes a destination address and a payload, typically encoding a function call for Solidity contracts.

Next, the off-chain machine uses the address of the application on the base layer to generate a voucher for execution at the [`executeVoucher()`](../api-reference/contracts/application.md/#executevoucher) function of the `CartesiDApp` contract. This address is known to the offchain machine because it is embedded in the metadata of every input sent to the application, though the developer will need to implement extra logic fetch this address from the metadata then properly store and retrieve it when needed in situations like generating the above Voucher.
The application’s off-chain layer often requires knowledge of its address to facilitate on-chain interactions for withdrawals, for example: `transferFrom(sender, recipient, amount)`. In this case, the sender is the application itself.

Next, the off-chain machine uses the address of the application on the base layer to generate a voucher for execution at the [`executeOutput()`](../api-reference/json-rpc/application.md/#executeoutput) function of the `Application` contract. This address is known to the offchain machine because it is embedded in the metadata of every input sent to the application, though the developer will need to implement extra logic fetch this address from the metadata then properly store and retrieve it when needed in situations like generating the above Voucher.

### Withdrawing Ether

To execute Ether withdrawal it is important to emit a voucher with the necessary details as regarding whom you intend to send the Ether to and also the amount to send, nevertheless since the Application contract Executes vouchers by making a [safeCall](https://github.com/cartesi/rollups-contracts/blob/cb52d00ededd2da9f8bf7757710301dccb7d536d/src/library/LibAddress.sol#L18C14-L18C22) to the destination, passing a value (Ether amount to send along with the call) and a payload (function signature to call), it's acceptable to leave the payload section empty if you do not intend to call any functions in the destination address while sending just the specified value of Ether to the destination address. If you intend to call a payable function and also send Ether along, you can add a function signature matching the payable function you intend to call to the payload field.

:::note epoch length
By default, Cartesi nodes close one epoch every 7200 blocks. You can [manually set the epoch length](./cli-commands.md/#run) to facilitate quicker asset-handling methods.
:::

Here are the function signatures used by vouchers to withdraw the different types of assets:

| Asset | Destination | Function signature |
| :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
| Ether | dApp contract | `withdrawEther(address,uint256)` [:page_facing_up:](../api-reference/contracts/application.md/#withdrawether) |
| ERC-20 | Token contract | `transfer(address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) |
| ERC-20 | Token contract | `transferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) |
| ERC-721 | Token contract | `safeTransferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) |
| ERC-721 | Token contract | `safeTransferFrom(address,address,uint256,bytes)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) |
| ERC-1155 | Token contract | `safeTransferFrom(address,address,uint256,uint256,data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) |
| ERC-1155 | Token contract | `safeBatchTransferFrom(address,address,uint256[],uint256[],data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) |
| Asset | Destination | Function signature |
| :------- | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
| Ether | Receiver's Address | Optional |
| ERC-20 | Token contract | `transfer(address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) |
| ERC-20 | Token contract | `transferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-20#methods) |
| ERC-721 | Token contract | `safeTransferFrom(address,address,uint256)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) |
| ERC-721 | Token contract | `safeTransferFrom(address,address,uint256,bytes)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-721#specification) |
| ERC-1155 | Token contract | `safeTransferFrom(address,address,uint256,uint256,data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) |
| ERC-1155 | Token contract | `safeBatchTransferFrom(address,address,uint256[],uint256[],data)` [:page_facing_up:](https://eips.ethereum.org/EIPS/eip-1155#specification) |
Loading