Skip to content

Commit

Permalink
update implementation to read writes data while loading remote manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
lambda-0x committed Jul 30, 2024
1 parent 1994ba4 commit 571aca1
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 221 deletions.
16 changes: 11 additions & 5 deletions bin/sozo/src/commands/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,15 @@ pub async fn grant(
contracts=?models_contracts,
"Granting Writer permissions."
);
auth::grant_writer(ui, &world, models_contracts, transaction.into(), default_namespace)
auth::grant_writer(ui, &world, &models_contracts, transaction.into(), default_namespace)
.await
}
AuthKind::Owner { owners_resources } => {
trace!(
resources=?owners_resources,
"Granting Owner permissions."
);
auth::grant_owner(ui, &world, owners_resources, transaction.into(), default_namespace)
auth::grant_owner(ui, &world, &owners_resources, transaction.into(), default_namespace)
.await
}
}
Expand Down Expand Up @@ -192,15 +192,21 @@ pub async fn revoke(
contracts=?models_contracts,
"Revoking Writer permissions."
);
auth::revoke_writer(ui, &world, models_contracts, transaction.into(), default_namespace)
.await
auth::revoke_writer(
ui,
&world,
&models_contracts,
transaction.into(),
default_namespace,
)
.await
}
AuthKind::Owner { owners_resources } => {
trace!(
resources=?owners_resources,
"Revoking Owner permissions."
);
auth::revoke_owner(ui, &world, owners_resources, transaction.into(), default_namespace)
auth::revoke_owner(ui, &world, &owners_resources, transaction.into(), default_namespace)
.await
}
}
Expand Down
110 changes: 82 additions & 28 deletions crates/dojo-world/src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::PathBuf;
use std::{fs, io};

use anyhow::Result;
use cainome::cairo_serde::{ByteArray, CairoSerde, Error as CainomeError};
use cainome::cairo_serde::{ByteArray, CairoSerde, Error as CainomeError, Zeroable};
use camino::Utf8PathBuf;
use serde::de::DeserializeOwned;
use serde::Serialize;
Expand All @@ -29,10 +29,9 @@ mod test;
mod types;

pub use types::{
AbiFormat, BaseManifest, Class, ComputedValueEntrypoint, ContractMetadata, DeploymentManifest,
DeploymentMetadata, DojoContract, DojoModel, Manifest, ManifestMethods, Member, Operation,
OverlayClass, OverlayContract, OverlayDojoContract, OverlayDojoModel, OverlayManifest,
WorldContract, WorldMetadata,
AbiFormat, BaseManifest, Class, ComputedValueEntrypoint, DeploymentManifest, DojoContract,
DojoModel, Manifest, ManifestMethods, Member, OverlayClass, OverlayContract,
OverlayDojoContract, OverlayDojoModel, OverlayManifest, WorldContract, WorldMetadata,
};

pub const WORLD_CONTRACT_TAG: &str = "dojo-world";
Expand Down Expand Up @@ -445,31 +444,22 @@ impl DeploymentManifest {
}
}

impl DeploymentMetadata {
pub fn load_from_path(path: &Utf8PathBuf) -> Result<Self, AbstractManifestError> {
let manifest: Self = toml::from_str(&fs::read_to_string(path)?).unwrap();
// impl DeploymentMetadata {
// pub fn load_from_path(path: &Utf8PathBuf) -> Result<Self, AbstractManifestError> {
// let manifest: Self = toml::from_str(&fs::read_to_string(path)?).unwrap();

Ok(manifest)
}
// Ok(manifest)
// }

pub fn write_to_path_toml(&self, path: &Utf8PathBuf) -> Result<()> {
fs::create_dir_all(path.parent().unwrap())?;
// pub fn write_to_path_toml(&self, path: &Utf8PathBuf) -> Result<()> {
// fs::create_dir_all(path.parent().unwrap())?;

let deployed_manifest = toml::to_string_pretty(&self)?;
fs::write(path, deployed_manifest)?;
// let deployed_manifest = toml::to_string_pretty(&self)?;
// fs::write(path, deployed_manifest)?;

Ok(())
}

// adds any missing contracts to the metadata
// this is required so we add any newly added contract to the metadata
pub fn add_missing(&mut self, manifest: &BaseManifest) {
for contract in manifest.contracts.iter() {
let name = naming::get_tag_from_filename(&contract.manifest_name).unwrap();
self.contracts.entry(name).or_insert(ContractMetadata::default());
}
}
}
// Ok(())
// }
// }

