Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: stark sig auth #467

Merged
merged 27 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ethereum/lib
22 changes: 22 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended"
],
"plugins": [
"prettier",
"@typescript-eslint"
],
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"no-console": "off",
"prettier/prettier": "error",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/no-explicit-any": "off"
pscott marked this conversation as resolved.
Show resolved Hide resolved
}
}
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2
}
11 changes: 11 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { JestConfigWithTsJest } from 'ts-jest';

const jestConfig: JestConfigWithTsJest = {
// [...]
// Replace `ts-jest` with the preset you want to use
// from the above list
preset: 'ts-jest',
roots: ['<rootDir>/starknet/tests'],
};

export default jestConfig;
23 changes: 22 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,26 @@
"description": "Cairo 1 implementation of the Snapshot X Protocol",
"repository": "https://github.com/snapshot-labs/sx-starknet-2.git",
"author": "Snapshot Labs",
"license": "MIT"
"license": "MIT",
"scripts": {
"format": "eslint . --ext .ts --fix"
},
"devDependencies": {
"@types/jest": "^29.5.3",
"@types/node": "^20.4.5",
"@typescript-eslint/parser": "^6.2.1",
"dotenv": "^16.3.1",
"eslint": "^8.46.0",
"eslint-plugin-prettier": "^5.0.0",
"fs": "^0.0.1-security",
"jest": "^29.6.2",
"prettier": "^3.0.0",
"starknet": "^5.14.1",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.1.6"
},
"dependencies": {
"@typescript-eslint/eslint-plugin": "^6.2.1"
}
}
3 changes: 2 additions & 1 deletion starknet/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ version = "0.1.0"
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest

[[target.starknet-contract]]
allowed-libfuncs-list.name = "all"
allowed-libfuncs-list.name = "audited"
casm = true

[dependencies]
starknet = ">=2.1.0-rc1"
Expand Down
2 changes: 2 additions & 0 deletions starknet/src/authenticators.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ mod vanilla;
mod eth_tx;

mod eth_sig;

mod stark_sig;
147 changes: 147 additions & 0 deletions starknet/src/authenticators/stark_sig.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use starknet::ContractAddress;
use sx::types::{Strategy, IndexedStrategy, Choice};

#[starknet::interface]
trait IStarkSigAuthenticator<TContractState> {
fn authenticate_propose(
ref self: TContractState,
signature: Array<felt252>,
target: ContractAddress,
author: ContractAddress,
execution_strategy: Strategy,
user_proposal_validation_params: Array<felt252>,
salt: felt252,
account_type: felt252
);
fn authenticate_vote(
ref self: TContractState,
signature: Array<felt252>,
target: ContractAddress,
voter: ContractAddress,
proposal_id: u256,
choice: Choice,
user_voting_strategies: Array<IndexedStrategy>,
account_type: felt252
);
fn authenticate_update_proposal(
ref self: TContractState,
signature: Array<felt252>,
target: ContractAddress,
author: ContractAddress,
proposal_id: u256,
execution_strategy: Strategy,
salt: felt252,
account_type: felt252
);
}

#[starknet::contract]
mod StarkSigAuthenticator {
use super::IStarkSigAuthenticator;
use starknet::{ContractAddress, info};
use core::array::{ArrayTrait, SpanTrait};
use serde::Serde;
use sx::space::space::{ISpaceDispatcher, ISpaceDispatcherTrait};
use sx::types::{Strategy, IndexedStrategy, UserAddress, Choice};
use sx::utils::stark_eip712;

#[storage]
struct Storage {
_domain_hash: felt252,
_used_salts: LegacyMap::<(ContractAddress, felt252), bool>
pscott marked this conversation as resolved.
Show resolved Hide resolved
pscott marked this conversation as resolved.
Show resolved Hide resolved
}

#[external(v0)]
impl StarkSigAuthenticator of IStarkSigAuthenticator<ContractState> {
fn authenticate_propose(
ref self: ContractState,
signature: Array<felt252>,
target: ContractAddress,
author: ContractAddress,
execution_strategy: Strategy,
user_proposal_validation_params: Array<felt252>,
salt: felt252,
account_type: felt252
) {
stark_eip712::verify_propose_sig(
self._domain_hash.read(),
signature,
target,
author,
@execution_strategy,
user_proposal_validation_params.span(),
salt,
account_type
);

self._used_salts.write((author, salt), true);
pscott marked this conversation as resolved.
Show resolved Hide resolved
ISpaceDispatcher {
contract_address: target
}
.propose(
UserAddress::Starknet(author),
execution_strategy,
user_proposal_validation_params
);
}

fn authenticate_vote(
ref self: ContractState,
signature: Array<felt252>,
target: ContractAddress,
voter: ContractAddress,
proposal_id: u256,
choice: Choice,
user_voting_strategies: Array<IndexedStrategy>,
account_type: felt252
) {
stark_eip712::verify_vote_sig(
self._domain_hash.read(),
signature,
target,
voter,
proposal_id,
choice,
user_voting_strategies.span(),
account_type
);
// No need to check salts here, as double voting is prevented by the space itself.

ISpaceDispatcher {
contract_address: target
}.vote(UserAddress::Starknet(voter), proposal_id, choice, user_voting_strategies);
}

fn authenticate_update_proposal(
ref self: ContractState,
signature: Array<felt252>,
target: ContractAddress,
author: ContractAddress,
proposal_id: u256,
execution_strategy: Strategy,
salt: felt252,
account_type: felt252
) {
stark_eip712::verify_update_proposal_sig(
self._domain_hash.read(),
signature,
target,
author,
proposal_id,
@execution_strategy,
salt,
account_type
);

self._used_salts.write((author, salt), true);
ISpaceDispatcher {
contract_address: target
}.update_proposal(UserAddress::Starknet(author), proposal_id, execution_strategy);
}
}
#[constructor]
fn constructor(ref self: ContractState, name: felt252, version: felt252) {
// TODO: store domain hash in stark_eip712 component once syntax is live.
self._domain_hash.write(stark_eip712::get_domain_hash(name, version));
}
}
5 changes: 5 additions & 0 deletions starknet/src/interfaces.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod i_voting_strategy;
mod i_execution_strategy;
mod i_proposal_validation_strategy;
mod i_account;

use i_voting_strategy::{IVotingStrategy, IVotingStrategyDispatcher, IVotingStrategyDispatcherTrait};
use i_execution_strategy::{
Expand All @@ -10,3 +11,7 @@ use i_proposal_validation_strategy::{
IProposalValidationStrategy, IProposalValidationStrategyDispatcher,
IProposalValidationStrategyDispatcherTrait
};
use i_account::{
AccountABI, AccountABIDispatcher, AccountABIDispatcherTrait, AccountCamelABI,
AccountCamelABIDispatcher, AccountCamelABIDispatcherTrait
};
33 changes: 33 additions & 0 deletions starknet/src/interfaces/i_account.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use array::ArrayTrait;
use array::SpanTrait;
use starknet::account::Call;
use starknet::ContractAddress;

#[starknet::interface]
trait AccountABI<TState> {
pscott marked this conversation as resolved.
Show resolved Hide resolved
fn __execute__(self: @TState, calls: Array<Call>) -> Array<Span<felt252>>;
fn __validate__(self: @TState, calls: Array<Call>) -> felt252;
fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252;
fn __validate_deploy__(
self: @TState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252
) -> felt252;
fn set_public_key(ref self: TState, new_public_key: felt252);
fn get_public_key(self: @TState) -> felt252;
fn is_valid_signature(self: @TState, hash: felt252, signature: Array<felt252>) -> felt252;
fn supports_interface(self: @TState, interface_id: felt252) -> bool;
}

// Entry points case-convention is enforced by the protocol
#[starknet::interface]
trait AccountCamelABI<TState> {
fn __execute__(self: @TState, calls: Array<Call>) -> Array<Span<felt252>>;
fn __validate__(self: @TState, calls: Array<Call>) -> felt252;
fn __validate_declare__(self: @TState, classHash: felt252) -> felt252;
fn __validate_deploy__(
self: @TState, classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252
) -> felt252;
fn setPublicKey(ref self: TState, newPublicKey: felt252);
fn getPublicKey(self: @TState) -> felt252;
fn isValidSignature(self: @TState, hash: felt252, signature: Array<felt252>) -> felt252;
fn supportsInterface(self: @TState, interfaceId: felt252) -> bool;
}
7 changes: 5 additions & 2 deletions starknet/src/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ mod constants;
mod felt_arr_to_uint_arr;
use felt_arr_to_uint_arr::Felt252ArrayIntoU256Array;

mod legacy_hash_eth_address;
use legacy_hash_eth_address::LegacyHashEthAddress;
mod legacy_hash;
use legacy_hash::{LegacyHashEthAddress, LegacyHashSpanFelt252};

mod math;

mod struct_hash;

mod single_slot_proof;

mod signatures;

mod stark_eip712;
30 changes: 30 additions & 0 deletions starknet/src/utils/constants.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,33 @@ const STRATEGY_TYPEHASH_LOW: u128 = 0x78d5506febfdb18580ea361801747e63;
// keccak256("IndexedStrategy(uint256 index,uint8[] params)")
const INDEXED_STRATEGY_TYPEHASH_HIGH: u128 = 0x894665428ec742c74109dc21d320d1ab;
const INDEXED_STRATEGY_TYPEHASH_LOW: u128 = 0x8b36195eec0090e913c01e7534729c74;


// ------ Stark Sig Constants ------

// 'StarkNet Message' as short string
const STARKNET_MESSAGE: felt252 = 0x537461726b4e6574204d657373616765;
pscott marked this conversation as resolved.
Show resolved Hide resolved
// H('StarkNetDomain(name:felt252,version:felt252,chainId:felt252,verifyingContract:ContractAddress)')
Orland0x marked this conversation as resolved.
Show resolved Hide resolved
const DOMAIN_TYPEHASH: felt252 = 0xa9974a36dee531bbc36aad5eeab4ade4df5ad388a296bb14d28ad4e9bf2164;
// H('Propose(space:ContractAddress,author:ContractAddress,executionStrategy:Strategy,
// userProposalValidationParams:felt*,salt:felt252)Strategy(address:felt252,params:felt*)')
const PROPOSE_TYPEHASH: felt252 = 0x1f8c9b1ab74c5990f89bac4c632dc405457352e22bbc7573a237989aa62cb60;
// H('Vote(space:ContractAddress,voter:ContractAddress,proposalId:u256,choice:felt252,userVotingStrategies:IndexedStrategy*)
// IndexedStrategy(index:felt252,params:felt*)u256(low:felt252,high:felt252)')
const VOTE_TYPEHASH: felt252 = 0x1845db28c74470cdaa3cf6a1c5017d013586d70ffee45b519a5670e23fe9512;
// H('UpdateProposal(space:ContractAddress,author:ContractAddress,proposalId:u256,executionStrategy:Strategy,
// salt:felt252)Strategy(address:felt252,params:felt*)u256(low:felt252,high:felt252)')
const UPDATE_PROPOSAL_TYPEHASH: felt252 =
0x222b737bd5f9ba595cf15e62d789e31a2e51c57c794a82802c94cd3925e7d49;
// H('Strategy(address:felt252,params:felt*)')
const STRATEGY_TYPEHASH: felt252 =
0x39154ec0efadcd0deffdfc2044cf45dd986d260e59c26d69564b50a18f40f6b;
// H('IndexedStrategy(index:felt252,params:felt*)')
const INDEXED_STRATEGY_TYPEHASH: felt252 =
0x1f464f3e668281a899c5f3fc74a009ccd1df05fd0b9331b0460dc3f8054f64c;
// H('u256(low:felt252,high:felt252)')
const U256_TYPEHASH: felt252 = 0x1094260a770342332e6a73e9256b901d484a438925316205b4b6ff25df4a97a;

// ------ ERC165 Interface Ids ------
const ERC165_ACCOUNT_INTERFACE_ID: felt252 = 0xa66bd575; // snake
pscott marked this conversation as resolved.
Show resolved Hide resolved
const ERC165_OLD_ACCOUNT_INTERFACE_ID: felt252 = 0x3943f10f; // camel
26 changes: 26 additions & 0 deletions starknet/src/utils/legacy_hash.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use hash::LegacyHash;
use traits::Into;
use array::SpanTrait;
use starknet::EthAddress;

impl LegacyHashEthAddress of LegacyHash<EthAddress> {
fn hash(state: felt252, value: EthAddress) -> felt252 {
LegacyHash::<felt252>::hash(state, value.into())
}
}

impl LegacyHashSpanFelt252 of LegacyHash<Span<felt252>> {
fn hash(state: felt252, mut value: Span<felt252>) -> felt252 {
let mut call_data_state: felt252 = 0;
loop {
match value.pop_front() {
Option::Some(item) => {
call_data_state = LegacyHash::hash(call_data_state, *item);
},
Option::None(_) => {
break call_data_state;
},
};
}
}
}
9 changes: 0 additions & 9 deletions starknet/src/utils/legacy_hash_eth_address.cairo

This file was deleted.

1 change: 1 addition & 0 deletions starknet/src/utils/signatures.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ impl KeccakTypeHashIndexedStrategyArray of KeccakTypeHash<Array<IndexedStrategy>
break ();
}
encoded_data.append(self.at(i).clone().hash());
i += 1;
pscott marked this conversation as resolved.
Show resolved Hide resolved
};
keccak::keccak_u256s_le_inputs(encoded_data.span())
}
Expand Down
Loading
Loading