Skip to content

Commit

Permalink
feat: write world and model Abi and embed in json manifest (#1734)
Browse files Browse the repository at this point in the history
* feat: write world manifest and embed in json manifest

* feat: add model ABIs

* refacto: factorize to_embed function and add tests

* fix: use assert_eq in tests and fix warning

---------

Co-authored-by: glihm <[email protected]>
  • Loading branch information
lambda-0x and glihm authored Apr 3, 2024
1 parent f9b8c02 commit 47a87bc
Show file tree
Hide file tree
Showing 22 changed files with 4,316 additions and 120 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/dojo-lang/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ fn update_manifest(

let mut crate_ids = crate_ids.to_vec();

let (hash, _) = get_compiled_artifact_from_map(&compiled_artifacts, WORLD_CONTRACT_NAME)?;
let (hash, abi) = get_compiled_artifact_from_map(&compiled_artifacts, WORLD_CONTRACT_NAME)?;
write_manifest_and_abi(
&relative_manifests_dir,
&relative_abis_dir,
Expand All @@ -237,7 +237,7 @@ fn update_manifest(
Class { class_hash: *hash, abi: None },
WORLD_CONTRACT_NAME.into(),
),
&None,
abi,
)?;

let (hash, _) = get_compiled_artifact_from_map(&compiled_artifacts, BASE_CONTRACT_NAME)?;
Expand Down
1 change: 1 addition & 0 deletions crates/dojo-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dojo-lang.workspace = true
dojo-test-utils = { path = "../dojo-test-utils" }
similar-asserts.workspace = true
tokio.workspace = true
tempfile = "3.3.0"

[features]
contracts = [ "dep:dojo-types", "dep:http" ]
Expand Down
67 changes: 45 additions & 22 deletions crates/dojo-world/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,25 +127,8 @@ pub enum AbiFormat {
Embed(Vec<AbiEntry>),
}

#[cfg(test)]
impl PartialEq for AbiFormat {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(AbiFormat::Path(p1), AbiFormat::Path(p2)) => p1 == p2,
(AbiFormat::Embed(e1), AbiFormat::Embed(e2)) => {
// Currently, [`AbiEntry`] does not implement [`PartialEq`] so we cannot compare
// them directly.
let e1_json = serde_json::to_string(e1).expect("valid JSON from ABI");
let e2_json = serde_json::to_string(e2).expect("valid JSON from ABI");
e1_json == e2_json
}
_ => false,
}
}
}

impl AbiFormat {
/// Returns the path to the ABI file, or None if embedded.
/// Get the [`Utf8PathBuf`] if the ABI is stored as a path.
pub fn to_path(&self) -> Option<&Utf8PathBuf> {
match self {
AbiFormat::Path(p) => Some(p),
Expand All @@ -164,6 +147,37 @@ impl AbiFormat {
AbiFormat::Embed(abi) => Ok(serde_json::to_string(&abi)?),
}
}

/// Convert to embed variant.
///
/// # Arguments
///
/// * `root_dir` - The root directory for the abi file resolution.
pub fn to_embed(&self, root_dir: &Utf8PathBuf) -> Result<AbiFormat, AbstractManifestError> {
if let AbiFormat::Path(abi_path) = self {
let mut abi_file = std::fs::File::open(root_dir.join(abi_path))?;
Ok(serde_json::from_reader(&mut abi_file)?)
} else {
Ok(self.clone())
}
}
}

#[cfg(test)]
impl PartialEq for AbiFormat {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(AbiFormat::Path(p1), AbiFormat::Path(p2)) => p1 == p2,
(AbiFormat::Embed(e1), AbiFormat::Embed(e2)) => {
// Currently, [`AbiEntry`] does not implement [`PartialEq`] so we cannot compare
// them directly.
let e1_json = serde_json::to_string(e1).expect("valid JSON from ABI");
let e2_json = serde_json::to_string(e2).expect("valid JSON from ABI");
e1_json == e2_json
}
_ => false,
}
}
}