// TODO: currently implementing this method using trait is causing lifetime issue due to
// `async_trait` macro which is hard to debug. So moved it as a async method on type itself.
Expand All @@ -494,6 +484,7 @@ where
let registered_models_event_name = starknet_keccak("ModelRegistered".as_bytes());
let contract_deployed_event_name = starknet_keccak("ContractDeployed".as_bytes());
let contract_upgraded_event_name = starknet_keccak("ContractUpgraded".as_bytes());
let writer_updated_event_name = starknet_keccak("WriterUpdated".as_bytes());

let events = get_events(
&provider,
Expand All @@ -502,13 +493,15 @@ where
registered_models_event_name,
contract_deployed_event_name,
contract_upgraded_event_name,
writer_updated_event_name,
]],
)
.await?;

let mut registered_models_events = vec![];
let mut contract_deployed_events = vec![];
let mut contract_upgraded_events = vec![];
let mut writer_updated_events = vec![];

for event in events {
match event.keys.first() {
Expand All @@ -521,12 +514,19 @@ where
Some(event_name) if *event_name == contract_upgraded_event_name => {
contract_upgraded_events.push(event)
}
Some(event_name) if *event_name == writer_updated_event_name => {
writer_updated_events.push(event)
}
_ => {}
}
}

let models = parse_models_events(registered_models_events);
let mut contracts = parse_contracts_events(contract_deployed_events, contract_upgraded_events);
let mut contracts = parse_contracts_events(
contract_deployed_events,
contract_upgraded_events,
writer_updated_events,
);

for contract in &mut contracts {
contract.manifest_name = naming::get_filename_from_tag(&contract.inner.tag);
Expand Down Expand Up @@ -566,7 +566,54 @@ async fn get_events<P: Provider + Send + Sync>(
fn parse_contracts_events(
deployed: Vec<EmittedEvent>,
upgraded: Vec<EmittedEvent>,
granted: Vec<EmittedEvent>,
) -> Vec<Manifest<DojoContract>> {
fn retain_only_latest_grant_events(events: Vec<EmittedEvent>) -> HashMap<Felt, Vec<Felt>> {
// create a map with some extra data which will be flattened later
// system -> (block_num, (resource -> perm))
let mut grants: HashMap<Felt, (u64, HashMap<Felt, bool>)> = HashMap::new();
events.into_iter().for_each(|event| {
let mut data = event.data.into_iter();
let block_num = event.block_number;
let resource = data.next().expect("resource is missing from event");
let contract = data.next().expect("contract is missing from event");
let value = data.next().expect("value is missing from event");

let value = if value.is_zero() { false } else { true };

// Events that do not have a block number are ignored because we are unable to evaluate
// whether the events happened before or after the latest event that has been processed.
if let Some(num) = block_num {
grants
.entry(contract)
.and_modify(|(current_block, current_resource)| {
if *current_block <= num {
*current_block = num;
current_resource.insert(resource, value);
}
})
.or_insert((num, HashMap::from([(resource, value)])));
}
});

// flatten out the map to remove block_number information and only include resources that are true
// i.e. system -> [resources]
let ret = grants
.into_iter()
.map(|(contract, (_, resources))| {
(
contract,
resources
.into_iter()
.filter_map(|(resource, bool)| if bool { Some(resource) } else { None })
.collect(),
)
})
.collect();
// dbg!(&ret);
ret
}

fn retain_only_latest_upgrade_events(events: Vec<EmittedEvent>) -> HashMap<Felt, Felt> {
// addr -> (block_num, class_hash)
let mut upgrades: HashMap<Felt, (u64, Felt)> = HashMap::new();
Expand All @@ -584,7 +631,7 @@ fn parse_contracts_events(
upgrades
.entry(address)
.and_modify(|(current_block, current_class_hash)| {
if *current_block < num {
if *current_block <= num {
*current_block = num;
*current_class_hash = class_hash;
}
Expand All @@ -597,6 +644,7 @@ fn parse_contracts_events(
}

let upgradeds = retain_only_latest_upgrade_events(upgraded);
let grants = retain_only_latest_grant_events(granted);

deployed
.into_iter()
Expand All @@ -623,12 +671,18 @@ fn parse_contracts_events(
class_hash = *upgrade;
}

let mut writes = vec![];
if let Some(contract) = grants.get(&address) {
writes.extend(contract.iter().map(|f| f.to_hex_string()));
}

Manifest::new(
DojoContract {
address: Some(address),
class_hash,
abi: None,
tag: tag.clone(),
writes,
..Default::default()
},
naming::get_filename_from_tag(&tag),
Expand Down
23 changes: 7 additions & 16 deletions crates/dojo-world/src/manifest/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::fs;

use camino::Utf8PathBuf;
Expand Down Expand Up @@ -33,26 +32,18 @@ pub struct BaseManifest {
pub struct DeploymentManifest {
pub world: Manifest<WorldContract>,
pub base: Manifest<Class>,
// NOTE: `writes` field in contracts is of String but we read the values which are resource hashes
// from the events, so needs to be handled accordingly
pub contracts: Vec<Manifest<DojoContract>>,
pub models: Vec<Manifest<DojoModel>>,
}

pub type ContractMetadata = HashMap<String, Operation>;
// bool represents authorization has been done for that contract
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
pub struct DeploymentMetadata {
pub world_metadata: bool,
// tag -> ContractMetadata
pub contracts: HashMap<String, ContractMetadata>,
}

#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub enum Operation {
#[default]
Grant,
Revoke,
}
// #[derive(Default, Clone, Debug, Serialize, Deserialize)]
// #[cfg_attr(test, derive(PartialEq))]
// pub struct DeploymentMetadata {
// pub world_metadata: bool,
// }

#[derive(Default, Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
Expand Down
2 changes: 2 additions & 0 deletions crates/dojo-world/src/migration/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct ContractDiff {
pub base_class_hash: Felt,
pub remote_class_hash: Option<Felt>,
pub init_calldata: Vec<String>,
pub local_writes: Vec<String>,
pub remote_writes: Vec<String>,
}

impl StateDiff for ContractDiff {
Expand Down
12 changes: 12 additions & 0 deletions crates/dojo-world/src/migration/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ impl WorldDiff {
.map(|r| *r.inner.class_hash())
}),
init_calldata: contract.inner.init_calldata.clone(),
local_writes: contract.inner.writes.clone(),
remote_writes: remote
.as_ref()
.and_then(|m| {
m.contracts
.iter()
.find(|r| r.inner.class_hash() == contract.inner.class_hash())
.map(|r| r.inner.writes.clone())
})
.unwrap_or_default(),
}
})
.collect::<Vec<_>>();
Expand All @@ -92,6 +102,8 @@ impl WorldDiff {
base_class_hash: *local.base.inner.class_hash(),
remote_class_hash: remote.map(|m| *m.world.inner.class_hash()),
init_calldata: vec![],
local_writes: vec![],
remote_writes: vec![],
};

let mut diff = WorldDiff { world, base, contracts, models };
Expand Down
Loading

0 comments on commit 571aca1

Please sign in to comment.