Skip to content

Commit

Permalink
feat : Added Signature verification (#176)
Browse files Browse the repository at this point in the history
* feat : Added Signature verification

* feat : Added deployed contract instance link

* feat : Changed the topic from ch01 to ch02
  • Loading branch information
Akashneelesh authored Feb 21, 2024
1 parent 32b8abc commit b2c8e07
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 30 deletions.
4 changes: 4 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ version = "0.1.0"
name = "custom_type_serde"
version = "0.1.0"

[[package]]
name = "ecdsa_verification"
version = "0.1.0"

[[package]]
name = "erc20"
version = "0.1.0"
Expand Down
1 change: 1 addition & 0 deletions listings/advanced-concepts/ecdsa_verification/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
6 changes: 6 additions & 0 deletions listings/advanced-concepts/ecdsa_verification/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "ecdsa_verification"
version = "0.1.0"
13 changes: 13 additions & 0 deletions listings/advanced-concepts/ecdsa_verification/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "ecdsa_verification"
version.workspace = true
edition = '2023_11'


[dependencies]
starknet.workspace = true

[scripts]
test.workspace = true

[[target.starknet-contract]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// How to Sign and Verify
// # Signing
// 1. Create message to sign
// 2. Hash the message
// 3. Sign the hash (off chain, keep your private key secret)

use core::starknet::eth_address::EthAddress;
use starknet::secp256_trait::{Signature};
#[starknet::interface]
trait IVerifySignature<TContractState> {
fn get_signature(self: @TContractState, r: u256, s: u256, v: u32,) -> Signature;
fn verify_eth_signature(
self: @TContractState, eth_address: EthAddress, msg_hash: u256, r: u256, s: u256, v: u32,
);
fn recover_public_key(
self: @TContractState, eth_address: EthAddress, msg_hash: u256, r: u256, s: u256, v: u32
);
}

#[starknet::contract]
mod verifySignature {
use super::IVerifySignature;
use core::starknet::eth_address::EthAddress;
use starknet::get_caller_address;
use starknet::secp256_trait;
use starknet::secp256k1::{Secp256k1Point};
use starknet::{SyscallResult, SyscallResultTrait};
use starknet::secp256_trait::{
Secp256Trait, Secp256PointTrait, Signature, signature_from_vrs, recover_public_key,
is_signature_entry_valid
};
use core::traits::{TryInto, Into};
use starknet::eth_signature::{verify_eth_signature, public_key_point_to_eth_address};

#[storage]
struct Storage {
msg_hash: u256,
signature: Signature,
eth_address: EthAddress,
}

#[abi(embed_v0)]
impl VerifySignature of IVerifySignature<ContractState> {
/// This function returns the signature struct for the given parameters.
///
/// # Arguments
///
/// * `r` - The R component of the signature.
/// * `s` - The S component of the signature.
/// * `v` - The V component of the signature.
///
/// # Returns
///
/// * `Signature` - The signature struct.
fn get_signature(self: @ContractState, r: u256, s: u256, v: u32,) -> Signature {
// Create a Signature object from the given v, r, and s values.
let signature: Signature = signature_from_vrs(v, r, s);
signature
}


/// Verifies an Ethereum signature.
///
/// # Arguments
///
/// * `eth_address` - The Ethereum address to verify the signature against.
/// * `msg_hash` - The hash of the message that was signed.
/// * `r` - The R component of the signature.
/// * `s` - The S component of the signature.
/// * `v` - The V component of the signature.
fn verify_eth_signature(
self: @ContractState, eth_address: EthAddress, msg_hash: u256, r: u256, s: u256, v: u32
) {
let signature = self.get_signature(r, s, v);
verify_eth_signature(:msg_hash, :signature, :eth_address);
}

/// Recovers the public key from an Ethereum signature and verifies that it matches the given Ethereum address.
///
/// # Arguments
///
/// * `eth_address` - The Ethereum address to verify the signature against.
/// * `msg_hash` - The hash of the message that was signed.
/// * `r` - The R component of the signature.
/// * `s` - The S component of the signature.
/// * `v` - The V component of the signature.
fn recover_public_key(
self: @ContractState, eth_address: EthAddress, msg_hash: u256, r: u256, s: u256, v: u32
) {
let signature = self.get_signature(r, s, v);
let public_key_point = recover_public_key::<Secp256k1Point>(msg_hash, signature)
.unwrap();
let calculated_eth_address = public_key_point_to_eth_address(:public_key_point);
assert(calculated_eth_address == eth_address, 'Invalid Address');
}
}
}

4 changes: 4 additions & 0 deletions listings/advanced-concepts/ecdsa_verification/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod ecdsa_verification;

#[cfg(test)]
mod tests;
41 changes: 41 additions & 0 deletions listings/advanced-concepts/ecdsa_verification/src/tests.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#[cfg(test)]
mod tests {
use starknet::secp256_trait::{
Secp256Trait, Secp256PointTrait, Signature, signature_from_vrs, recover_public_key,
is_signature_entry_valid
};

use starknet::EthAddress;
use starknet::secp256k1::{Secp256k1Point};
use core::traits::{TryInto, Into};
use starknet::eth_signature::{verify_eth_signature, public_key_point_to_eth_address};


fn get_message_and_signature() -> (u256, Signature, EthAddress) {
let msg_hash = 0x546ec3fa4f7d3308931816fafd47fa297afe9ac9a09651f77acc13c05a84734f;
let r = 0xc0f30bcef72974dedaf165cf7848a83b0b9eb6a65167a14643df96698d753efb;
let s = 0x7f189e3cb5eb992d8cd26e287a13e900326b87f58da2b7fb48fbd3977e3cab1c;
let v = 27;

let eth_address = 0x5F04693482cfC121FF244cB3c3733aF712F9df02_u256.into();
let signature: Signature = signature_from_vrs(v, r, s);

(msg_hash, signature, eth_address)
}

#[test]
#[available_gas(100000000)]
fn test_verify_eth_signature() {
let (msg_hash, signature, eth_address) = get_message_and_signature();
verify_eth_signature(:msg_hash, :signature, :eth_address);
}

#[test]
#[available_gas(100000000)]
fn test_secp256k1_recover_public_key() {
let (msg_hash, signature, eth_address) = get_message_and_signature();
let public_key_point = recover_public_key::<Secp256k1Point>(msg_hash, signature).unwrap();
let calculated_eth_address = public_key_point_to_eth_address(:public_key_point);
assert(calculated_eth_address == eth_address, 'Invalid Address');
}
}
62 changes: 32 additions & 30 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,35 @@ Summary
# Getting Started

<!-- - [Local environment setup](./ch00/env_setup.md) -->
- [Basics of a Starknet contract](./ch00/basics/introduction.md)
- [Storage](./ch00/basics/storage.md)
- [Constructor](./ch00/basics/constructor.md)
- [Variables](./ch00/basics/variables.md)
- [Visibility and Mutability](./ch00/basics/visibility-mutability.md)
- [Counter Example](./ch00/basics/counter.md)
- [Mappings](./ch00/basics/mappings.md)
- [Errors](./ch00/basics/errors.md)
- [Events](./ch00/basics/events.md)
- [Syscalls](./ch00/basics/syscalls.md)
- [Strings and ByteArrays](./ch00/basics/bytearrays-strings.md)
- [Storing Custom Types](./ch00/basics/storing-custom-types.md)
- [Custom types in entrypoints](./ch00/basics/custom-types-in-entrypoints.md)
- [Documentation](./ch00/basics/documentation.md)
- [Deploy and interact with contracts](./ch00/interacting/interacting.md)
- [Contract interfaces and Traits generation](./ch00/interacting/interfaces-traits.md)
- [Calling other contracts](./ch00/interacting/calling_other_contracts.md)
- [Factory pattern](./ch00/interacting/factory.md)
- [Testing contracts](./ch00/testing/contract-testing.md)
- [Cairo cheatsheet](./ch00/cairo_cheatsheet/cairo_cheatsheet.md)
- [Felt](./ch00/cairo_cheatsheet/felt.md)
- [LegacyMap](./ch00/cairo_cheatsheet/mapping.md)
- [Arrays](./ch00/cairo_cheatsheet/arrays.md)
- [Loop](./ch00/cairo_cheatsheet/loop.md)
- [Match](./ch00/cairo_cheatsheet/match.md)
- [Tuples](./ch00/cairo_cheatsheet/tuples.md)
- [Struct](./ch00/cairo_cheatsheet/struct.md)
- [Type casting](./ch00/cairo_cheatsheet/type_casting.md)

- [Basics of a Starknet contract](./ch00/basics/introduction.md)
- [Storage](./ch00/basics/storage.md)
- [Constructor](./ch00/basics/constructor.md)
- [Variables](./ch00/basics/variables.md)
- [Visibility and Mutability](./ch00/basics/visibility-mutability.md)
- [Counter Example](./ch00/basics/counter.md)
- [Mappings](./ch00/basics/mappings.md)
- [Errors](./ch00/basics/errors.md)
- [Events](./ch00/basics/events.md)
- [Syscalls](./ch00/basics/syscalls.md)
- [Strings and ByteArrays](./ch00/basics/bytearrays-strings.md)
- [Storing Custom Types](./ch00/basics/storing-custom-types.md)
- [Custom types in entrypoints](./ch00/basics/custom-types-in-entrypoints.md)
- [Documentation](./ch00/basics/documentation.md)
- [Deploy and interact with contracts](./ch00/interacting/interacting.md)
- [Contract interfaces and Traits generation](./ch00/interacting/interfaces-traits.md)
- [Calling other contracts](./ch00/interacting/calling_other_contracts.md)
- [Factory pattern](./ch00/interacting/factory.md)
- [Testing contracts](./ch00/testing/contract-testing.md)
- [Cairo cheatsheet](./ch00/cairo_cheatsheet/cairo_cheatsheet.md)
- [Felt](./ch00/cairo_cheatsheet/felt.md)
- [LegacyMap](./ch00/cairo_cheatsheet/mapping.md)
- [Arrays](./ch00/cairo_cheatsheet/arrays.md)
- [Loop](./ch00/cairo_cheatsheet/loop.md)
- [Match](./ch00/cairo_cheatsheet/match.md)
- [Tuples](./ch00/cairo_cheatsheet/tuples.md)
- [Struct](./ch00/cairo_cheatsheet/struct.md)
- [Type casting](./ch00/cairo_cheatsheet/type_casting.md)

# Components

Expand All @@ -60,9 +61,10 @@ Summary
- [Storing Arrays](./ch02/storing_arrays.md)
- [Struct as mapping key](./ch02/struct-mapping-key.md)
- [Hashing](./ch02/hashing.md)
<!-- Hidden until #123 is solved -->
<!-- - [Hash Solidity Compatible](./ch02/hash-solidity-compatible.md) -->
<!-- Hidden until #123 is solved -->
<!-- - [Hash Solidity Compatible](./ch02/hash-solidity-compatible.md) -->
- [Optimisations](./ch02/optimisations/optimisations.md)
- [Storage Optimisations](./ch02/optimisations/store_using_packing.md)
- [List](./ch02/list.md)
- [Plugins](./ch02/plugins.md)
- [Signature Verification](./ch02/signature_verification.md)
1 change: 1 addition & 0 deletions src/ch01/signature_verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Signature Verification
10 changes: 10 additions & 0 deletions src/ch02/signature_verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# ECDSA Verification

This is the Cairo adaptation of the [Solidity by example Verifying Signature](https://solidity-by-example.org/signature/)
Messages can be signed off chain and then verified on chain using a smart contract.

```rust
{{#include ../../listings/advanced-concepts/ecdsa_verification/src/ecdsa_verification.cairo}}
```

[Click here to interact with the deployed contract on Voyager](https://goerli.voyager.online/contract/0x070bad62072d2a30fd08a95e9de99828955cfcffc40eac8adf3b21e9970590be#writeContract)

0 comments on commit b2c8e07

Please sign in to comment.