Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit b5b8e5e

Browse files
authored
Merge branch 'main' into implement_testing_syscalls
2 parents fbde559 + 0dda64e commit b5b8e5e

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use starknet::account::Call;
2+
3+
mod SUPPORTED_TX_VERSION {
4+
const DEPLOY_ACCOUNT: felt252 = 1;
5+
const DECLARE: felt252 = 2;
6+
const INVOKE: felt252 = 1;
7+
}
8+
9+
#[starknet::interface]
10+
trait IAccount<T> {
11+
fn is_valid_signature(self: @T, hash: felt252, signature: Array<felt252>) -> felt252;
12+
fn supports_interface(self: @T, interface_id: felt252) -> bool;
13+
fn public_key(self: @T) -> felt252;
14+
}
15+
16+
#[starknet::contract]
17+
mod Account {
18+
use super::{Call, IAccount, SUPPORTED_TX_VERSION};
19+
use starknet::{get_caller_address, call_contract_syscall, get_tx_info, VALIDATED};
20+
use zeroable::Zeroable;
21+
use array::{ArrayTrait, SpanTrait};
22+
use ecdsa::check_ecdsa_signature;
23+
use box::BoxTrait;
24+
use result::ResultTrait;
25+
26+
const SIMULATE_TX_VERSION_OFFSET: felt252 = 340282366920938463463374607431768211456; // 2**128
27+
const SRC6_TRAIT_ID: felt252 = 1270010605630597976495846281167968799381097569185364931397797212080166453709; // hash of SNIP-6 trait
28+
29+
#[storage]
30+
struct Storage {
31+
public_key: felt252
32+
}
33+
34+
#[constructor]
35+
fn constructor(ref self: ContractState, public_key: felt252) {
36+
self.public_key.write(public_key);
37+
}
38+
39+
#[external(v0)]
40+
impl AccountImpl of IAccount<ContractState> {
41+
fn is_valid_signature(self: @ContractState, hash: felt252, signature: Array<felt252>) -> felt252 {
42+
let is_valid = self.is_valid_signature_bool(hash, signature.span());
43+
if is_valid { VALIDATED } else { 0 }
44+
}
45+
46+
fn supports_interface(self: @ContractState, interface_id: felt252) -> bool {
47+
interface_id == SRC6_TRAIT_ID
48+
}
49+
50+
fn public_key(self: @ContractState) -> felt252 {
51+
self.public_key.read()
52+
}
53+
}
54+
55+
#[external(v0)]
56+
#[generate_trait]
57+
impl ProtocolImpl of ProtocolTrait {
58+
fn __execute__(ref self: ContractState, calls: Array<Call>) -> Array<Span<felt252>> {
59+
let arr = ArrayTrait::new();
60+
panic_with_felt252('panic');
61+
arr
62+
//self.only_protocol();
63+
// self.only_supported_tx_version(SUPPORTED_TX_VERSION::INVOKE);
64+
// self.execute_multiple_calls(calls)
65+
}
66+
67+
fn __validate__(self: @ContractState, calls: Array<Call>) -> felt252 {
68+
panic_with_felt252('panic');
69+
0
70+
// self.only_protocol();
71+
// self.only_supported_tx_version(SUPPORTED_TX_VERSION::INVOKE);
72+
// self.validate_transaction()
73+
}
74+
75+
fn __validate_declare__(self: @ContractState, class_hash: felt252) -> felt252 {
76+
self.only_protocol();
77+
self.only_supported_tx_version(SUPPORTED_TX_VERSION::DECLARE);
78+
self.validate_transaction()
79+
}
80+
81+
fn __validate_deploy__(self: @ContractState, class_hash: felt252, salt: felt252, public_key: felt252) -> felt252 {
82+
self.only_protocol();
83+
self.only_supported_tx_version(SUPPORTED_TX_VERSION::DEPLOY_ACCOUNT);
84+
self.validate_transaction()
85+
}
86+
}
87+
88+
#[generate_trait]
89+
impl PrivateImpl of PrivateTrait {
90+
fn only_protocol(self: @ContractState) {
91+
let sender = get_caller_address();
92+
assert(sender.is_zero(), 'Account: invalid caller');
93+
}
94+
95+
fn is_valid_signature_bool(self: @ContractState, hash: felt252, signature: Span<felt252>) -> bool {
96+
let is_valid_length = signature.len() == 2_u32;
97+
98+
if !is_valid_length {
99+
return false;
100+
}
101+
102+
check_ecdsa_signature(
103+
hash, self.public_key.read(), *signature.at(0_u32), *signature.at(1_u32)
104+
)
105+
}
106+
107+
fn validate_transaction(self: @ContractState) -> felt252 {
108+
let tx_info = get_tx_info().unbox();
109+
let tx_hash = tx_info.transaction_hash;
110+
let signature = tx_info.signature;
111+
112+
let is_valid = self.is_valid_signature_bool(tx_hash, signature);
113+
assert(is_valid, 'Account: Incorrect tx signature');
114+
VALIDATED
115+
}
116+
117+
fn execute_single_call(self: @ContractState, call: Call) -> Span<felt252> {
118+
let Call{to, selector, calldata} = call;
119+
call_contract_syscall(to, selector, calldata.span()).unwrap()
120+
}
121+
122+
fn execute_multiple_calls(self: @ContractState, mut calls: Array<Call>) -> Array<Span<felt252>> {
123+
let mut res = ArrayTrait::new();
124+
loop {
125+
match calls.pop_front() {
126+
Option::Some(call) => {
127+
let _res = self.execute_single_call(call);
128+
res.append(_res);
129+
},
130+
Option::None(_) => {
131+
break ();
132+
},
133+
};
134+
};
135+
res
136+
}
137+
138+
fn only_supported_tx_version(self: @ContractState, supported_tx_version: felt252) {
139+
let tx_info = get_tx_info().unbox();
140+
let version = tx_info.version;
141+
assert(
142+
version == supported_tx_version ||
143+
version == SIMULATE_TX_VERSION_OFFSET + supported_tx_version,
144+
'Account: Unsupported tx version'
145+
);
146+
}
147+
}
148+
}

