Skip to content

Commit

Permalink
Support grant/revoke writer/owner
Browse files Browse the repository at this point in the history
  • Loading branch information
tarrencev committed Feb 21, 2024
1 parent 64992c9 commit 86c60ce
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 74 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.

1 change: 1 addition & 0 deletions bin/sozo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dojo-bindgen.workspace = true
dojo-lang.workspace = true
dojo-types.workspace = true
dojo-world = { workspace = true, features = [ "contracts", "metadata", "migration" ] }
futures.workspace = true
notify = "6.0.1"
notify-debouncer-mini = "0.3.0"
scarb-ui.workspace = true
Expand Down
124 changes: 121 additions & 3 deletions bin/sozo/src/commands/auth.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::str::FromStr;

use anyhow::Result;
use clap::{Args, Subcommand};
use dojo_world::contracts::cairo_utils;
use dojo_world::metadata::dojo_metadata_from_workspace;
use scarb::core::Config;
use starknet_crypto::FieldElement;

use super::options::account::AccountOptions;
use super::options::starknet::StarknetOptions;
Expand All @@ -15,17 +19,131 @@ pub struct AuthArgs {
pub command: AuthCommand,
}

#[derive(Debug, Clone)]

Check warning on line 22 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L22

Added line #L22 was not covered by tests
pub struct ModelContract {
pub model: FieldElement,
pub contract: FieldElement,
}

impl FromStr for ModelContract {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split(',').collect();

Check warning on line 32 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L31-L32

Added lines #L31 - L32 were not covered by tests

let (model, contract_part) = match parts.as_slice() {
[model, contract] => (model.to_string(), *contract),
_ => anyhow::bail!(
"Model and contract address are expected to be comma separated: `sozo auth writer \
model_name,0x1234`"
),

Check warning on line 39 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L34-L39

Added lines #L34 - L39 were not covered by tests
};

let model = cairo_utils::str_to_felt(&model)
.map_err(|_| anyhow::anyhow!("Invalid model name: {}", model))?;

Check warning on line 43 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L42-L43

Added lines #L42 - L43 were not covered by tests

let contract = FieldElement::from_hex_be(contract_part)
.map_err(|_| anyhow::anyhow!("Invalid contract address: {}", contract_part))?;

Check warning on line 46 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L45-L46

Added lines #L45 - L46 were not covered by tests

Ok(ModelContract { model, contract })
}

Check warning on line 49 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L48-L49

Added lines #L48 - L49 were not covered by tests
}

#[derive(Debug, Clone)]

Check warning on line 52 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L52

Added line #L52 was not covered by tests
pub enum ResourceType {
Contract(String),
Model(FieldElement),
}

#[derive(Debug, Clone)]

Check warning on line 58 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L58

Added line #L58 was not covered by tests
pub struct OwnerResource {
pub owner: FieldElement,
pub resource: ResourceType,
}

impl FromStr for OwnerResource {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split(',').collect();

Check warning on line 68 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L67-L68

Added lines #L67 - L68 were not covered by tests

let (owner_part, resource_part) = match parts.as_slice() {
[owner, resource] => (*owner, *resource),
_ => anyhow::bail!(
"Owner and resource are expected to be comma separated: `sozo auth owner \
0x1234,resource_type:resource_name`"
),

Check warning on line 75 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L70-L75

Added lines #L70 - L75 were not covered by tests
};

let owner = FieldElement::from_hex_be(owner_part)
.map_err(|_| anyhow::anyhow!("Invalid owner address: {}", owner_part))?;

Check warning on line 79 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L78-L79

Added lines #L78 - L79 were not covered by tests

let resource_parts: Vec<&str> = resource_part.split(':').collect();
let resource = match resource_parts.as_slice() {
["contract", name] => ResourceType::Contract(name.to_string()),
["model", name] => {
let model = cairo_utils::str_to_felt(name)
.map_err(|_| anyhow::anyhow!("Invalid model name: {}", name))?;
ResourceType::Model(model)

Check warning on line 87 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L81-L87

Added lines #L81 - L87 were not covered by tests
}
_ => anyhow::bail!(
"Resource is expected to be in the format `resource_type:resource_name`: `sozo \
auth owner 0x1234,resource_type:resource_name`"
),

Check warning on line 92 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L89-L92

Added lines #L89 - L92 were not covered by tests
};

Ok(OwnerResource { owner, resource })
}

