diff --git a/fs/Dockerfile b/fs/Dockerfile index 06f39b6..fd09582 100644 --- a/fs/Dockerfile +++ b/fs/Dockerfile @@ -9,12 +9,12 @@ RUN apt-get update && \ bash=5.1-6ubuntu1.1 \ psmisc=23.4-2build3 \ bc=1.07.1-3build1 \ - curl=7.81.0-1ubuntu1.18 \ + curl=7.81.0-1ubuntu1.20 \ device-tree-compiler=1.6.1-1 \ jq=1.6-2.1ubuntu3 \ lua5.4=5.4.4-1 \ lua-socket=3.0~rc1+git+ac3201d-6 \ - xxd=2:8.2.3995-1ubuntu2.19 \ + xxd=2:8.2.3995-1ubuntu2.21 \ file=1:5.41-3ubuntu0.1 \ stress-ng=0.13.12-2ubuntu1 \ /tmp/${TOOLS_DEB} \ diff --git a/rollup-http/rollup-http-client/src/client.rs b/rollup-http/rollup-http-client/src/client.rs index 3fb2520..20b2e81 100644 --- a/rollup-http/rollup-http-client/src/client.rs +++ b/rollup-http/rollup-http-client/src/client.rs @@ -16,7 +16,7 @@ use crate::rollup::{ AdvanceRequest, Exception, GIORequest, IndexResponse, InspectRequest, Notice, Report, - RollupRequest, RollupResponse, Voucher, + RollupRequest, RollupResponse, Voucher, DelegateCallVoucher, }; use hyper::Response; use serde::{Deserialize, Serialize}; @@ -59,6 +59,34 @@ pub async fn send_voucher(rollup_http_server_addr: &str, voucher: Voucher) { } } +pub async fn send_delegate_call_voucher(rollup_http_server_addr: &str, delegate_call_voucher: DelegateCallVoucher) { + log::debug!("sending delegate call voucher request to {}", rollup_http_server_addr); + let client = hyper::Client::new(); + let req = hyper::Request::builder() + .method(hyper::Method::POST) + .header(hyper::header::CONTENT_TYPE, "application/json") + .uri(rollup_http_server_addr.to_string() + "/delegate-call-voucher") + .body(hyper::Body::from(serde_json::to_string(&delegate_call_voucher).unwrap())) + .expect("delegate call voucher request"); + match client.request(req).await { + Ok(res) => { + let id_response = serde_json::from_slice::( + &hyper::body::to_bytes(res) + .await + .expect("error in voucher in response handling") + .to_vec(), + ); + log::debug!("voucher generated: {:?}", &id_response); + } + Err(e) => { + log::error!( + "failed to send delegate call voucher request to rollup http server: {}", + e + ); + } + } +} + pub async fn send_notice(rollup_http_server_addr: &str, notice: Notice) { log::debug!("sending notice request to {}", rollup_http_server_addr); let client = hyper::Client::new(); diff --git a/rollup-http/rollup-http-client/src/rollup.rs b/rollup-http/rollup-http-client/src/rollup.rs index 3a6939b..95e625c 100644 --- a/rollup-http/rollup-http-client/src/rollup.rs +++ b/rollup-http/rollup-http-client/src/rollup.rs @@ -57,6 +57,12 @@ pub struct Voucher { pub payload: String, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DelegateCallVoucher { + pub destination: String, + pub payload: String, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Report { pub payload: String, diff --git a/rollup-http/rollup-http-server/src/http_service.rs b/rollup-http/rollup-http-server/src/http_service.rs index e846dca..dc70f0d 100644 --- a/rollup-http/rollup-http-server/src/http_service.rs +++ b/rollup-http/rollup-http-server/src/http_service.rs @@ -16,7 +16,9 @@ use std::sync::Arc; -use actix_web::{middleware::Logger, web::Data, App, HttpResponse, HttpServer, http::header::CONTENT_TYPE}; +use actix_web::{ + http::header::CONTENT_TYPE, middleware::Logger, web::Data, App, HttpResponse, HttpServer, +}; use actix_web_validator::{Json, JsonConfig}; use async_mutex::Mutex; use serde::{Deserialize, Serialize}; @@ -26,8 +28,8 @@ use tokio::sync::Notify; use crate::config::Config; use crate::rollup::{self, GIORequest, RollupFd}; use crate::rollup::{ - AdvanceRequest, Exception, FinishRequest, InspectRequest, Notice, Report, RollupRequest, - Voucher, + AdvanceRequest, DelegateCallVoucher, Exception, FinishRequest, InspectRequest, Notice, Report, + RollupRequest, Voucher, }; #[derive(Debug, Serialize, Deserialize)] @@ -54,6 +56,7 @@ pub fn create_server( .app_data(json_cfg) .wrap(Logger::default()) .service(voucher) + .service(delegate_call_voucher) .service(notice) .service(report) .service(gio) @@ -116,6 +119,54 @@ async fn voucher(mut voucher: Json, data: Data>) -> Http }; } +/// Process voucher request from DApp, write voucher to rollup device +#[actix_web::post("/delegate-call-voucher")] +async fn delegate_call_voucher( + mut delegate_call_voucher: Json, + data: Data>, +) -> HttpResponse { + log::debug!("received voucher request"); + // Check if address is valid + if delegate_call_voucher.destination.len() + != (rollup::CARTESI_ROLLUP_ADDRESS_SIZE * 2 + 2) as usize + || (!delegate_call_voucher.destination.starts_with("0x")) + { + log::error!( + "address not valid: '{}' len: {}", + delegate_call_voucher.destination, + delegate_call_voucher.destination.len() + ); + return HttpResponse::BadRequest() + .append_header((CONTENT_TYPE, "text/plain")) + .body("address not valid"); + } + let context = data.lock().await; + // Write voucher to linux rollup device + return match rollup::rollup_write_delegate_call_voucher( + &*context.rollup_fd.lock().await, + &mut delegate_call_voucher.0, + ) { + Ok(voucher_index) => { + log::debug!( + "delegate call voucher successfully inserted {:#?}", + delegate_call_voucher + ); + HttpResponse::Created().json(IndexResponse { + index: voucher_index, + }) + } + Err(e) => { + log::error!( + "unable to insert voucher, error details: '{}'", + e.to_string() + ); + HttpResponse::BadRequest() + .append_header((CONTENT_TYPE, "text/plain")) + .body(format!("unable to insert voucher, error details: '{}'", e)) + } + }; +} + /// Process notice request from DApp, write notice to rollup device #[actix_web::post("/notice")] async fn notice(mut notice: Json, data: Data>) -> HttpResponse { diff --git a/rollup-http/rollup-http-server/src/rollup/mod.rs b/rollup-http/rollup-http-server/src/rollup/mod.rs index c8e0d36..c2061bb 100644 --- a/rollup-http/rollup-http-server/src/rollup/mod.rs +++ b/rollup-http/rollup-http-server/src/rollup/mod.rs @@ -245,6 +245,13 @@ pub struct Voucher { pub payload: String, } +#[derive(Debug, Clone, Serialize, Deserialize, Validate)] +pub struct DelegateCallVoucher { + #[validate(regex(path = "*ETH_ADDR_REGEXP"))] + pub destination: String, + pub payload: String, +} + #[derive(Debug, Clone, Serialize, Deserialize, Validate)] pub struct Report { pub payload: String, @@ -455,7 +462,7 @@ pub fn rollup_write_voucher( if res != 0 { return Err(Box::new(RollupError::new(&format!( - "IOCTL_ROLLUP_WRITE_VOUCHER returned error {}", + "IOCTL_ROLLUP_WRITE_OUTPUT returned error {}", res )))); } else { @@ -465,6 +472,46 @@ pub fn rollup_write_voucher( Ok(voucher_index as u64) } +pub fn rollup_write_delegate_call_voucher( + fd: &RollupFd, + delegate_call_voucher: &mut DelegateCallVoucher, +) -> Result> { + print_delegate_call_voucher(delegate_call_voucher); + + let mut binary_payload = match hex::decode(&delegate_call_voucher.payload[2..]) { + Ok(payload) => payload, + Err(_err) => { + return Err(Box::new(RollupError::new(&format!( + "Error decoding voucher payload, it must be in Ethereum hex binary format" + )))); + } + }; + let address = cmt_abi_address_t::from_hex(&delegate_call_voucher.destination[2..])?; + let payload = cmt_abi_bytes_t { + data: binary_payload.as_mut_ptr() as *mut c_void, + length: binary_payload.len(), + }; + + let mut voucher_index: std::os::raw::c_ulong = 0; + let res = unsafe { + cmt_rollup_emit_delegate_call_voucher(fd.0, &address, &payload, &mut voucher_index) + }; + + if res != 0 { + return Err(Box::new(RollupError::new(&format!( + "IOCTL_ROLLUP_WRITE_OUTPUT returned error {}", + res + )))); + } else { + log::debug!( + "delegate call voucher with id {} successfully written!", + voucher_index + ); + } + + Ok(voucher_index as u64) +} + pub fn rollup_write_report( fd: &RollupFd, report: &Report, @@ -714,6 +761,21 @@ pub fn print_voucher(voucher: &Voucher) { log::debug!("{}", &voucher_request_printout); } +pub fn print_delegate_call_voucher(delegate_call_voucher: &DelegateCallVoucher) { + let mut voucher_request_printout = String::new(); + voucher_request_printout.push_str("voucher: {{ destination: "); + format_address_printout( + &delegate_call_voucher.destination, + &mut voucher_request_printout, + ); + voucher_request_printout.push_str(&format!( + " length: {} payload: {} }}", + delegate_call_voucher.payload.len(), + delegate_call_voucher.payload + )); + log::debug!("{}", &voucher_request_printout); +} + pub fn print_report(report: &Report) { log::debug!( "report: {{ length: {} payload: {}}}", diff --git a/rollup-http/rollup-http-server/tests/rollup-http-server-tests.rs b/rollup-http/rollup-http-server/tests/rollup-http-server-tests.rs index 13fe149..695c5c0 100644 --- a/rollup-http/rollup-http-server/tests/rollup-http-server-tests.rs +++ b/rollup-http/rollup-http-server/tests/rollup-http-server-tests.rs @@ -21,7 +21,8 @@ use actix_server::ServerHandle; use async_mutex::Mutex; use rand::Rng; use rollup_http_client::rollup::{ - Exception, GIORequest, Notice, Report, RollupRequest, RollupResponse, Voucher, + DelegateCallVoucher, Exception, GIORequest, Notice, Report, RollupRequest, RollupResponse, + Voucher, }; use rollup_http_server::config::Config; use rollup_http_server::rollup::RollupFd; @@ -283,6 +284,69 @@ async fn test_write_voucher( Ok(()) } +fn check_delegate_call_voucher_or_fail( + original_delegate_call_voucher: DelegateCallVoucher, + output_filename: &str, +) { + // we try to decode the produced voucher with a third-party lib to see if it matches + // the expected values + let data = std::fs::read(output_filename).expect("error reading voucher file"); + let decoded_voucher = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Bytes], + &data[4..], // skip the first 4 bytes that are the function signature + ) + .ok() + .unwrap(); + + assert_eq!( + "0x".to_string() + &decoded_voucher[0].to_string(), + original_delegate_call_voucher.destination, + ); + assert_eq!( + "0x".to_string() + &decoded_voucher[1].to_string(), + original_delegate_call_voucher.payload, + ); +} + +#[rstest] +#[tokio::test] +async fn test_write_delegate_call_voucher( + context_future: impl Future, +) -> Result<(), Box> { + let context = context_future.await; + println!("Writing delegate call voucher"); + let test_delegate_call_voucher_01 = DelegateCallVoucher { + destination: "0x1111111111111111111111111111111111111111".to_string(), + payload: "0x".to_string() + &hex::encode("delegate call voucher test payload 01"), + }; + let test_delegate_call_voucher_02 = DelegateCallVoucher { + destination: "0x2222222222222222222222222222222222222222".to_string(), + payload: "0x".to_string() + &hex::encode("delegate call voucher test payload 02"), + }; + rollup_http_client::client::send_delegate_call_voucher( + &context.address, + test_delegate_call_voucher_01.clone(), + ) + .await; + + check_delegate_call_voucher_or_fail(test_delegate_call_voucher_01, "none.output-0.bin"); + std::fs::remove_file("none.output-0.bin")?; + + println!("Writing second voucher!"); + + rollup_http_client::client::send_delegate_call_voucher( + &context.address, + test_delegate_call_voucher_02.clone(), + ) + .await; + context.server_handle.stop(true).await; + + check_delegate_call_voucher_or_fail(test_delegate_call_voucher_02, "none.output-1.bin"); + std::fs::remove_file("none.output-1.bin")?; + + Ok(()) +} + #[rstest] #[tokio::test] async fn test_write_notice( diff --git a/sys-utils/libcmt/include/libcmt/rollup.h b/sys-utils/libcmt/include/libcmt/rollup.h index d50e726..f35ef17 100644 --- a/sys-utils/libcmt/include/libcmt/rollup.h +++ b/sys-utils/libcmt/include/libcmt/rollup.h @@ -111,6 +111,22 @@ void cmt_rollup_fini(cmt_rollup_t *me); * |< 0| failure with a -errno value | */ int cmt_rollup_emit_voucher(cmt_rollup_t *me, const cmt_abi_address_t *address, const cmt_abi_u256_t *value, const cmt_abi_bytes_t *data, uint64_t *index); +/** Emit a delegate call voucher + * + * Equivalent to the `DelegateCallVoucher(address,bytes)` solidity call. + * + * @param [in,out] me initialized @ref cmt_rollup_t instance + * @param [in] address destination data + * @param [in] data message contents + * @param [out] index index of emitted voucher, if successful + * + * @return + * | | | + * |--:|-----------------------------| + * | 0| success | + * |< 0| failure with a -errno value | */ +int cmt_rollup_emit_delegate_call_voucher(cmt_rollup_t *me, const cmt_abi_address_t *address, const cmt_abi_bytes_t *data, uint64_t *index); + /** Emit a notice * * @param [in,out] me initialized cmt_rollup_t instance diff --git a/sys-utils/libcmt/src/rollup.c b/sys-utils/libcmt/src/rollup.c index 0cc3c34..84b0973 100644 --- a/sys-utils/libcmt/src/rollup.c +++ b/sys-utils/libcmt/src/rollup.c @@ -25,6 +25,9 @@ // Voucher(address,uint256,bytes) #define VOUCHER CMT_ABI_FUNSEL(0x23, 0x7a, 0x81, 0x6f) +// DelegateCallVoucher(address,bytes) +#define DELEGATE_CALL_VOUCHER CMT_ABI_FUNSEL(0x10, 0x32, 0x1e, 0x8b) + // Notice(bytes) #define NOTICE CMT_ABI_FUNSEL(0xc2, 0x58, 0xd6, 0xe5) @@ -117,6 +120,56 @@ int cmt_rollup_emit_voucher(cmt_rollup_t *me, const cmt_abi_address_t *address, return 0; } +int cmt_rollup_emit_delegate_call_voucher(cmt_rollup_t *me, + const cmt_abi_address_t *address, const cmt_abi_bytes_t *payload, uint64_t *index) { + if (!me) { + return -EINVAL; + } + if (!payload || (!payload->data && payload->length)) { + return -EINVAL; + } + + cmt_buf_t tx[1] = {cmt_io_get_tx(me->io)}; + cmt_buf_t wr[1] = {*tx}; + cmt_buf_t of[1]; + cmt_buf_t frame[1]; + + // clang-format off + if (DBG(cmt_abi_put_funsel(wr, DELEGATE_CALL_VOUCHER)) + || DBG(cmt_abi_mark_frame(wr, frame)) + || DBG(cmt_abi_put_address(wr, address)) + || DBG(cmt_abi_put_bytes_s(wr, of)) + || DBG(cmt_abi_put_bytes_d(wr, of, frame, payload))) { + return -ENOBUFS; + } + // clang-format on + + size_t used_space = wr->begin - tx->begin; + struct cmt_io_yield req[1] = {{ + .dev = HTIF_DEVICE_YIELD, + .cmd = HTIF_YIELD_CMD_AUTOMATIC, + .reason = HTIF_YIELD_AUTOMATIC_REASON_TX_OUTPUT, + .data = used_space, + }}; + int rc = DBG(cmt_io_yield(me->io, req)); + if (rc) { + return rc; + } + + uint64_t count = cmt_merkle_get_leaf_count(me->merkle); + + rc = cmt_merkle_push_back_data(me->merkle, used_space, tx->begin); + if (rc) { + return rc; + } + + if (index) { + *index = count; + } + + return 0; +} + int cmt_rollup_emit_notice(cmt_rollup_t *me, const cmt_abi_bytes_t *payload, uint64_t *index) { if (!me) { return -EINVAL; diff --git a/sys-utils/libcmt/tests/create-data.sh b/sys-utils/libcmt/tests/create-data.sh index d98096c..6f0290d 100755 --- a/sys-utils/libcmt/tests/create-data.sh +++ b/sys-utils/libcmt/tests/create-data.sh @@ -45,5 +45,11 @@ echo "uint8_t valid_gio_reply_0[] = {" echo -en "gio-reply-0" | xxd -i echo "};" +echo "uint8_t valid_delegate_call_voucher_0[] = {" +cast calldata "DelegateCallVoucher(address,bytes)" \ + 0x0000000000000000000000000000000000000001 \ + 0x`echo -en "delegate-call-voucher-0" | xxd -p -c0` | xxd -r -p | xxd -i +echo "};" + echo "#endif /* DATA_H */" diff --git a/sys-utils/libcmt/tests/data.h b/sys-utils/libcmt/tests/data.h index eb7d44b..ca68f31 100644 --- a/sys-utils/libcmt/tests/data.h +++ b/sys-utils/libcmt/tests/data.h @@ -69,4 +69,17 @@ uint8_t valid_notice_0[] = { uint8_t valid_gio_reply_0[] = { 0x67, 0x69, 0x6f, 0x2d, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x2d, 0x30 }; +uint8_t valid_delegate_call_voucher_0[] = { + 0x10, 0x32, 0x1e, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x17, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2d, 0x63, 0x61, 0x6c, 0x6c, 0x2d, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, + 0x72, 0x2d, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; #endif /* DATA_H */ diff --git a/sys-utils/libcmt/tests/rollup.c b/sys-utils/libcmt/tests/rollup.c index ec34b68..bc81da8 100644 --- a/sys-utils/libcmt/tests/rollup.c +++ b/sys-utils/libcmt/tests/rollup.c @@ -205,6 +205,14 @@ void test_rollup_outputs_reports_and_exceptions(void) { assert(cmt_rollup_emit_exception(&rollup, &(cmt_abi_bytes_t){strlen(exception_data), NULL}) == -EINVAL); assert(cmt_rollup_emit_exception(&rollup, &(cmt_abi_bytes_t){UINT32_MAX, exception_data}) == -ENOBUFS); + // delegate voucher + char delegate_call_voucher_data[] = "delegate-call-voucher-0"; + assert(cmt_rollup_emit_delegate_call_voucher(&rollup, &address, &(cmt_abi_bytes_t){strlen(delegate_call_voucher_data), delegate_call_voucher_data}, &index) == 0); + assert(cmt_util_read_whole_file("none.output-2.bin", sizeof buffer, buffer, &read_size) == 0); + assert(sizeof valid_delegate_call_voucher_0 == read_size); + assert(memcmp(valid_delegate_call_voucher_0, buffer, sizeof valid_delegate_call_voucher_0) == 0); + assert(index == 2); + cmt_rollup_fini(&rollup); printf("test_rollup_outputs_reports_and_exceptions passed!\n"); } diff --git a/sys-utils/rollup/rollup.cpp b/sys-utils/rollup/rollup.cpp index 79aad99..8e0fb7d 100644 --- a/sys-utils/rollup/rollup.cpp +++ b/sys-utils/rollup/rollup.cpp @@ -73,6 +73,16 @@ static void print_help(void) { {"index": } where field "index" is the index allocated for the voucher + delegate-call-voucher + emit a delegate call voucher read from stdin as a JSON object in the format + {"destination":
, "payload": } + where +
contains a 20-byte EVM address in hex, + contains arbitrary data in hex + if successful, prints to stdout a JSON object in the format + {"index": } + where field "index" is the index allocated for the voucher + notice emit a notice read from stdin as a JSON object in the format {"payload": } @@ -244,6 +254,34 @@ static int write_voucher(void) try { return 1; } +// Read input for delegate call voucher data, issue voucher, write result to output +static int write_delegate_call_voucher(void) try { + rollup r; + auto ji = nlohmann::json::parse(read_input()); + auto payload_bytes = unhex(ji["payload"].get()); + auto destination_bytes = unhex20(ji["destination"].get()); + uint64_t index = 0; + cmt_abi_address_t destination; + cmt_abi_u256_t value; + cmt_abi_bytes_t payload; + payload.data = reinterpret_cast(payload_bytes.data()); + payload.length = payload_bytes.size(); + + memcpy(destination.data, reinterpret_cast(destination_bytes.data()), destination_bytes.size()); + + int ret = cmt_rollup_emit_delegate_call_voucher(r, &destination, &payload, &index); + if (ret) + return ret; + + nlohmann::json j = {{"index", index}}; + std::cout << j.dump(2) << '\n'; + + return 0; +} catch (std::exception &x) { + std::cerr << x.what() << '\n'; + return 1; +} + // Read input for notice data, issue notice, write result to output static int write_notice(void) try { rollup r; @@ -426,6 +464,8 @@ int main(int argc, char *argv[]) { const char *command = argv[1]; if (strcmp(command, "voucher") == 0) { return write_voucher(); + } else if (strcmp(command, "delegate-call-voucher") == 0) { + return write_delegate_call_voucher(); } else if (strcmp(command, "notice") == 0) { return write_notice(); } else if (strcmp(command, "report") == 0) {