Skip to content
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

feat(octez): implement execute_rollup_outbox_message #645

Merged
merged 1 commit into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions crates/jstzd/src/task/octez_rollup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use anyhow::{anyhow, Result};
use async_trait::async_trait;
use octez::r#async::{
directory::Directory,
endpoint::Endpoint,
rollup::{OctezRollupConfig, RollupDataDir},
};
use serde::Deserialize;
Expand All @@ -23,6 +24,12 @@ pub struct OctezRollup {
_data_dir: Arc<Directory>,
}

impl OctezRollup {
pub fn rpc_endpoint(&self) -> &Endpoint {
&self.config.rpc_endpoint
}
}

#[derive(Debug, Deserialize)]
struct HealthCheckResponse {
healthy: bool,
Expand Down
2 changes: 1 addition & 1 deletion crates/jstzd/tests/contract_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use tokio::io::AsyncWriteExt;

#[tokio::test(flavor = "multi_thread")]
async fn built_contracts() {
let (_node, client, _baker) = utils::setup().await;
let (_node, client, _baker) = utils::setup(None).await;
let tmp_dir = TempDir::new().unwrap();
generate_bootstrap_contract_files(&client, tmp_dir.path()).await;
let contract_names = ["exchanger", "jstz_native_bridge"];
Expand Down
2 changes: 1 addition & 1 deletion crates/jstzd/tests/octez_baker_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use utils::{get_block_level, setup};

#[tokio::test(flavor = "multi_thread")]
async fn test_baker() {
let (mut octez_node, _, mut baker) = setup().await;
let (mut octez_node, _, mut baker) = setup(None).await;
let node_endpoint = octez_node.rpc_endpoint();

let _ = baker.kill().await;
Expand Down
196 changes: 192 additions & 4 deletions crates/jstzd/tests/octez_client_test.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
use anyhow::Context;
use jstz_crypto::public_key_hash::PublicKeyHash;
use jstzd::task::Task;
use octez::r#async::{
client::{OctezClient, OctezClientConfigBuilder, Signature},
endpoint::Endpoint,
protocol::{
BootstrapContract, BootstrapSmartRollup, ProtocolParameter,
ProtocolParameterBuilder, SmartRollupPvmKind,
},
};
use serde::Deserialize;
use serde_json::Value;
use std::{
fs::{read_to_string, remove_file},
io::Write,
path::Path,
};
use tempfile::{NamedTempFile, TempDir};
use tezos_crypto_rs::hash::SmartRollupHash;
use tokio::io::AsyncWriteExt;
mod utils;
use std::path::PathBuf;
use utils::{
activate_alpha, create_client, get_head_block_hash, get_operation_kind, get_request,
import_activator, setup, spawn_octez_node, ACTIVATOR_SECRET_KEY,
import_activator, poll, setup, spawn_octez_node, spawn_rollup, ACTIVATOR_SECRET_KEY,
};

fn read_file(path: &Path) -> Value {
Expand Down Expand Up @@ -242,7 +250,7 @@ async fn add_address() {

#[tokio::test(flavor = "multi_thread")]
async fn call_contract() {
let (mut octez_node, octez_client, mut baker) = setup().await;
let (mut octez_node, octez_client, mut baker) = setup(None).await;
let bootstrap1: String = "bootstrap1".to_string();
let contract = "KT1F3MuqvT9Yz57TgCS3EkDcKNZe9HpiavUJ".to_string();
let before = octez_client.get_balance(&contract).await.unwrap();
Expand All @@ -266,7 +274,7 @@ async fn call_contract() {

#[tokio::test(flavor = "multi_thread")]
async fn originate_contract_and_wait_for() {
let (mut octez_node, octez_client, mut baker) = setup().await;
let (mut octez_node, octez_client, mut baker) = setup(None).await;
let head = get_head_block_hash(&octez_node.rpc_endpoint().to_string()).await;

let mut config_file = NamedTempFile::new().unwrap();
Expand Down Expand Up @@ -327,7 +335,7 @@ async fn originate_contract_and_wait_for() {

#[tokio::test(flavor = "multi_thread")]
async fn send_rollup_inbox_message() {
let (mut octez_node, octez_client, mut baker) = setup().await;
let (mut octez_node, octez_client, mut baker) = setup(None).await;

let (block, op) = octez_client
.send_rollup_inbox_message("bootstrap1", "0000", Some(0.1))
Expand All @@ -351,3 +359,183 @@ async fn send_rollup_inbox_message() {
let _ = baker.kill().await;
let _ = octez_node.kill().await;
}

#[derive(Deserialize)]
struct OutputProof {
pub commitment: String,
pub proof: String,
}

#[tokio::test(flavor = "multi_thread")]
async fn execute_rollup_outbox_message() {
let rollup_address = "sr1Uuiucg1wk5aovEY2dj1ZBsqjwxndrSaao";
// this is the destination contract where the outbox messages target
// this address is sealed in the rollup code
let contract_address = "KT1TFAweS9bMBetdDB3ndFicJWAEMb8MtSrK";
let installer_path = Path::new(std::env!("CARGO_MANIFEST_DIR")).join(format!(
"tests/resources/rollup/{rollup_address}/installer.hex"
));
let preimages_dir = Path::new(std::env!("CARGO_MANIFEST_DIR"))
.join(format!("tests/resources/rollup/{rollup_address}/preimages"));
let contract_path = Path::new(std::env!("CARGO_MANIFEST_DIR"))
.join(format!("tests/resources/contract/{contract_address}.json"));
let params = set_up_parameters_for_outbox_message(
rollup_address,
contract_address,
&installer_path,
&contract_path,
)
.await;

let (mut octez_node, octez_client, mut baker) =
setup(Some(params.parameter_file().path().to_path_buf())).await;
let mut rollup = spawn_rollup(
zcabter marked this conversation as resolved.
Show resolved Hide resolved
&octez_node,
&octez_client,
installer_path,
preimages_dir,
Some(rollup_address),
)
.await;

octez_client
.send_rollup_inbox_message("bootstrap1", "0000", Some(0.1))
.await
.unwrap();

// wait until outbox message is cemented
let proof = wait_for_outbox_proof(&rollup.rpc_endpoint().to_string())
.await
.unwrap();

let (block, op) = octez_client
.execute_rollup_outbox_message(
&SmartRollupHash::from_base58_check(rollup_address).unwrap(),
"bootstrap1",
&proof.commitment,
&format!("0x{}", &proof.proof),
Some(0.1),
)
.await
.unwrap();

tokio::time::timeout(
tokio::time::Duration::from_secs(5),
octez_client.wait_for(&op, Some(&block), None),
)
.await
.expect("wait_for should complete soon enough")
.expect("wait_for should be able to find the operation");

let operation_kind =
get_operation_kind(&octez_node.rpc_endpoint().to_string(), &block, &op)
.await
.unwrap();
assert_eq!(operation_kind, "smart_rollup_execute_outbox_message");

let _ = rollup.kill().await;
let _ = baker.kill().await;
let _ = octez_node.kill().await;
}

async fn set_up_parameters_for_outbox_message(
rollup_address: &str,
contract_address: &str,
installer_path: &PathBuf,
contract_path: &PathBuf,
) -> ProtocolParameter {
let kernel = String::from_utf8(
tokio::fs::read(&installer_path)
.await
.unwrap_or_else(|e| panic!("failed to read installer file: {:?}", e)),
)
.unwrap();
let contract_json = serde_json::from_slice(
&tokio::fs::read(&contract_path)
.await
.unwrap_or_else(|e| panic!("failed to read contract file: {:?}", e)),
)
.unwrap();
let params = ProtocolParameterBuilder::new()
.set_bootstrap_smart_rollups([BootstrapSmartRollup::new(
rollup_address,
SmartRollupPvmKind::Wasm,
&kernel,
serde_json::json!({"prim": "bytes"}),
)
.unwrap()])
.set_bootstrap_contracts([BootstrapContract::new(
contract_json,
1_000_000,
Some(contract_address),
)
.unwrap()])
.set_source_path(
Path::new(std::env!("CARGO_MANIFEST_DIR"))
.join("tests/sandbox-params.json")
.to_str()
.unwrap(),
)
.build()
.unwrap();

let mut content = tokio::fs::read_to_string(params.parameter_file().path())
.await
.unwrap();
let mut value: serde_json::Value = serde_json::from_str(&content).unwrap();

// overwriting these config values so that outbox messages get cemented sooner
value.as_object_mut().unwrap().insert(
"smart_rollup_challenge_window_in_blocks".to_owned(),
serde_json::json!(8),
);
value.as_object_mut().unwrap().insert(
"smart_rollup_commitment_period_in_blocks".to_owned(),
serde_json::json!(8),
);

content = serde_json::to_string(&value).unwrap();
tokio::fs::File::create(params.parameter_file().path())
.await
.unwrap()
.write_all(content.as_bytes())
.await
.unwrap();

params
}

async fn wait_for_outbox_proof(rollup_rpc_endpoint: &str) -> anyhow::Result<OutputProof> {
huancheng-trili marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Deserialize)]
struct Message {
message_index: u32,
}
#[derive(Deserialize)]
struct Executable {
outbox_level: u32,
messages: Vec<Message>,
}

let url = format!("{rollup_rpc_endpoint}/local/outbox/pending/executable");
let (outbox_level, message_index) = poll(30, 1000, || async {
// response: [{"outbox_level": 1, "messages": [{"message_index": 0, ...}, {"message_index": 1, ...}]}]
let res = reqwest::get(&url).await.ok()?;
let vs = res.json::<Vec<Executable>>().await.unwrap();
// using the first message here since any of those should work
let v = vs.first()?;
let m = v.messages.first()?;
Some((v.outbox_level, m.message_index))
})
.await
.expect("should be able to find outbox message soon enough");

let url = format!("{rollup_rpc_endpoint}/global/block/head/helpers/proofs/outbox/{outbox_level}/messages?index={message_index}");
let res = reqwest::get(&url)
.await
.context("failed to call rollup RPC endpoint")?;
let v = res
.json::<OutputProof>()
.await
.context("failed to parse response of rollup outbox proof RPC")?;
Ok(v)
}
10 changes: 8 additions & 2 deletions crates/jstzd/tests/octez_rollup_test.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
mod utils;
use jstzd::task::Task;
use std::path::Path;
use utils::{setup, spawn_rollup};

#[tokio::test(flavor = "multi_thread")]
async fn test_rollup() {
let (mut octez_node, client, mut baker) = setup().await;
let mut rollup = spawn_rollup(&octez_node, &client).await;
let (mut octez_node, client, mut baker) = setup(None).await;
let kernel_installer = Path::new(std::env!("CARGO_MANIFEST_DIR"))
.join("tests/toy_rollup/kernel_installer");
let preimages_dir =
Path::new(std::env!("CARGO_MANIFEST_DIR")).join("tests/toy_rollup/preimages");
let mut rollup =
spawn_rollup(&octez_node, &client, kernel_installer, preimages_dir, None).await;
let _ = rollup.kill().await;
// Should get an error since the rollup node should have been terminated
let rollup_is_alive = rollup.health_check().await;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"code": [
{
"args": [{ "annots": ["%myEntryPoint"], "prim": "int" }],
"prim": "parameter"
},
{
"args": [
{
"args": [
{ "annots": ["%myParameter1"], "prim": "int" },
{ "annots": ["%myParameter2"], "prim": "int" }
],
"prim": "pair"
}
],
"prim": "storage"
},
{
"args": [
[
{ "prim": "UNPAIR" },
{ "args": [{ "prim": "int" }, { "int": "123" }], "prim": "PUSH" },
{ "args": [{ "int": "3" }], "prim": "DUP" },
{ "prim": "CAR" },
{ "prim": "COMPARE" },
{ "prim": "LE" },
{
"args": [
[],
[
{
"args": [
{ "prim": "string" },
{
"string": "Assert failure: self.data.myParameter1 <= 123"
}
],
"prim": "PUSH"
},
{ "prim": "FAILWITH" }
]
],
"prim": "IF"
},
{ "prim": "SWAP" },
{ "prim": "DUP" },
{ "prim": "CAR" },
{ "args": [{ "int": "2" }], "prim": "DIG" },
{ "prim": "ADD" },
{ "args": [{ "int": "1" }], "prim": "UPDATE" },
{ "args": [{ "prim": "operation" }], "prim": "NIL" },
{ "prim": "PAIR" }
]
],
"prim": "code"
}
],
"storage": { "args": [{ "int": "1" }, { "int": "0" }], "prim": "Pair" }
}
Loading