tests/account_panic.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use std::{collections::HashMap, sync::Arc};
2+
3+
use cairo_vm::felt::Felt252;
4+
use starknet_in_rust::{
5+
core::contract_address::compute_casm_class_hash,
6+
definitions::{block_context::BlockContext, constants::TRANSACTION_VERSION},
7+
services::api::contract_classes::compiled_class::CompiledClass,
8+
state::{cached_state::CachedState, in_memory_state_reader::InMemoryStateReader},
9+
transaction::{InvokeFunction, Transaction},
10+
utils::{calculate_sn_keccak, Address},
11+
CasmContractClass,
12+
};
13+
14+
#[test]
15+
fn account_panic() {
16+
let account_data = include_bytes!("../starknet_programs/cairo2/account_panic.casm");
17+
let contract_data = include_bytes!("../starknet_programs/cairo2/contract_a.casm");
18+
19+
let account_contract_class: CasmContractClass = serde_json::from_slice(account_data).unwrap();
20+
let account_class_hash = compute_casm_class_hash(&account_contract_class)
21+
.unwrap()
22+
.to_be_bytes();
23+
24+
let contract_class: CasmContractClass = serde_json::from_slice(contract_data).unwrap();
25+
let contract_class_hash_felt = compute_casm_class_hash(&contract_class).unwrap();
26+
let contract_class_hash = contract_class_hash_felt.to_be_bytes();
27+
28+
let account_address = Address(1111.into());
29+
let contract_address = Address(0000.into());
30+
let nonce = 0.into();
31+
32+
let block_context = BlockContext::default();
33+
34+
let mut contract_class_cache = HashMap::new();
35+
36+
contract_class_cache.insert(
37+
account_class_hash,
38+
CompiledClass::Casm(Arc::new(account_contract_class)),
39+
);
40+
contract_class_cache.insert(
41+
contract_class_hash,
42+
CompiledClass::Casm(Arc::new(contract_class.clone())),
43+
);
44+
45+
let mut state_reader = InMemoryStateReader::default();
46+
state_reader
47+
.address_to_class_hash_mut()
48+
.insert(account_address.clone(), account_class_hash);
49+
state_reader
50+
.address_to_nonce_mut()
51+
.insert(account_address.clone(), nonce);
52+
state_reader
53+
.address_to_class_hash_mut()
54+
.insert(contract_address.clone(), contract_class_hash);
55+
state_reader
56+
.address_to_nonce_mut()
57+
.insert(contract_address, 1.into());
58+
let mut state = CachedState::new(Arc::new(state_reader), contract_class_cache);
59+
60+
let selector = Felt252::from_bytes_be(&calculate_sn_keccak(b"__execute__"));
61+
62+
// arguments of contract_a contract
63+
// calldata is a Vec of Call, which is
64+
/*
65+
#[derive(Drop, Serde)]
66+
struct Call {
67+
to: ContractAddress,
68+
selector: felt252,
69+
calldata: Array<felt252>
70+
}
71+
*/
72+
let selector_contract = &contract_class
73+
.entry_points_by_type
74+
.external
75+
.get(0)
76+
.unwrap()
77+
.selector;
78+
// calldata of contract_a is 1 value.
79+
let calldata: Vec<_> = [
80+
1.into(),
81+
contract_class_hash_felt,
82+
selector_contract.into(),
83+
1.into(),
84+
2.into(),
85+
]
86+
.to_vec();
87+
88+
// set up remaining structures
89+
90+
let invoke = InvokeFunction::new(
91+
account_address,
92+
Felt252::new(selector),
93+
0,
94+
TRANSACTION_VERSION.clone(),
95+
calldata,
96+
vec![],
97+
block_context.starknet_os_config().chain_id().clone(),
98+
Some(0.into()),
99+
)
100+
.unwrap();
101+
102+
let tx = Transaction::InvokeFunction(invoke);
103+
let exec_info = tx
104+
.execute(&mut state, &block_context, u128::MAX)
105+
.expect("failed to invoke");
106+
let call_info = exec_info.call_info.as_ref().unwrap();
107+
108+
assert_eq!(exec_info.revert_error, None);
109+
110+
// 482670963043u128 == 'panic'
111+
assert_eq!(call_info.retdata[0], 482670963043u128.into());
112+
assert!(call_info.failure_flag);
113+
}

0 commit comments

Comments
 (0)