Skip to content

Commit

Permalink
move SNIP12 to components
Browse files Browse the repository at this point in the history
  • Loading branch information
pscott committed Oct 4, 2024
1 parent f50679c commit df42704
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 79 deletions.
85 changes: 48 additions & 37 deletions starknet/src/authenticators/stark_sig.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,23 @@ mod StarkSigAuthenticator {
use starknet::{ContractAddress, info};
use sx::interfaces::{ISpaceDispatcher, ISpaceDispatcherTrait};
use sx::types::{Strategy, IndexedStrategy, UserAddress, Choice};
use sx::utils::SNIP12;
use sx::utils::snip12::SNIP12Component;

component!(path: SNIP12Component, storage: snip12, event: SNIP12Event);

impl SNIP12InternalImpl = SNIP12Component::InternalImpl<ContractState>;

#[storage]
struct Storage {
_used_salts: LegacyMap::<(ContractAddress, felt252), bool>
_used_salts: LegacyMap::<(ContractAddress, felt252), bool>,
#[substorage(v0)]
snip12: SNIP12Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
SNIP12Event: SNIP12Component::Event
}

#[abi(embed_v0)]
Expand All @@ -102,17 +114,17 @@ mod StarkSigAuthenticator {
) {
assert(!self._used_salts.read((author, salt)), 'Salt Already Used');

let state = SNIP12::unsafe_new_contract_state();
SNIP12::InternalImpl::verify_propose_sig(
@state,
signature,
space,
author,
metadata_uri.span(),
@execution_strategy,
user_proposal_validation_params.span(),
salt
);
self
.snip12
.verify_propose_sig(
signature,
space,
author,
metadata_uri.span(),
@execution_strategy,
user_proposal_validation_params.span(),
salt
);

self._used_salts.write((author, salt), true);
ISpaceDispatcher { contract_address: space }
Expand All @@ -136,17 +148,17 @@ mod StarkSigAuthenticator {
) {
// No need to check salts here, as double voting is prevented by the space itself.

let state = SNIP12::unsafe_new_contract_state();
SNIP12::InternalImpl::verify_vote_sig(
@state,
signature,
space,
voter,
proposal_id,
choice,
user_voting_strategies.span(),
metadata_uri.span()
);
self
.snip12
.verify_vote_sig(
signature,
space,
voter,
proposal_id,
choice,
user_voting_strategies.span(),
metadata_uri.span()
);

ISpaceDispatcher { contract_address: space }
.vote(
Expand All @@ -170,17 +182,17 @@ mod StarkSigAuthenticator {
) {
assert(!self._used_salts.read((author, salt)), 'Salt Already Used');

let state = SNIP12::unsafe_new_contract_state();
SNIP12::InternalImpl::verify_update_proposal_sig(
@state,
signature,
space,
author,
proposal_id,
@execution_strategy,
metadata_uri.span(),
salt
);
self
.snip12
.verify_update_proposal_sig(
signature,
space,
author,
proposal_id,
@execution_strategy,
metadata_uri.span(),
salt
);

self._used_salts.write((author, salt), true);
ISpaceDispatcher { contract_address: space }
Expand All @@ -191,7 +203,6 @@ mod StarkSigAuthenticator {
}
#[constructor]
fn constructor(ref self: ContractState, name: felt252, version: felt252) {
let mut state = SNIP12::unsafe_new_contract_state();
SNIP12::InternalImpl::initializer(ref state, name, version);
self.snip12.initializer(name, version);
}
}
1 change: 0 additions & 1 deletion starknet/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ mod utils {
use single_slot_proof::SingleSlotProof;

mod snip12;
use snip12::SNIP12;

mod struct_hash;
use struct_hash::StructHash;
Expand Down
92 changes: 51 additions & 41 deletions starknet/src/utils/snip12.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// SNIP12 style typed data signing implementation.
/// See here for more info: https://community.starknet.io/t/snip-off-chain-signatures-a-la-eip712/98029
#[starknet::contract]
mod SNIP12 {
#[starknet::component]
pub mod SNIP12Component {
use starknet::ContractAddress;
use openzeppelin::account::interface::{AccountABIDispatcher, AccountABIDispatcherTrait};
use sx::types::{Strategy, IndexedStrategy, Choice};
Expand All @@ -12,19 +12,31 @@ mod SNIP12 {
};

#[storage]
struct Storage {
pub struct Storage {
_domain_hash: felt252
}

// TODO : comment
#[event]
#[derive(Drop, starknet::Event)]
pub enum Event {
NoOpEvent: NoOpEvent
}

#[derive(Drop, starknet::Event)]
struct NoOpEvent {}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn initializer(ref self: ContractState, name: felt252, version: felt252) {
self._domain_hash.write(InternalImpl::get_domain_hash(name, version));
pub impl InternalImpl<
TContractState, +HasComponent<TContractState>
> of InternalTrait<TContractState> {
fn initializer(ref self: ComponentState<TContractState>, name: felt252, version: felt252) {
self._domain_hash.write(get_domain_hash(name, version));
}

/// Verifies the signature of the propose calldata.
fn verify_propose_sig(
self: @ContractState,
self: @ComponentState<TContractState>,
signature: Array<felt252>,
space: ContractAddress,
author: ContractAddress,
Expand All @@ -43,12 +55,12 @@ mod SNIP12 {
salt
);

InternalImpl::verify_signature(digest, signature, author);
verify_signature(digest, signature, author);
}

/// Verifies the signature of the vote calldata.
fn verify_vote_sig(
self: @ContractState,
self: @ComponentState<TContractState>,
signature: Array<felt252>,
space: ContractAddress,
voter: ContractAddress,
Expand All @@ -61,12 +73,12 @@ mod SNIP12 {
.get_vote_digest(
space, voter, proposal_id, choice, user_voting_strategies, metadata_uri
);
InternalImpl::verify_signature(digest, signature, voter);
verify_signature(digest, signature, voter);
}

/// Verifies the signature of the update proposal calldata.
fn verify_update_proposal_sig(
self: @ContractState,
self: @ComponentState<TContractState>,
signature: Array<felt252>,
space: ContractAddress,
author: ContractAddress,
Expand All @@ -79,12 +91,12 @@ mod SNIP12 {
.get_update_proposal_digest(
space, author, proposal_id, execution_strategy, metadata_uri, salt
);
InternalImpl::verify_signature(digest, signature, author);
verify_signature(digest, signature, author);
}

/// Returns the digest of the propose calldata.
fn get_propose_digest(
self: @ContractState,
self: @ComponentState<TContractState>,
space: ContractAddress,
author: ContractAddress,
metadata_uri: Span<felt252>,
Expand All @@ -105,7 +117,7 @@ mod SNIP12 {

/// Returns the digest of the vote calldata.
fn get_vote_digest(
self: @ContractState,
self: @ComponentState<TContractState>,
space: ContractAddress,
voter: ContractAddress,
proposal_id: u256,
Expand All @@ -127,7 +139,7 @@ mod SNIP12 {

/// Returns the digest of the update proposal calldata.
fn get_update_proposal_digest(
self: @ContractState,
self: @ComponentState<TContractState>,
space: ContractAddress,
author: ContractAddress,
proposal_id: u256,
Expand All @@ -146,20 +158,9 @@ mod SNIP12 {
self.hash_typed_data(encoded_data.span().struct_hash(), author)
}


/// Returns the domain hash of the contract.
fn get_domain_hash(name: felt252, version: felt252) -> felt252 {
let mut encoded_data = array![];
DOMAIN_TYPEHASH.serialize(ref encoded_data);
name.serialize(ref encoded_data);
version.serialize(ref encoded_data);
starknet::get_tx_info().unbox().chain_id.serialize(ref encoded_data);
encoded_data.span().struct_hash()
}

/// Hashes typed data according to the starknet equiavalent to the EIP-712 specification.
fn hash_typed_data(
self: @ContractState, message_hash: felt252, signer: ContractAddress
self: @ComponentState<TContractState>, message_hash: felt252, signer: ContractAddress
) -> felt252 {
let mut encoded_data = array![];
STARKNET_MESSAGE.serialize(ref encoded_data);
Expand All @@ -168,21 +169,30 @@ mod SNIP12 {
message_hash.serialize(ref encoded_data);
encoded_data.span().struct_hash()
}
}

/// Verifies the signature of a message by calling the account contract.
fn verify_signature(digest: felt252, signature: Array<felt252>, account: ContractAddress) {
// Only SNIP-6 compliant accounts are supported.
assert(
AccountABIDispatcher { contract_address: account }
.supports_interface(ERC165_ACCOUNT_INTERFACE_ID) == true,
'Invalid Account'
);
/// Returns the domain hash of the contract.
fn get_domain_hash(name: felt252, version: felt252) -> felt252 {
let mut encoded_data = array![];
DOMAIN_TYPEHASH.serialize(ref encoded_data);
name.serialize(ref encoded_data);
version.serialize(ref encoded_data);
starknet::get_tx_info().unbox().chain_id.serialize(ref encoded_data);
encoded_data.span().struct_hash()
}
/// Verifies the signature of a message by calling the account contract.
fn verify_signature(digest: felt252, signature: Array<felt252>, account: ContractAddress) {
// Only SNIP-6 compliant accounts are supported.
assert(
AccountABIDispatcher { contract_address: account }
.supports_interface(ERC165_ACCOUNT_INTERFACE_ID) == true,
'Invalid Account'
);

assert(
AccountABIDispatcher { contract_address: account }
.is_valid_signature(digest, signature) == 'VALID',
'Invalid Signature'
);
}
assert(
AccountABIDispatcher { contract_address: account }
.is_valid_signature(digest, signature) == 'VALID',
'Invalid Signature'
);
}
}

0 comments on commit df42704

Please sign in to comment.