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

test(katana): messaging e2e test #1925

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6a11b1d
-Runner creation
fabrobles92 May 4, 2024
1a5f775
L2 -> L1 messaging
fabrobles92 May 5, 2024
fd99511
clippy and fmt
fabrobles92 May 5, 2024
438dced
style
kariy May 6, 2024
bd2d71b
apply kari's cool formatting
kariy May 6, 2024
f7e830c
fmt
kariy May 6, 2024
4dcb1b7
Applying more change from Code review
fabrobles92 May 15, 2024
20fb000
tests
kariy May 18, 2024
ed2bae1
Merge remote-tracking branch 'origin/main' into katana/Messaging-Intr…
fabrobles92 May 20, 2024
fe95f29
Merge remote-tracking branch 'origin/main' into katana/Messaging-Intr…
fabrobles92 May 24, 2024
122fa28
Delete debug from Sol! and not used B256
fabrobles92 May 26, 2024
2990cf9
Merge remote-tracking branch 'origin/main' into katana/Messaging-Intr…
fabrobles92 May 27, 2024
d51cb51
Cleaning cargo.lock
fabrobles92 May 29, 2024
e1766f9
Merge remote-tracking branch 'origin/main' into katana/Messaging-Intr…
fabrobles92 May 29, 2024
758ce41
Add new option to Katnna Runner Config
fabrobles92 Jun 2, 2024
6548532
Merge remote-tracking branch 'origin/main' into katana/Messaging-Intr…
fabrobles92 Jun 3, 2024
1da68bb
consume message and check msg fee
kariy Jun 10, 2024
a1641ee
clippy and fmt
kariy Jun 10, 2024
866aa6a
- Use alloy Anvil Binding Object to spawn an instance
fabrobles92 Jun 11, 2024
22cfff7
Add step to install anvil in Dockerfile
fabrobles92 Jun 11, 2024
a72ed8a
Remove profile local from Anvil installation command
fabrobles92 Jun 11, 2024
0ce7e88
Merge remote-tracking branch 'origin/main' into katana/Messaging-Intr…
fabrobles92 Jun 12, 2024
5d691f4
Delete step to install anvil
fabrobles92 Jun 12, 2024
7f64bb8
Point CI to devcontainer that has Anvil installed
fabrobles92 Jun 12, 2024
7187343
Update Alloy
fabrobles92 Jun 13, 2024
dcdebce
merge main + fix L1HandlerTxHash
glihm Jun 22, 2024
f71f05f
merge main
glihm Jun 22, 2024
9e1ed0d
fix: fix test with pre-computed tx hash
glihm Jun 22, 2024
c6bcd1d
ci: increment cores for clippy
glihm Jun 22, 2024
6785aa7
ci: pre-build latest sozo to re-use in jobs
glihm Jun 22, 2024
b4d3f65
ci: fix missing dependency
glihm Jun 22, 2024
e368c33
ci: fix missing x permission
glihm Jun 22, 2024
5fd834e
ci: remove unused cached to save time
glihm Jun 22, 2024
cb0e662
restore lockfile
kariy Jun 24, 2024
dbfe622
revert l1 handler tx hash computation changes
kariy Jun 24, 2024
28db6b4
revert some changes
kariy Jun 24, 2024
d1df5f5
revert test case
kariy Jun 24, 2024
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,058 changes: 1,013 additions & 45 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion crates/katana/rpc/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ repository.workspace = true
version.workspace = true

[dependencies]
dojo-world = { workspace = true }
dojo-metrics.workspace = true
katana-core.workspace = true
katana-executor.workspace = true
Expand All @@ -16,7 +17,7 @@ katana-rpc-api.workspace = true
katana-rpc-types-builder.workspace = true
katana-rpc-types.workspace = true
katana-tasks.workspace = true

tempfile = "3.8.1"
anyhow.workspace = true
flate2.workspace = true
futures.workspace = true
Expand All @@ -34,6 +35,7 @@ tokio.workspace = true
tower = { version = "0.4.13", features = [ "full" ] }
tower-http = { version = "0.4.1", features = [ "full" ] }
tracing.workspace = true
cainome.workspace = true