#[serde_as]
Expand Down Expand Up @@ -380,11 +394,20 @@ impl DeploymentManifest {

// Embedding ABIs into the manifest.
let mut manifest_with_abis = self.clone();

if let Some(abi_format) = &manifest_with_abis.world.inner.abi {
manifest_with_abis.world.inner.abi = Some(abi_format.to_embed(manifest_dir)?);
}

for contract in &mut manifest_with_abis.contracts {
if let Some(AbiFormat::Path(abi_path)) = &contract.inner.abi {
let mut abi_file = std::fs::File::open(manifest_dir.join(abi_path))?;
let abi_entries: Vec<AbiEntry> = serde_json::from_reader(&mut abi_file)?;
contract.inner.abi = Some(AbiFormat::Embed(abi_entries));
if let Some(abi_format) = &contract.inner.abi {
contract.inner.abi = Some(abi_format.to_embed(manifest_dir)?);
}
}

for model in &mut manifest_with_abis.models {
if let Some(abi_format) = &model.inner.abi {
model.inner.abi = Some(abi_format.to_embed(manifest_dir)?);
}
}

Expand Down
40 changes: 39 additions & 1 deletion crates/dojo-world/src/manifest_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::io::Write;

use camino::Utf8PathBuf;
use dojo_lang::compiler::{BASE_DIR, MANIFESTS_DIR};
use dojo_test_utils::rpc::MockJsonRpcTransport;
Expand All @@ -6,11 +8,12 @@ use dojo_test_utils::sequencer::{
};
use serde_json::json;
use starknet::accounts::ConnectedAccount;
use starknet::core::types::contract::AbiEntry;
use starknet::core::types::{EmittedEvent, FieldElement};
use starknet::macros::{felt, selector, short_string};
use starknet::providers::jsonrpc::{JsonRpcClient, JsonRpcMethod};

use super::{parse_contracts_events, BaseManifest, DojoContract, DojoModel};
use super::{parse_contracts_events, AbiFormat, BaseManifest, DojoContract, DojoModel};
use crate::contracts::world::test::deploy_world;
use crate::manifest::{parse_models_events, AbstractManifestError, DeploymentManifest, Manifest};
use crate::migration::world::WorldDiff;
Expand Down Expand Up @@ -392,3 +395,38 @@ async fn fetch_remote_manifest() {

assert_eq!(diff.count_diffs(), 0, "there should not be any diff");
}

#[test]
fn test_abi_format_to_embed() -> Result<(), Box<dyn std::error::Error>> {
let temp_dir = tempfile::tempdir()?;
let temp_path = temp_dir.path().join("abi.json");
let mut temp_file = std::fs::File::create(&temp_path)?;

let temp_dir_utf8 = Utf8PathBuf::from_path_buf(temp_dir.path().into()).unwrap();

writeln!(
temp_file,
"[{{\"type\":\"function\",\"name\":\"testFunction\",\"inputs\":[],\"outputs\":[],\"\
state_mutability\":\"view\"}}]"
)?;

let abi_format_path = AbiFormat::Path(Utf8PathBuf::from_path_buf(temp_path).unwrap());
let embedded_abi = abi_format_path.to_embed(&temp_dir_utf8)?;

let abi_format_not_changed = embedded_abi.clone();

match &embedded_abi {
AbiFormat::Embed(abi_entries) => {
assert_eq!(abi_entries.len(), 1);
let entry_0 = &abi_entries[0];
if let AbiEntry::Function(function) = entry_0 {
assert_eq!(function.name, "testFunction");
}
}
_ => panic!("Expected AbiFormat::Embed variant"),
}

assert_eq!(embedded_abi, abi_format_not_changed.to_embed(&temp_dir_utf8).unwrap());

Ok(())
}
72 changes: 36 additions & 36 deletions crates/katana/rpc/rpc/tests/torii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ async fn test_get_transactions() {
let response: TransactionsPage = client.get_transactions(cursor).await.unwrap();

assert!(response.transactions.is_empty());
assert!(response.cursor.block_number == 1);
assert!(response.cursor.transaction_index == 0);
assert_eq!(response.cursor.block_number, 1);
assert_eq!(response.cursor.transaction_index, 0);

let declare_res = account.declare(contract.clone(), compiled_class_hash).send().await.unwrap();

// Should return successfully with single pending txn.
let response: TransactionsPage = client.get_transactions(response.cursor).await.unwrap();

assert!(response.transactions.len() == 1);
assert!(response.cursor.block_number == 1);
assert!(response.cursor.transaction_index == 1);
assert_eq!(response.transactions.len(), 1);
assert_eq!(response.cursor.block_number, 1);
assert_eq!(response.cursor.transaction_index, 1);

// Create block 1.
let _: () = client.generate_block().await.unwrap();
Expand All @@ -60,8 +60,8 @@ async fn test_get_transactions() {
let response: TransactionsPage = client.get_transactions(response.cursor).await.unwrap();

assert!(response.transactions.is_empty());
assert!(response.cursor.block_number == 2);
assert!(response.cursor.transaction_index == 0);
assert_eq!(response.cursor.block_number, 2);
assert_eq!(response.cursor.transaction_index, 0);

// Should block on cursor at end of page and return on new txn
let long_poll_future = client.get_transactions(response.cursor);
Expand All @@ -72,9 +72,9 @@ async fn test_get_transactions() {
tokio::select! {
result = long_poll_future => {
let long_poll_result = result.unwrap();
assert!(long_poll_result.transactions.len() == 1);
assert!(long_poll_result.cursor.block_number == 2);
assert!(long_poll_result.cursor.transaction_index == 1);
assert_eq!(long_poll_result.transactions.len(), 1);
assert_eq!(long_poll_result.cursor.block_number, 2);
assert_eq!(long_poll_result.cursor.transaction_index, 1);
}
result = deploy_txn_future => {
// The declare transaction has completed, but we don't need to do anything with it here.
Expand All @@ -99,10 +99,10 @@ async fn test_get_transactions() {
.await
.unwrap();

assert!(response.transactions.len() == 1);
assert!(response.transactions[0].0.hash == deploy_txn_future.transaction_hash);
assert!(response.cursor.block_number == 3);
assert!(response.cursor.transaction_index == 1);
assert_eq!(response.transactions.len(), 1);
assert_eq!(response.transactions[0].0.hash, deploy_txn_future.transaction_hash);
assert_eq!(response.cursor.block_number, 3);
assert_eq!(response.cursor.transaction_index, 1);

// Create block 3.
let _: () = client.generate_block().await.unwrap();
Expand All @@ -123,29 +123,29 @@ async fn test_get_transactions() {

let start_cursor = response.cursor;
let response: TransactionsPage = client.get_transactions(start_cursor).await.unwrap();
assert!(response.transactions.len() == 100);
assert!(response.cursor.block_number == 4);
assert!(response.cursor.transaction_index == 100);
assert_eq!(response.transactions.len(), 100);
assert_eq!(response.cursor.block_number, 4);
assert_eq!(response.cursor.transaction_index, 100);

// Should get one more
let response: TransactionsPage = client.get_transactions(response.cursor).await.unwrap();
assert!(response.transactions.len() == 1);
assert!(response.cursor.block_number == 4);
assert!(response.cursor.transaction_index == 101);
assert_eq!(response.transactions.len(), 1);
assert_eq!(response.cursor.block_number, 4);
assert_eq!(response.cursor.transaction_index, 101);

// Create block 4.
let _: () = client.generate_block().await.unwrap();

let response: TransactionsPage = client.get_transactions(start_cursor).await.unwrap();
assert!(response.transactions.len() == 100);
assert!(response.cursor.block_number == 4);
assert!(response.cursor.transaction_index == 100);
assert_eq!(response.transactions.len(), 100);
assert_eq!(response.cursor.block_number, 4);
assert_eq!(response.cursor.transaction_index, 100);

// Should get one more
let response: TransactionsPage = client.get_transactions(response.cursor).await.unwrap();
assert!(response.transactions.len() == 1);
assert!(response.cursor.block_number == 5);
assert!(response.cursor.transaction_index == 0);
assert_eq!(response.transactions.len(), 1);
assert_eq!(response.cursor.block_number, 5);
assert_eq!(response.cursor.transaction_index, 0);

sequencer.stop().expect("failed to stop sequencer");
}
Expand Down Expand Up @@ -176,9 +176,9 @@ async fn test_get_transactions_with_instant_mining() {
// Should return successfully with single txn.
let response: TransactionsPage = client.get_transactions(cursor).await.unwrap();

assert!(response.transactions.len() == 1);
assert!(response.cursor.block_number == 1);
assert!(response.cursor.transaction_index == 0);
assert_eq!(response.transactions.len(), 1);
assert_eq!(response.cursor.block_number, 1);
assert_eq!(response.cursor.transaction_index, 0);

// Should block on cursor at end of page and return on new txn
let long_poll_future = client.get_transactions(response.cursor);
Expand All @@ -189,9 +189,9 @@ async fn test_get_transactions_with_instant_mining() {
tokio::select! {
result = long_poll_future => {
let long_poll_result = result.unwrap();
assert!(long_poll_result.transactions.len() == 1);
assert!(long_poll_result.cursor.block_number == 2);
assert!(long_poll_result.cursor.transaction_index == 0);
assert_eq!(long_poll_result.transactions.len(), 1);
assert_eq!(long_poll_result.cursor.block_number, 2);
assert_eq!(long_poll_result.cursor.transaction_index, 0);
}
result = deploy_txn_future => {
// The declare transaction has completed, but we don't need to do anything with it here.
Expand All @@ -213,10 +213,10 @@ async fn test_get_transactions_with_instant_mining() {
.await
.unwrap();

assert!(response.transactions.len() == 1);
assert!(response.transactions[0].0.hash == deploy_txn_future.transaction_hash);
assert!(response.cursor.block_number == 3);
assert!(response.cursor.transaction_index == 1);
assert_eq!(response.transactions.len(), 1);
assert_eq!(response.transactions[0].0.hash, deploy_txn_future.transaction_hash);
assert_eq!(response.cursor.block_number, 3);
assert_eq!(response.cursor.transaction_index, 1);

sequencer.stop().expect("failed to stop sequencer");
}
Expand Down
6 changes: 4 additions & 2 deletions crates/sozo/ops/src/migration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use dojo_world::contracts::abi::world::ResourceMetadata;
use dojo_world::contracts::cairo_utils;
use dojo_world::contracts::world::WorldContract;
use dojo_world::manifest::{
AbiFormat, AbstractManifestError, BaseManifest, DeploymentManifest, DojoContract, Manifest,
ManifestMethods, OverlayManifest,
AbiFormat, AbstractManifestError, BaseManifest, Contract, DeploymentManifest, DojoContract,
Manifest, ManifestMethods, OverlayManifest,
};
use dojo_world::metadata::dojo_metadata_from_workspace;
use dojo_world::migration::contract::ContractMigration;
Expand Down Expand Up @@ -226,6 +226,8 @@ async fn update_manifest_abis(
manifest.inner.set_abi(Some(AbiFormat::Path(deployed_relative_path)));
}

inner_helper::<Contract>(manifest_dir, &mut local_manifest.world, chain_id).await;

for contract in local_manifest.contracts.iter_mut() {
inner_helper::<DojoContract>(manifest_dir, contract, chain_id).await;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/torii/libp2p/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ mod test {
});

// Initialize the first client (listener)
let mut client = RelayClient::new("/ip4/127.0.0.1/tcp/9900".to_string())?;
let client = RelayClient::new("/ip4/127.0.0.1/tcp/9900".to_string())?;
tokio::spawn(async move {
client.event_loop.lock().await.run().await;
});
Expand Down
Loading

0 comments on commit 47a87bc

Please sign in to comment.