Skip to content

Commit

Permalink
test: scarb library with template account contract (#813)
Browse files Browse the repository at this point in the history
  • Loading branch information
ICavlek authored Oct 26, 2024
1 parent 8dd10ba commit bd6c854
Show file tree
Hide file tree
Showing 22 changed files with 1,987 additions and 31 deletions.
1,763 changes: 1,745 additions & 18 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,15 @@ gloo-timers = { version = "0.3.0", features = ["futures"] }
[dev-dependencies]
anyhow = "1.0.89"
alloy-primitives = { version = "0.8.5", default-features = false }
chrono = "0.4.38"
katana-core = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.9" }
katana-executor = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.9" }
katana-node = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.9" }
katana-primitives = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.9" }
katana-rpc = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.9" }
katana-rpc-api = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.9" }
scarb = { git = "https://github.com/software-mansion/scarb/", tag = "v2.8.3" }
semver = { version = "1", features = ["serde"] }
wiremock = "0.6.2"

[patch.crates-io]
Expand Down
5 changes: 3 additions & 2 deletions tests/account_katana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ use beerus::{
TxnHash,
},
};
use common::constants::{
use starknet::constants::{
CLASS_HASH, COMPILED_ACCOUNT_CONTRACT_V2, COMPILED_ACCOUNT_CONTRACT_V3,
CONTRACT_ADDRESS, DECLARE_ACCOUNT_V2, DECLARE_ACCOUNT_V3, SENDER_ADDRESS,
};
use common::katana::Katana;
use starknet::katana::Katana;

mod common;
mod starknet;

async fn setup() -> (Katana, Client<Http>) {
let katana = Katana::init("http://127.0.0.1:0").await.unwrap();
Expand Down
4 changes: 2 additions & 2 deletions tests/account_mock.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use beerus::gen::{Address, BlockId, BlockTag, Felt, Rpc, TxnHash};

use common::{
use mock::{
dummies::{
declare_transaction, deploy_transaction, estimate_fee_transaction,
invoke_transaction,
Expand All @@ -15,7 +15,7 @@ use common::{
node::setup_client_with_mock_starknet_node,
};

mod common;
mod mock;

#[tokio::test]
async fn chain_id_test() {
Expand Down
4 changes: 4 additions & 0 deletions tests/common/err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ pub enum Error {
Json(#[from] serde_json::Error),
#[error("starknet api error: {0:?}")]
Api(#[from] starknet_api::StarknetApiError),
#[error("IO error: {0:?}")]
IO(#[from] std::io::Error),
#[error("Anyhow error: {0:?}")]
Anyhow(#[from] anyhow::Error),
}
5 changes: 0 additions & 5 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
pub mod constants;
pub mod ctx;
pub mod dummies;
pub mod err;
pub mod katana;
pub mod matchers;
pub mod node;
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions tests/mock/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod dummies;
pub mod matchers;
pub mod node;
File renamed without changes.
1 change: 1 addition & 0 deletions tests/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use beerus::{
};

mod common;
mod starknet;

use common::err::Error;

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions tests/common/constants.rs → tests/starknet/constants.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#[allow(dead_code)]
pub const COMPILED_ACCOUNT_CONTRACT_V2: &str =
include_str!("../clob/compiled_account_contract_v2.txt");
include_str!("./clob/compiled_account_contract_v2.txt");
#[allow(dead_code)]
pub const COMPILED_ACCOUNT_CONTRACT_V3: &str =
include_str!("../clob/compiled_account_contract_v3.txt");
include_str!("./clob/compiled_account_contract_v3.txt");
#[allow(dead_code)]
pub const DECLARE_ACCOUNT_V2: &str =
include_str!("../clob/declare_account_v2.txt");
include_str!("./clob/declare_account_v2.txt");
#[allow(dead_code)]
pub const DECLARE_ACCOUNT_V3: &str =
include_str!("../clob/declare_account_v3.txt");
include_str!("./clob/declare_account_v3.txt");

#[allow(dead_code)]
pub const CLASS_HASH: &str =
Expand Down
11 changes: 11 additions & 0 deletions tests/starknet/contract/account/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "account"
version = "0.1.0"
cairo-version = "2.5.4"

[dependencies]
starknet = ">=2.5.4"

[[target.starknet-contract]]
sierra = true
casm = true
149 changes: 149 additions & 0 deletions tests/starknet/contract/account/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use starknet::account::Call;

mod SUPPORTED_TX_VERSION {
const DEPLOY_ACCOUNT: felt252 = 1;
const DECLARE: felt252 = 2;
const INVOKE: felt252 = 1;
}

#[starknet::interface]
trait IAccount<T> {
fn is_valid_signature(self: @T, hash: felt252, signature: Array<felt252>) -> felt252;
fn supports_interface(self: @T, interface_id: felt252) -> bool;
fn public_key(self: @T) -> felt252;
fn id(self: @T) -> u128;
}

#[starknet::contract]
mod Account {
use super::{Call, IAccount, SUPPORTED_TX_VERSION};
use starknet::{get_caller_address, call_contract_syscall, get_tx_info, VALIDATED};
use zeroable::Zeroable;
use array::{ArrayTrait, SpanTrait};
use ecdsa::check_ecdsa_signature;
use box::BoxTrait;

const SIMULATE_TX_VERSION_OFFSET: felt252 = 340282366920938463463374607431768211456; // 2**128
const SRC6_TRAIT_ID: felt252 = 1270010605630597976495846281167968799381097569185364931397797212080166453709; // hash of SNIP-6 trait

#[storage]
struct Storage {
public_key: felt252,
id: u128
}

#[constructor]
fn constructor(ref self: ContractState, public_key: felt252) {
self.public_key.write(public_key);
self.id.write(<ID>);
}

#[abi(embed_v0)]
impl AccountImpl of IAccount<ContractState> {
fn is_valid_signature(self: @ContractState, hash: felt252, signature: Array<felt252>) -> felt252 {
let is_valid = self.is_valid_signature_bool(hash, signature.span());
if is_valid { VALIDATED } else { 0 }
}

fn supports_interface(self: @ContractState, interface_id: felt252) -> bool {
interface_id == SRC6_TRAIT_ID
}

fn public_key(self: @ContractState) -> felt252 {
self.public_key.read()
}

fn id(self: @ContractState) -> u128 {
self.id.read()
}
}

#[abi(per_item)]
#[generate_trait]
impl ProtocolImpl of ProtocolTrait {
fn __execute__(ref self: ContractState, calls: Array<Call>) -> Array<Span<felt252>> {
self.only_protocol();
self.only_supported_tx_version(SUPPORTED_TX_VERSION::INVOKE);
self.execute_multiple_calls(calls)
}

fn __validate__(self: @ContractState, calls: Array<Call>) -> felt252 {
self.only_protocol();
self.only_supported_tx_version(SUPPORTED_TX_VERSION::INVOKE);
self.validate_transaction()
}

fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 {
self.only_protocol();
self.only_supported_tx_version(SUPPORTED_TX_VERSION::DECLARE);
self.validate_transaction()
}

fn __validate_deploy__(self: @ContractState, class_hash: felt252, salt: felt252, public_key: felt252) -> felt252 {
self.only_protocol();
self.only_supported_tx_version(SUPPORTED_TX_VERSION::DEPLOY_ACCOUNT);
self.validate_transaction()
}
}

#[generate_trait]
impl PrivateImpl of PrivateTrait {
fn only_protocol(self: @ContractState) {
let sender = get_caller_address();
assert(sender.is_zero(), 'Account: invalid caller');
}

fn is_valid_signature_bool(self: @ContractState, hash: felt252, signature: Span<felt252>) -> bool {
let is_valid_length = signature.len() == 2_u32;

if !is_valid_length {
return false;
}

check_ecdsa_signature(
hash, self.public_key.read(), *signature.at(0_u32), *signature.at(1_u32)
)
}

fn validate_transaction(self: @ContractState) -> felt252 {
let tx_info = get_tx_info().unbox();
let tx_hash = tx_info.transaction_hash;
let signature = tx_info.signature;

let is_valid = self.is_valid_signature_bool(tx_hash, signature);
assert(is_valid, 'Account: Incorrect tx signature');
VALIDATED
}

fn execute_single_call(self: @ContractState, call: Call) -> Span<felt252> {
let Call{to, selector, calldata} = call;
call_contract_syscall(to, selector, calldata.into()).unwrap()
}

fn execute_multiple_calls(self: @ContractState, mut calls: Array<Call>) -> Array<Span<felt252>> {
let mut res = ArrayTrait::new();
loop {
match calls.pop_front() {
Option::Some(call) => {
let _res = self.execute_single_call(call);
res.append(_res);
},
Option::None(_) => {
break ();
},
};
};
res
}

fn only_supported_tx_version(self: @ContractState, supported_tx_version: felt252) {
let tx_info = get_tx_info().unbox();
let version = tx_info.version;
assert(
version == supported_tx_version ||
version == SIMULATE_TX_VERSION_OFFSET + supported_tx_version,
'Account: Unsupported tx version'
);
}
}
}
File renamed without changes.
4 changes: 4 additions & 0 deletions tests/starknet/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod constants;
pub mod katana;
pub mod scarb;
pub mod utils;
30 changes: 30 additions & 0 deletions tests/starknet/scarb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use anyhow::Error;
use scarb::{
core::{Config, PackageId, PackageName, SourceId, TargetKind},
ops::{self, CompileOpts, FeaturesOpts, FeaturesSelector},
};
use semver::Version;
use std::{fs::canonicalize, path::PathBuf};

#[allow(dead_code)]
pub fn compile(toml: String) -> Result<(), Error> {
let toml_absolute = canonicalize(PathBuf::from(toml))?;
let opts = CompileOpts {
include_target_kinds: vec![],
exclude_target_kinds: vec![TargetKind::new("test")],
include_target_names: vec![],
features: FeaturesOpts {
features: FeaturesSelector::Features(vec![]),
no_default_features: false,
},
};
let packages = vec![PackageId::new(
PackageName::new("account"),
Version::new(0, 1, 0),
SourceId::for_path(toml_absolute.to_str().unwrap().into())?,
)];
let config = Config::builder(toml_absolute.to_str().unwrap()).build()?;
let ws = ops::read_workspace(config.manifest_path(), &config)?;

scarb::ops::compile(packages, opts, &ws)
}
28 changes: 28 additions & 0 deletions tests/starknet/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::fs;

use anyhow::Error;
use chrono;

const SOURCE_LIB: &str = "./tests/starknet/contract/account/src/lib.cairo";
const SOURCE_SCARB: &str = "./tests/starknet/contract/account/Scarb.toml";

#[allow(dead_code)]
pub fn prepare_account() -> Result<String, Error> {
let now = chrono::offset::Local::now();
let id = now.format("%Y%m%y%H%M%S").to_string();
let target = "./target/account-".to_string() + &id;
let target_lib = target.clone() + "/src/lib.cairo";
let target_scarb = target.clone() + "/Scarb.toml";
let target_src = target.clone() + "/src";

fs::create_dir(target)?;
fs::create_dir(target_src)?;
fs::copy(SOURCE_LIB, target_lib.clone())?;
fs::copy(SOURCE_SCARB, target_scarb.clone())?;

let account_template = fs::read_to_string(target_lib.clone())?;
let account_new = account_template.replace("<ID>", &id);
fs::write(target_lib, account_new)?;

Ok(target_scarb)
}

0 comments on commit bd6c854

Please sign in to comment.