Exception
The /exception endpoint is used to register an exception when the dApp cannot proceed with request processing. This should be the last method called by the dApp backend while processing a request.
When an exception occurs during request processing, the dApp backend should:
- Call the
/exceptionendpoint with a payload describing the error - Not make any further API calls after registering the exception
- Exit the processing loop
The Rollup HTTP Server will:
- Skip the input with the reason
EXCEPTION - Forward the exception message
- Return status code 200
The exception payload should be a hex-encoded string starting with '0x' followed by pairs of hexadecimal numbers.
Let's see how a Cartesi dApp's backend handles exceptions:
- JavaScript
- Python
- Rust
async function handle_advance(data) {
console.log("Received advance request data " + JSON.stringify(data));
try {
// Process the request
// ...
} catch (error) {
// Register exception and exit
await fetch(rollup_server + "/exception", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
payload: "0x" + Buffer.from(error.message).toString("hex"),
}),
});
process.exit(1);
}
return "accept";
}
def handle_advance(data):
logger.info(f"Received advance request data {data}")
try:
# Process the request
# ...
except Exception as e:
# Register exception and exit
response = requests.post(
rollup_server + "/exception",
json={"payload": "0x" + e.message.encode("utf-8").hex()},
)
logger.info(
f"Received exception status {response.status_code} body {response.content}"
)
sys.exit(1)
return "accept"
fn hex_to_string(hex: &str) -> Result<String, Box<dyn std::error::Error>> {
let hexstr = hex.strip_prefix("0x").unwrap_or(hex);
let bytes = hex::decode(hexstr).map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
let s = String::from_utf8(bytes).map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
Ok(s)
}
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")?;
let payload_string = hex_to_string(payload);
match payload_string {
Ok(payload_extract) => {
// DO SOMETHING HERE!!
}
Err(e) => {
throw_execption(e.to_string()).await;
}
}
Ok("accept")
}
async fn throw_execption( payload: String) -> Option<bool> {
let hex_string = {
let s = payload.strip_prefix("0x").unwrap_or(payload.as_str());
hex::encode(s.as_bytes())
};
let server_addr = env::var("ROLLUP_HTTP_SERVER_URL").expect("ROLLUP_HTTP_SERVER_URL not set");
let client = hyper::Client::new();
let response = object! {
"payload" => format!("0x{}", hex_string),
};
let request = hyper::Request::builder()
.method(hyper::Method::POST)
.header(hyper::header::CONTENT_TYPE, "application/json")
.uri(format!("{}/exception", server_addr))
.body(hyper::Body::from(response.dump()))
.ok()?;
let response = client.request(request).await;
match response {
Ok(_) => {
println!("Exception generation successful");
return Some(true);
}
Err(e) => {
println!("Exception request failed {}", e);
None
}
}
}
Notes
- This endpoint should only be called when the dApp cannot proceed with request processing
- After calling this endpoint, the dApp should not make any further API calls
- The exception payload should be a hex-encoded string starting with '0x'
- An empty payload is represented by the string '0x'