From b2c8e07bb25b5b8ea50742a96cdfdccf22386fb2 Mon Sep 17 00:00:00 2001 From: Akashneelesh <66639153+Akashneelesh@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:04:53 +0530 Subject: [PATCH] feat : Added Signature verification (#176) * feat : Added Signature verification * feat : Added deployed contract instance link * feat : Changed the topic from ch01 to ch02 --- Scarb.lock | 4 + .../ecdsa_verification/.gitignore | 1 + .../ecdsa_verification/Scarb.lock | 6 ++ .../ecdsa_verification/Scarb.toml | 13 +++ .../src/ecdsa_verification.cairo | 98 +++++++++++++++++++ .../ecdsa_verification/src/lib.cairo | 4 + .../ecdsa_verification/src/tests.cairo | 41 ++++++++ src/SUMMARY.md | 62 ++++++------ src/ch01/signature_verification.md | 1 + src/ch02/signature_verification.md | 10 ++ 10 files changed, 210 insertions(+), 30 deletions(-) create mode 100644 listings/advanced-concepts/ecdsa_verification/.gitignore create mode 100644 listings/advanced-concepts/ecdsa_verification/Scarb.lock create mode 100644 listings/advanced-concepts/ecdsa_verification/Scarb.toml create mode 100644 listings/advanced-concepts/ecdsa_verification/src/ecdsa_verification.cairo create mode 100644 listings/advanced-concepts/ecdsa_verification/src/lib.cairo create mode 100644 listings/advanced-concepts/ecdsa_verification/src/tests.cairo create mode 100644 src/ch01/signature_verification.md create mode 100644 src/ch02/signature_verification.md diff --git a/Scarb.lock b/Scarb.lock index 562e3451..570a20ef 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -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" diff --git a/listings/advanced-concepts/ecdsa_verification/.gitignore b/listings/advanced-concepts/ecdsa_verification/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/listings/advanced-concepts/ecdsa_verification/.gitignore @@ -0,0 +1 @@ +target diff --git a/listings/advanced-concepts/ecdsa_verification/Scarb.lock b/listings/advanced-concepts/ecdsa_verification/Scarb.lock new file mode 100644 index 00000000..f287d8c3 --- /dev/null +++ b/listings/advanced-concepts/ecdsa_verification/Scarb.lock @@ -0,0 +1,6 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "ecdsa_verification" +version = "0.1.0" diff --git a/listings/advanced-concepts/ecdsa_verification/Scarb.toml b/listings/advanced-concepts/ecdsa_verification/Scarb.toml new file mode 100644 index 00000000..9c069f75 --- /dev/null +++ b/listings/advanced-concepts/ecdsa_verification/Scarb.toml @@ -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]] diff --git a/listings/advanced-concepts/ecdsa_verification/src/ecdsa_verification.cairo b/listings/advanced-concepts/ecdsa_verification/src/ecdsa_verification.cairo new file mode 100644 index 00000000..fef18549 --- /dev/null +++ b/listings/advanced-concepts/ecdsa_verification/src/ecdsa_verification.cairo @@ -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 { + 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 { + /// 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::(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'); + } + } +} + diff --git a/listings/advanced-concepts/ecdsa_verification/src/lib.cairo b/listings/advanced-concepts/ecdsa_verification/src/lib.cairo new file mode 100644 index 00000000..3cfe89c7 --- /dev/null +++ b/listings/advanced-concepts/ecdsa_verification/src/lib.cairo @@ -0,0 +1,4 @@ +mod ecdsa_verification; + +#[cfg(test)] +mod tests; diff --git a/listings/advanced-concepts/ecdsa_verification/src/tests.cairo b/listings/advanced-concepts/ecdsa_verification/src/tests.cairo new file mode 100644 index 00000000..0a062284 --- /dev/null +++ b/listings/advanced-concepts/ecdsa_verification/src/tests.cairo @@ -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::(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'); + } +} diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 85359fb4..e39387b4 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -7,34 +7,35 @@ Summary # Getting Started - - [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 @@ -60,9 +61,10 @@ Summary - [Storing Arrays](./ch02/storing_arrays.md) - [Struct as mapping key](./ch02/struct-mapping-key.md) - [Hashing](./ch02/hashing.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) diff --git a/src/ch01/signature_verification.md b/src/ch01/signature_verification.md new file mode 100644 index 00000000..afd2f847 --- /dev/null +++ b/src/ch01/signature_verification.md @@ -0,0 +1 @@ +# Signature Verification diff --git a/src/ch02/signature_verification.md b/src/ch02/signature_verification.md new file mode 100644 index 00000000..3cbd8dc6 --- /dev/null +++ b/src/ch02/signature_verification.md @@ -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)