From c60afbe4499babe884b19adbb06f806e8b6e4914 Mon Sep 17 00:00:00 2001 From: julienbrs Date: Sat, 18 Nov 2023 19:16:52 +0100 Subject: [PATCH 1/5] docs + contracts + test --- .../hash_solidity_compatible/.gitignore | 1 + .../hash_solidity_compatible/Scarb.lock | 6 +++ .../hash_solidity_compatible/Scarb.toml | 8 ++++ .../src/hash_solidity_compatible.cairo | 41 +++++++++++++++++++ .../hash_solidity_compatible/src/lib.cairo | 4 ++ .../hash_solidity_compatible/src/tests.cairo | 33 +++++++++++++++ src/SUMMARY.md | 1 + src/ch02/hash-solidity-compatible.md | 11 +++++ src/ch02/hash_solidity_compatible.md | 1 + 9 files changed, 106 insertions(+) create mode 100644 listings/ch02-advanced-concepts/hash_solidity_compatible/.gitignore create mode 100644 listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.lock create mode 100644 listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.toml create mode 100644 listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo create mode 100644 listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo create mode 100644 listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo create mode 100644 src/ch02/hash-solidity-compatible.md create mode 100644 src/ch02/hash_solidity_compatible.md diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/.gitignore b/listings/ch02-advanced-concepts/hash_solidity_compatible/.gitignore new file mode 100644 index 00000000..1de56593 --- /dev/null +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.lock b/listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.lock new file mode 100644 index 00000000..195fb297 --- /dev/null +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.lock @@ -0,0 +1,6 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "hash_solidity_compatible" +version = "0.1.0" diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.toml b/listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.toml new file mode 100644 index 00000000..35d7a5fc --- /dev/null +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/Scarb.toml @@ -0,0 +1,8 @@ +[package] +name = "hash_solidity_compatible" +version = "0.1.0" + +[dependencies] +starknet = ">=2.3.0" + +[[target.starknet-contract]] \ No newline at end of file diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo new file mode 100644 index 00000000..cd1a5623 --- /dev/null +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo @@ -0,0 +1,41 @@ +#[starknet::interface] +trait ISolidityHashExample { + fn hash_data(ref self: TContractState, input_data: Span); + fn get_hashed_value(self: @TContractState) -> u256; +} + + +#[starknet::contract] +mod SolidityHashExample { + use keccak::{keccak_u256s_be_inputs}; + use array::Span; + + #[storage] + struct Storage { + solidity_hash_value: u256, + } + + #[abi(embed_v0)] + impl SolidityHashExample of super::ISolidityHashExample { + fn hash_data(ref self: ContractState, input_data: Span) { + let hashed = keccak_u256s_be_inputs(input_data); + + // Split the hashed value into two 128-bit segments + let low: u128 = hashed.low; + let high: u128 = hashed.high; + + // Reverse each 128-bit segment + let reversed_low = integer::u128_byte_reverse(low); + let reversed_high = integer::u128_byte_reverse(high); + + // Reverse merge the reversed segments back into a u256 value + let compatible_hash = u256 { low: reversed_high, high: reversed_low }; + + self.solidity_hash_value.write(compatible_hash); + } + + fn get_hashed_value(self: @ContractState) -> u256 { + self.solidity_hash_value.read() + } + } +} diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo new file mode 100644 index 00000000..1225e6c3 --- /dev/null +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo @@ -0,0 +1,4 @@ +mod hash_solidity_compatible; + +#[cfg(test)] +mod tests; diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo new file mode 100644 index 00000000..9f636da0 --- /dev/null +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo @@ -0,0 +1,33 @@ +mod tests { + use hash_solidity_compatible::{ + hash_solidity_compatible::{SolidityHashExample, ISolidityHashExample} + }; + use debug::PrintTrait; + + use starknet::{ + ContractAddress, get_contract_address, contract_address_const, call_contract_syscall, + testing::{set_contract_address} + }; + + fn setup() -> SolidityHashExample::ContractState { + let mut state = SolidityHashExample::contract_state_for_testing(); + let contract_address = contract_address_const::<0x1>(); + set_contract_address(contract_address); + state + } + + #[test] + #[available_gas(2000000000)] + fn get_same_hash_solidity() { + let mut state = setup(); + let mut array: Array = ArrayTrait::new(); + array.append(1); + + let hashExpected: u256 = 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6; + + let hash = state.hash_data(array.span()); + let hashReceived: u256 = state.get_hashed_value(); + + assert(hashReceived == hashExpected, 'hashReceived != hashExpected'); + } +} diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 85a76907..e47987d4 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -42,5 +42,6 @@ Summary - [Writing to any storage slot](./ch02/write_to_any_slot.md) - [Storing Arrays](./ch02/storing_arrays.md) - [Struct as mapping key](./ch02/struct-mapping-key.md) + - [Hash Solidity Compatible](./ch02/hash-solidity-compatible.md) - [Optimisations](./ch02/optimisations/optimisations.md) - [Storage Optimisations](./ch02/optimisations/store_using_packing.md) diff --git a/src/ch02/hash-solidity-compatible.md b/src/ch02/hash-solidity-compatible.md new file mode 100644 index 00000000..91a7e1f7 --- /dev/null +++ b/src/ch02/hash-solidity-compatible.md @@ -0,0 +1,11 @@ +# Hash Solidity Compatible + +This contract demonstrates Keccak hashing in Cairo to match Solidity's keccak256. While both use Keccak, their endianness differs: Cairo is little-endian, Solidity big-endian. The contract achieves compatibility by hashing in big-endian using `keccak_u256s_be_inputs`, and reversing the bytes of the result with `u128_byte_reverse`. + +For example: + +```rust +{{#include ../../listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo}} +``` + +Visit contract on [Voyager](#) or play with it in [Remix](#). diff --git a/src/ch02/hash_solidity_compatible.md b/src/ch02/hash_solidity_compatible.md new file mode 100644 index 00000000..7deeb06f --- /dev/null +++ b/src/ch02/hash_solidity_compatible.md @@ -0,0 +1 @@ +# Hash Solidity Compatible From 44ebf3a1f506000e9264e5a54c2650b954f673c0 Mon Sep 17 00:00:00 2001 From: julienbrs Date: Sat, 18 Nov 2023 19:33:02 +0100 Subject: [PATCH 2/5] renaming + link --- .../src/{hash_solidity_compatible.cairo => contract.cairo} | 0 .../hash_solidity_compatible/src/lib.cairo | 2 +- .../hash_solidity_compatible/src/tests.cairo | 2 +- src/ch02/hash-solidity-compatible.md | 4 ++-- src/ch02/hash_solidity_compatible.md | 1 - 5 files changed, 4 insertions(+), 5 deletions(-) rename listings/ch02-advanced-concepts/hash_solidity_compatible/src/{hash_solidity_compatible.cairo => contract.cairo} (100%) delete mode 100644 src/ch02/hash_solidity_compatible.md diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo similarity index 100% rename from listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo rename to listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo index 1225e6c3..11ada17a 100644 --- a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/lib.cairo @@ -1,4 +1,4 @@ -mod hash_solidity_compatible; +mod contract; #[cfg(test)] mod tests; diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo index 9f636da0..15132826 100644 --- a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo @@ -1,6 +1,6 @@ mod tests { use hash_solidity_compatible::{ - hash_solidity_compatible::{SolidityHashExample, ISolidityHashExample} + contract::{SolidityHashExample, ISolidityHashExample} }; use debug::PrintTrait; diff --git a/src/ch02/hash-solidity-compatible.md b/src/ch02/hash-solidity-compatible.md index 91a7e1f7..2f766066 100644 --- a/src/ch02/hash-solidity-compatible.md +++ b/src/ch02/hash-solidity-compatible.md @@ -5,7 +5,7 @@ This contract demonstrates Keccak hashing in Cairo to match Solidity's keccak256 For example: ```rust -{{#include ../../listings/ch02-advanced-concepts/hash_solidity_compatible/src/hash_solidity_compatible.cairo}} +{{#include ../../listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo}} ``` -Visit contract on [Voyager](#) or play with it in [Remix](#). +Visit contract on [Voyager](#) or play with it in [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/blob/main/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo). diff --git a/src/ch02/hash_solidity_compatible.md b/src/ch02/hash_solidity_compatible.md deleted file mode 100644 index 7deeb06f..00000000 --- a/src/ch02/hash_solidity_compatible.md +++ /dev/null @@ -1 +0,0 @@ -# Hash Solidity Compatible From 8afc2cf0e5949a04335788843111758f1f3979b2 Mon Sep 17 00:00:00 2001 From: julienbrs Date: Sat, 18 Nov 2023 19:45:04 +0100 Subject: [PATCH 3/5] fmt --- .../hash_solidity_compatible/src/tests.cairo | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo index 15132826..464ec571 100644 --- a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo @@ -1,7 +1,5 @@ mod tests { - use hash_solidity_compatible::{ - contract::{SolidityHashExample, ISolidityHashExample} - }; + use hash_solidity_compatible::{contract::{SolidityHashExample, ISolidityHashExample}}; use debug::PrintTrait; use starknet::{ From a270848faae6706def63f13fed1f8e66b0b91192 Mon Sep 17 00:00:00 2001 From: julienbrs Date: Tue, 21 Nov 2023 00:52:56 +0100 Subject: [PATCH 4/5] changes requested --- .../hash_solidity_compatible/src/contract.cairo | 15 ++++----------- .../hash_solidity_compatible/src/tests.cairo | 8 +++----- src/ch02/hash-solidity-compatible.md | 2 +- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo index cd1a5623..5f6f8d24 100644 --- a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo @@ -1,7 +1,6 @@ #[starknet::interface] trait ISolidityHashExample { - fn hash_data(ref self: TContractState, input_data: Span); - fn get_hashed_value(self: @TContractState) -> u256; + fn hash_data(ref self: TContractState, input_data: Span) -> u256; } @@ -11,13 +10,11 @@ mod SolidityHashExample { use array::Span; #[storage] - struct Storage { - solidity_hash_value: u256, - } + struct Storage {} #[abi(embed_v0)] impl SolidityHashExample of super::ISolidityHashExample { - fn hash_data(ref self: ContractState, input_data: Span) { + fn hash_data(ref self: ContractState, input_data: Span) -> u256 { let hashed = keccak_u256s_be_inputs(input_data); // Split the hashed value into two 128-bit segments @@ -31,11 +28,7 @@ mod SolidityHashExample { // Reverse merge the reversed segments back into a u256 value let compatible_hash = u256 { low: reversed_high, high: reversed_low }; - self.solidity_hash_value.write(compatible_hash); - } - - fn get_hashed_value(self: @ContractState) -> u256 { - self.solidity_hash_value.read() + compatible_hash } } } diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo index 464ec571..43709a54 100644 --- a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo @@ -21,11 +21,9 @@ mod tests { let mut array: Array = ArrayTrait::new(); array.append(1); - let hashExpected: u256 = 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6; + let hash_expected: u256 = 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6; + let hash_received: u256 = state.hash_data(array.span()); - let hash = state.hash_data(array.span()); - let hashReceived: u256 = state.get_hashed_value(); - - assert(hashReceived == hashExpected, 'hashReceived != hashExpected'); + assert(hash_received == hash_expected, 'hash_received != hash_expected'); } } diff --git a/src/ch02/hash-solidity-compatible.md b/src/ch02/hash-solidity-compatible.md index 2f766066..68f3a2ec 100644 --- a/src/ch02/hash-solidity-compatible.md +++ b/src/ch02/hash-solidity-compatible.md @@ -8,4 +8,4 @@ For example: {{#include ../../listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo}} ``` -Visit contract on [Voyager](#) or play with it in [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/blob/main/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo). +Play with the contract in [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/blob/main/listings/ch02-advanced-concepts/hash_solidity_compatible/src/contract.cairo). From d2fdddb416f155e2dc4b6d609b2de18b9a591316 Mon Sep 17 00:00:00 2001 From: julienbrs Date: Tue, 21 Nov 2023 00:56:44 +0100 Subject: [PATCH 5/5] nit: fmt --- .../hash_solidity_compatible/src/tests.cairo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo index 43709a54..d1d1726b 100644 --- a/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo +++ b/listings/ch02-advanced-concepts/hash_solidity_compatible/src/tests.cairo @@ -21,7 +21,8 @@ mod tests { let mut array: Array = ArrayTrait::new(); array.append(1); - let hash_expected: u256 = 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6; + let hash_expected: u256 = + 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6; let hash_received: u256 = state.hash_data(array.span()); assert(hash_received == hash_expected, 'hash_received != hash_expected');