[dev-dependencies]
assert_matches = "1.5.0"
Expand All @@ -42,4 +44,6 @@ cairo-lang-starknet-classes.workspace = true
dojo-test-utils.workspace = true
jsonrpsee = { workspace = true, features = [ "client" ] }
katana-rpc-api = { workspace = true, features = [ "client" ] }
katana-runner.workspace = true
url.workspace = true
alloy = { git = "https://github.com/alloy-rs/alloy", rev = "7128c53"}
242 changes: 242 additions & 0 deletions crates/katana/rpc/rpc/tests/messaging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;

use alloy::primitives::{Uint, U256};
use alloy::sol;
use cainome::cairo_serde::EthAddress;
use cainome::rs::abigen;
use dojo_world::utils::TransactionWaiter;
use katana_runner::{AnvilRunner, KatanaRunner, KatanaRunnerConfig};
use serde_json::json;
use starknet::accounts::{Account, ConnectedAccount};
use starknet::contract::ContractFactory;
use starknet::core::types::{
BlockId, BlockTag, ContractClass, FieldElement, Transaction, TransactionFinalityStatus,
};
use starknet::core::utils::get_contract_address;
use starknet::macros::felt;
use starknet::providers::Provider;
use tempfile::tempdir;

mod common;

sol!(
#[allow(missing_docs)]
#[sol(rpc)]
StarknetContract,
"tests/test_data/solidity/StarknetMessagingLocalCompiled.json"
);

sol!(
#[allow(missing_docs)]
#[sol(rpc)]
Contract1,
"tests/test_data/solidity/Contract1Compiled.json"
);

abigen!(CairoMessagingContract, "crates/katana/rpc/rpc/tests/test_data/cairo_l1_msg_contract.json");

