-
Notifications
You must be signed in to change notification settings - Fork 335
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
Validate new public key in Account #989
Changes from all commits
86551cd
45f9fa3
49ea133
3030a9e
9deb8e9
8b29077
d2dfac2
3e088ce
3809f54
6af8ece
e6be71d
0086ab5
71656d5
88a13a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,12 +6,14 @@ | |
/// The Account component enables contracts to behave as accounts. | ||
#[starknet::component] | ||
mod AccountComponent { | ||
use core::hash::{HashStateExTrait, HashStateTrait}; | ||
use openzeppelin::account::interface; | ||
use openzeppelin::account::utils::{MIN_TRANSACTION_VERSION, QUERY_VERSION, QUERY_OFFSET}; | ||
use openzeppelin::account::utils::{execute_calls, is_valid_stark_signature}; | ||
use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; | ||
use openzeppelin::introspection::src5::SRC5Component::SRC5; | ||
use openzeppelin::introspection::src5::SRC5Component; | ||
use poseidon::PoseidonTrait; | ||
use starknet::account::Call; | ||
use starknet::get_caller_address; | ||
use starknet::get_contract_address; | ||
|
@@ -155,11 +157,20 @@ mod AccountComponent { | |
/// Requirements: | ||
/// | ||
/// - The caller must be the contract itself. | ||
/// - The signature must be valid for the new owner. | ||
/// | ||
/// Emits an `OwnerRemoved` event. | ||
fn set_public_key(ref self: ComponentState<TContractState>, new_public_key: felt252) { | ||
/// Emits both an `OwnerRemoved` and an `OwnerAdded` event. | ||
fn set_public_key( | ||
ref self: ComponentState<TContractState>, | ||
new_public_key: felt252, | ||
signature: Span<felt252> | ||
) { | ||
self.assert_only_self(); | ||
self.emit(OwnerRemoved { removed_owner_guid: self.Account_public_key.read() }); | ||
|
||
let current_owner = self.Account_public_key.read(); | ||
self.assert_valid_new_owner(current_owner, new_public_key, signature); | ||
|
||
self.emit(OwnerRemoved { removed_owner_guid: current_owner }); | ||
self._set_public_key(new_public_key); | ||
} | ||
} | ||
|
@@ -191,8 +202,12 @@ mod AccountComponent { | |
self.Account_public_key.read() | ||
} | ||
|
||
fn setPublicKey(ref self: ComponentState<TContractState>, newPublicKey: felt252) { | ||
PublicKey::set_public_key(ref self, newPublicKey); | ||
fn setPublicKey( | ||
ref self: ComponentState<TContractState>, | ||
newPublicKey: felt252, | ||
signature: Span<felt252> | ||
) { | ||
PublicKey::set_public_key(ref self, newPublicKey, signature); | ||
} | ||
} | ||
|
||
|
@@ -218,6 +233,31 @@ mod AccountComponent { | |
assert(self == caller, Errors::UNAUTHORIZED); | ||
} | ||
|
||
/// Validates that `new_owner` accepted the ownership of the contract. | ||
/// | ||
/// WARNING: This function assumes that `current_owner` is the current owner of the contract, and | ||
/// does not validate this assumption. | ||
/// | ||
/// Requirements: | ||
/// | ||
/// - The signature must be valid for the new owner. | ||
fn assert_valid_new_owner( | ||
self: @ComponentState<TContractState>, | ||
current_owner: felt252, | ||
new_owner: felt252, | ||
signature: Span<felt252> | ||
) { | ||
let message_hash = PoseidonTrait::new() | ||
.update_with('StarkNet Message') | ||
.update_with('accept_ownership') | ||
.update_with(get_contract_address()) | ||
.update_with(current_owner) | ||
Comment on lines
+250
to
+254
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we include the domain separator in compliance with SNIP-12? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And I assume you don't think it's worth it to include the account and the new owner in the message as Marto initially proposed?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this particular case, I don't think SNIP12 is suited, because we don't really need to sign a struct with nonces and so on, we just need to verify that the signer exists, and for that something like EIP 191 (version 0x45) should suffice. If we were to be SNIP 12 compliant, we would need to add a domain separator, with SNIP12Metadata, and that seems like an overhead for the use case. The proposed format is to avoid clashes with transactions by prefixing "StarkNet Message", and accept_ownership to scope the message, and then we have the account address and the previous owner, so the user is signing who will be receiving the ownership from. To me adding the new owner to the signature is redundant since the signature already enforces the new pub key. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Notice that, the use case is to avoid transferring the ownership to an invalid public key. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good points! |
||
.finalize(); | ||
|
||
let is_valid = is_valid_stark_signature(message_hash, new_owner, signature); | ||
assert(is_valid, Errors::INVALID_SIGNATURE); | ||
} | ||
|
||
/// Validates the signature for the current transaction. | ||
/// Returns the short string `VALID` if valid, otherwise it reverts. | ||
fn validate_transaction(self: @ComponentState<TContractState>) -> felt252 { | ||
|
@@ -300,17 +340,25 @@ mod AccountComponent { | |
PublicKey::get_public_key(self) | ||
} | ||
|
||
fn set_public_key(ref self: ComponentState<TContractState>, new_public_key: felt252) { | ||
PublicKey::set_public_key(ref self, new_public_key); | ||
fn set_public_key( | ||
ref self: ComponentState<TContractState>, | ||
new_public_key: felt252, | ||
signature: Span<felt252> | ||
) { | ||
PublicKey::set_public_key(ref self, new_public_key, signature); | ||
} | ||
|
||
// IPublicKeyCamel | ||
fn getPublicKey(self: @ComponentState<TContractState>) -> felt252 { | ||
PublicKeyCamel::getPublicKey(self) | ||
} | ||
|
||
fn setPublicKey(ref self: ComponentState<TContractState>, newPublicKey: felt252) { | ||
PublicKeyCamel::setPublicKey(ref self, newPublicKey); | ||
fn setPublicKey( | ||
ref self: ComponentState<TContractState>, | ||
newPublicKey: felt252, | ||
signature: Span<felt252> | ||
) { | ||
PublicKeyCamel::setPublicKey(ref self, newPublicKey, signature); | ||
} | ||
|
||
// ISRC5 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm good call with using js. The Cairo highlighting looks a little bland here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, we should at some point improve the highlights extension.