Check warning on line 96 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L95-L96

Added lines #L95 - L96 were not covered by tests
}

#[derive(Debug, Subcommand)]
pub enum AuthCommand {
#[command(about = "Auth a system with the given calldata.")]
pub enum AuthKind {
#[command(about = "Grant a contract permission to write to a model.")]
Writer {
#[arg(num_args = 1..)]
#[arg(required = true)]
#[arg(value_name = "model,contract_address")]
#[arg(help = "A list of models and contract address to grant write access to. Comma \
separated values to indicate model name and contract address e.g. \
model_name,0x1234 model_name,0x1111 ")]
models_contracts: Vec<String>,
models_contracts: Vec<ModelContract>,

Check warning on line 109 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L109

Added line #L109 was not covered by tests
},
#[command(about = "Grant ownership of a resource.")]
Owner {
#[arg(num_args = 1..)]
#[arg(required = true)]
#[arg(value_name = "owner,resource")]
#[arg(help = "A list of owners and resources to grant ownership to. Comma separated \
values to indicate owner address and resouce e.g. \
0x1234,contract:contract_name 0x1111,contract:contract_address, \
0xbeef,model:model_name")]
owners_resources: Vec<OwnerResource>,

Check warning on line 120 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L120

Added line #L120 was not covered by tests
},
}

#[derive(Debug, Subcommand)]

Check warning on line 124 in bin/sozo/src/commands/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/auth.rs#L124

Added line #L124 was not covered by tests
pub enum AuthCommand {
#[command(about = "Grant an auth role.")]
Grant {
#[command(subcommand)]
kind: AuthKind,

#[command(flatten)]
world: WorldOptions,

#[command(flatten)]
starknet: StarknetOptions,

#[command(flatten)]
account: AccountOptions,

#[command(flatten)]
transaction: TransactionOptions,
},
#[command(about = "Revoke an auth role.")]
Revoke {
#[command(subcommand)]
kind: AuthKind,

#[command(flatten)]
world: WorldOptions,
Expand Down
4 changes: 4 additions & 0 deletions bin/sozo/src/commands/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use starknet::core::types::FieldElement;
use super::options::account::AccountOptions;
use super::options::starknet::StarknetOptions;
use super::options::transaction::TransactionOptions;
use super::options::world::WorldOptions;
use crate::ops::execute;

#[derive(Debug, Args)]
Expand All @@ -31,6 +32,9 @@ pub struct ExecuteArgs {
#[command(flatten)]
pub account: AccountOptions,

#[command(flatten)]
pub world: WorldOptions,

#[command(flatten)]
pub transaction: TransactionOptions,
}
Expand Down
92 changes: 55 additions & 37 deletions bin/sozo/src/ops/auth.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,75 @@
use anyhow::{Context, Result};
use dojo_world::contracts::cairo_utils;
use dojo_world::contracts::world::WorldContract;
use dojo_world::metadata::Environment;
use dojo_world::utils::TransactionWaiter;
use starknet::accounts::Account;
use starknet::core::types::FieldElement;

use crate::commands::auth::AuthCommand;
use super::get_contract_address;
use crate::commands::auth::{AuthCommand, AuthKind, ResourceType};

pub async fn execute(command: AuthCommand, env_metadata: Option<Environment>) -> Result<()> {
match command {
AuthCommand::Writer { models_contracts, world, starknet, account, transaction } => {
let world_address = world.address(env_metadata.as_ref())?;
let provider = starknet.provider(env_metadata.as_ref())?;
AuthCommand::Grant { kind, world, starknet, account, transaction } => match kind {
AuthKind::Writer { models_contracts } => {
let world_address = world.address(env_metadata.as_ref())?;
let provider = starknet.provider(env_metadata.as_ref())?;

Check warning on line 15 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L12-L15

Added lines #L12 - L15 were not covered by tests

let account = account.account(&provider, env_metadata.as_ref()).await?;
let world = WorldContract::new(world_address, &account);
let account = account.account(&provider, env_metadata.as_ref()).await?;
let world = WorldContract::new(world_address, &account);

Check warning on line 18 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L17-L18

Added lines #L17 - L18 were not covered by tests

let mut calls = vec![];
let calls = models_contracts
.iter()
.map(|mc| world.grant_writer_getcall(&mc.model, &mc.contract.into()))
.collect();

Check warning on line 23 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L20-L23

Added lines #L20 - L23 were not covered by tests

for mc in models_contracts {
let parts: Vec<&str> = mc.split(',').collect();
let res = account
.execute(calls)
.send()
.await
.with_context(|| "Failed to send transaction")?;

Check warning on line 29 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L25-L29

Added lines #L25 - L29 were not covered by tests

let (model, contract_part) = match parts.as_slice() {
[model, contract] => (model.to_string(), *contract),
_ => anyhow::bail!(
"Model and contract address are expected to be comma separated: `sozo \
auth writer model_name,0x1234`"
),
};
if transaction.wait {
let receipt = TransactionWaiter::new(res.transaction_hash, &provider).await?;
println!("{}", serde_json::to_string_pretty(&receipt)?);
} else {
println!("Transaction hash: {:#x}", res.transaction_hash);
}

Check warning on line 36 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L31-L36

Added lines #L31 - L36 were not covered by tests
}
AuthKind::Owner { owners_resources } => {
let world_address = world.address(env_metadata.as_ref())?;
let provider = starknet.provider(env_metadata.as_ref())?;

Check warning on line 40 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L38-L40

Added lines #L38 - L40 were not covered by tests

let contract = FieldElement::from_hex_be(contract_part)
.map_err(|_| anyhow::anyhow!("Invalid contract address: {}", contract_part))?;
let account = account.account(&provider, env_metadata.as_ref()).await?;
let world = WorldContract::new(world_address, &account);

Check warning on line 43 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L42-L43

Added lines #L42 - L43 were not covered by tests

calls.push(
world
.grant_writer_getcall(&cairo_utils::str_to_felt(&model)?, &contract.into()),
);
}
let mut calls = Vec::new();

Check warning on line 45 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L45

Added line #L45 was not covered by tests

for or in owners_resources {
let resource = match &or.resource {
ResourceType::Model(name) => *name,
ResourceType::Contract(name_or_address) => {
get_contract_address(&world, name_or_address.clone()).await?

Check warning on line 51 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L47-L51

Added lines #L47 - L51 were not covered by tests
}
};

calls.push(world.grant_owner_getcall(&or.owner.into(), &resource));

Check warning on line 55 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L55

Added line #L55 was not covered by tests
}

let res = account
.execute(calls)
.send()
.await
.with_context(|| "Failed to send transaction")?;

Check warning on line 62 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L58-L62

Added lines #L58 - L62 were not covered by tests

let res = account
.execute(calls)
.send()
.await
.with_context(|| "Failed to send transaction")?;

if transaction.wait {
let receipt = TransactionWaiter::new(res.transaction_hash, &provider).await?;
println!("{}", serde_json::to_string_pretty(&receipt)?);
} else {
println!("Transaction hash: {:#x}", res.transaction_hash);
if transaction.wait {
let receipt = TransactionWaiter::new(res.transaction_hash, &provider).await?;
println!("{}", serde_json::to_string_pretty(&receipt)?);
} else {
println!("Transaction hash: {:#x}", res.transaction_hash);
}

Check warning on line 69 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L64-L69

Added lines #L64 - L69 were not covered by tests
}
}
},
_ => todo!(),

Check warning on line 72 in bin/sozo/src/ops/auth.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/auth.rs#L72

Added line #L72 was not covered by tests
}

Ok(())
Expand Down
42 changes: 8 additions & 34 deletions bin/sozo/src/ops/execute.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,24 @@
use anyhow::{Context, Result};
use dojo_world::contracts::world::WorldContract;
use dojo_world::metadata::Environment;
use dojo_world::migration::strategy::generate_salt;
use dojo_world::utils::TransactionWaiter;
use starknet::accounts::{Account, Call};
use starknet::core::types::{BlockId, BlockTag, FieldElement, FunctionCall};
use starknet::core::utils::{get_contract_address, get_selector_from_name};
use starknet::macros::selector;
use starknet::providers::Provider;
use starknet::core::utils::get_selector_from_name;

use super::get_contract_address;
use crate::commands::execute::ExecuteArgs;

pub async fn execute(args: ExecuteArgs, env_metadata: Option<Environment>) -> Result<()> {
let ExecuteArgs { contract, entrypoint, calldata, starknet, account, transaction } = args;
let ExecuteArgs { contract, entrypoint, calldata, starknet, world, account, transaction } =
args;

Check warning on line 13 in bin/sozo/src/ops/execute.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/execute.rs#L12-L13

Added lines #L12 - L13 were not covered by tests

let provider = starknet.provider(env_metadata.as_ref())?;

let contract_address = if contract.starts_with("0x") {
FieldElement::from_hex_be(&contract)?
} else {
let world_address = env_metadata
.as_ref()
.and_then(|env| env.world_address.as_ref())
.cloned()
.ok_or_else(|| anyhow::anyhow!("No World Address found"))?;

let contract_class_hash = provider
.call(
FunctionCall {
contract_address: FieldElement::from_hex_be(&world_address).unwrap(),
entry_point_selector: selector!("base"),
calldata: [].to_vec(),
},
BlockId::Tag(BlockTag::Latest),
)
.await?;

get_contract_address(
generate_salt(&contract),
contract_class_hash[0],
&[],
FieldElement::from_hex_be(&world_address).unwrap(),
)
};

let account = account.account(&provider, env_metadata.as_ref()).await?;
let world_address = world.address(env_metadata.as_ref())?;
let world = WorldContract::new(world_address, &account);

Check warning on line 19 in bin/sozo/src/ops/execute.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/execute.rs#L18-L19

Added lines #L18 - L19 were not covered by tests

let contract_address = get_contract_address(&world, contract).await?;

Check warning on line 21 in bin/sozo/src/ops/execute.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/execute.rs#L21

Added line #L21 was not covered by tests
let res = account
.execute(vec![Call {
calldata,
Expand Down
23 changes: 23 additions & 0 deletions bin/sozo/src/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
use anyhow::Result;
use dojo_world::contracts::world::WorldContract;
use dojo_world::migration::strategy::generate_salt;
use starknet::accounts::ConnectedAccount;
use starknet::core::types::FieldElement;

pub mod auth;
pub mod events;
pub mod execute;
pub mod migration;
pub mod model;
pub mod register;

pub async fn get_contract_address<A: ConnectedAccount + Sync>(
world: &WorldContract<A>,
name_or_address: String,
) -> Result<FieldElement> {
if name_or_address.starts_with("0x") {
FieldElement::from_hex_be(&name_or_address).map_err(anyhow::Error::from)

Check warning on line 19 in bin/sozo/src/ops/mod.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/mod.rs#L14-L19

Added lines #L14 - L19 were not covered by tests
} else {
let contract_class_hash = world.base().call().await?;
Ok(starknet::core::utils::get_contract_address(
generate_salt(&name_or_address),
contract_class_hash.into(),
&[],
world.address,
))

Check warning on line 27 in bin/sozo/src/ops/mod.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/mod.rs#L21-L27

Added lines #L21 - L27 were not covered by tests
}
}

Check warning on line 29 in bin/sozo/src/ops/mod.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/ops/mod.rs#L29

Added line #L29 was not covered by tests

0 comments on commit 86c60ce

Please sign in to comment.