#[tokio::test(flavor = "multi_thread")]
async fn test_messaging() {
// Prepare Anvil + Messaging Contracts
let anvil_runner = AnvilRunner::new().await.unwrap();
let l1_provider = anvil_runner.provider();

// Deploy the core messaging contract on L1
let core_contract = StarknetContract::deploy(anvil_runner.provider()).await.unwrap();
// Deploy test contract on L1 used to send/receive messages to/from L2
let l1_test_contract = Contract1::deploy(l1_provider, *core_contract.address()).await.unwrap();

// Prepare Katana + Messaging Contract
let messaging_config = json!({
"chain": "ethereum",
"rpc_url": anvil_runner.endpoint,
"contract_address": core_contract.address().to_string(),
"sender_address": anvil_runner.address(),
"private_key": anvil_runner.secret_key(),
"interval": 2,
"from_block": 0
})
.to_string();

let dir = tempdir().expect("failed creating temp dir");
let path = dir.path().join("temp-anvil-messaging.json");
std::fs::write(&path, messaging_config.as_bytes()).expect("failed to write config to file");

let katana_runner = KatanaRunner::new_with_config(KatanaRunnerConfig {
n_accounts: 2,
disable_fee: false,
block_time: None,
port: None,
program_name: None,
run_name: None,
messaging: Some(path.to_str().unwrap().to_string()),
})
.unwrap();

let katana_account = katana_runner.account(0);

// Deploy test L2 contract that can send/receive messages to/from L1
let l2_test_contract = {
// Prepare contract declaration params
let path = PathBuf::from("tests/test_data/cairo_l1_msg_contract.json");
let (contract, compiled_hash) = common::prepare_contract_declaration_params(&path).unwrap();

// Declare the contract
let class_hash = contract.class_hash();
let res = katana_account.declare(contract.into(), compiled_hash).send().await.unwrap();

// The waiter already checks that the transaction is accepted and succeeded on L2.
TransactionWaiter::new(res.transaction_hash, katana_account.provider())
.await
.expect("declare tx failed");

// Checks that the class was indeed declared
let block_id = BlockId::Tag(BlockTag::Latest);
let actual_class = katana_account.provider().get_class(block_id, class_hash).await.unwrap();

let ContractClass::Sierra(class) = actual_class else { panic!("Invalid class type") };
assert_eq!(class.class_hash(), class_hash, "invalid declared class"); // just to make sure the rpc returns the correct class

// Compute the contract address
let address = get_contract_address(FieldElement::ZERO, class_hash, &[], FieldElement::ZERO);

// Deploy the contract using UDC
let res = ContractFactory::new(class_hash, &katana_account)
.deploy(Vec::new(), FieldElement::ZERO, false)
.send()
.await
.expect("Unable to deploy contract");

// The waiter already checks that the transaction is accepted and succeeded on L2.
TransactionWaiter::new(res.transaction_hash, katana_account.provider())
.await
.expect("deploy tx failed");

// Checks that the class was indeed deployed with the correct class
let actual_class_hash = katana_account
.provider()
.get_class_hash_at(block_id, address)
.await
.expect("failed to get class hash at address");

assert_eq!(actual_class_hash, class_hash, "invalid deployed class");

address
};

// Send message from L1 to L2
{
// The L2 contract address to send the message to
let recipient = l2_test_contract;
// The L2 contract function to call
// TODO: compute the function hash from the function signature instead of hardcoding it
let function = "0x005421de947699472df434466845d68528f221a52fce7ad2934c5dae2e1f1cdc";
fabrobles92 marked this conversation as resolved.
Show resolved Hide resolved
// The L2 contract function arguments
let calldata = vec![123];

let call = l1_test_contract
.sendMessage(
U256::from_str(&recipient.to_string()).unwrap(),
U256::from_str(function).unwrap(),
calldata.iter().map(|x| U256::from(*x)).collect::<Vec<_>>(),
)
.gas(12000000)
.value(Uint::from(1));

let receipt = call
.send()
.await
.expect("failed to send tx")
.get_receipt()
.await
.expect("error getting transaction receipt");

assert!(receipt.status(), "failed to send L1 -> L2 message");

// TODO: query the core messaging contract to check that the message hash do exist
kariy marked this conversation as resolved.
Show resolved Hide resolved

// Wait for the tx to be mined on L2 (Katana)
tokio::time::sleep(Duration::from_secs(1)).await;

// Check that the transaction was indeed received by Katana
let block_id = BlockId::Tag(BlockTag::Latest);
let tx = katana_account
.provider()
.get_transaction_by_block_id_and_index(block_id, 0)
.await
.unwrap();

match tx {
Transaction::L1Handler(ref l1_handler_transaction) => {
let calldata = &l1_handler_transaction.calldata;

// TODO: compute the tx hash using the message params, we're not really asserting
// the hash to be correct as we just hardcoded it.
//
// hint: use `compute_l1_handler_tx_hash` and assert with the actual hash instead of
// the hardcoded value
fabrobles92 marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(
tx.transaction_hash(),
&felt!("0x00c33cc113afc56bc878034908472770cb13eda6ad8ad91feb25fd4e5c9196a0")
);

// TODO: compute the l1 message hash and assert with the actual message hash in the l1 handler receipt
// hint: use `compute_l1_message_hash`
fabrobles92 marked this conversation as resolved.
Show resolved Hide resolved

assert_eq!(FieldElement::to_string(&calldata[1]), "123")
}
_ => {
panic!("Error, No L1handler transaction")
}
}
}

// Send message from L2 to L1
{
// The L1 contract address to send the message to
let l1_contract_address = l1_test_contract.address();
let l1_contract_address = FieldElement::from_str(&l1_contract_address.to_string()).unwrap();

let l2_contract = CairoMessagingContract::new(l2_test_contract, &katana_account);

// Send message to L1
let res = l2_contract
.send_message_value(&EthAddress::from(l1_contract_address), &FieldElement::TWO)
.send()
.await
.expect("Call to send_message_value failed");

TransactionWaiter::new(res.transaction_hash, katana_account.provider())
.with_tx_status(TransactionFinalityStatus::AcceptedOnL2)
.await
.expect("send message to l1 tx failed");

// The L2 contract address that sent the message
let from_address = U256::from_str(&l2_test_contract.to_string()).unwrap();
// The message payload
let payload = vec![U256::from(2)];

let call = l1_test_contract
.consumeMessage(from_address, payload)
.value(Uint::from(1))
.gas(12000000)
.nonce(5);

// Wait for the tx to be mined on L1 (Anvil)
tokio::time::sleep(Duration::from_secs(8)).await;

let receipt = call
.send()
.await
.expect("failed to send tx")
.get_receipt()
.await
.expect("error getting transaction receipt");

assert!(receipt.status(), "failed to consume L2 message from L1");

// TODO: query the core messaging contract to check that the message hash do exist
kariy marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading