From ad9b859dbed4951c434ce4755f6c595b2b26b217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Roycourt?= <11146088+remiroyc@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:46:08 +0100 Subject: [PATCH] fix(ark-contracts): use poseidon hash function (#200) --- .../arkchain/src/crypto/hash.cairo | 31 +------- .../arkchain/src/order/order_v1.cairo | 7 +- .../arkchain/src/order/types.cairo | 2 +- .../arkchain/src/orderbook.cairo | 4 +- .../tests/unit/order/test_order_v1.cairo | 6 +- scripts/signature/src/index.ts | 61 +++++++-------- scripts/signature/src/utils.ts | 74 +++++++++++++------ 7 files changed, 92 insertions(+), 93 deletions(-) diff --git a/crates/ark-contracts/arkchain/src/crypto/hash.cairo b/crates/ark-contracts/arkchain/src/crypto/hash.cairo index 4798ea9cc..f43af2252 100644 --- a/crates/ark-contracts/arkchain/src/crypto/hash.cairo +++ b/crates/ark-contracts/arkchain/src/crypto/hash.cairo @@ -1,34 +1,7 @@ -//! Crypto hashes computation. - -/// Computes the starknet keccak on the input data. -/// Starknet keccak is used to fit into one felt, and being easily -/// computed with off-chain components using starknet-rs or starknetjs. -/// -/// # Arguments -/// -/// * `data` - Data to hash. -fn starknet_keccak(data: Span) -> felt252 { - let mut u256_data: Array = array![]; - - let mut i = 0_usize; - loop { - if i == data.len() { - break; - } - u256_data.append((*data[i]).into()); - i += 1; - }; - - let mut hash = keccak::keccak_u256s_be_inputs(u256_data.span()); - let low = integer::u128_byte_reverse(hash.high); - let high = integer::u128_byte_reverse(hash.low); - hash = u256 { low, high }; - hash = hash & 0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_u256; - hash.try_into().expect('starknet keccak overflow') -} +use poseidon::poseidon_hash_span; fn serialized_hash, +Drop>(value: T) -> felt252 { let mut buf = array![]; value.serialize(ref buf); - starknet_keccak(buf.span()) + poseidon_hash_span(buf.span()) } diff --git a/crates/ark-contracts/arkchain/src/order/order_v1.cairo b/crates/ark-contracts/arkchain/src/order/order_v1.cairo index 349350669..175179403 100644 --- a/crates/ark-contracts/arkchain/src/order/order_v1.cairo +++ b/crates/ark-contracts/arkchain/src/order/order_v1.cairo @@ -7,9 +7,8 @@ use core::option::OptionTrait; use starknet::ContractAddress; use starknet::contract_address_to_felt252; use arkchain::order::types::{OrderTrait, OrderValidationError, OrderType, RouteType}; -use arkchain::crypto::hash::starknet_keccak; use arkchain::order::types::FulfillInfo; - +use poseidon::poseidon_hash_span; const ORDER_VERSION_V1: felt252 = 'v1'; // Auction -> end_amount (reserve price) > start_amount (starting price). // Auction -> ERC721_ERC20. @@ -147,12 +146,12 @@ impl OrderTraitOrderV1 of OrderTrait { buf.append((token_id.high).into()); buf.append((*self.token_address).into()); buf.append(*self.token_chain_id); - starknet_keccak(buf.span()) + poseidon_hash_span(buf.span()) } fn compute_order_hash(self: @OrderV1) -> felt252 { let mut buf = array![]; self.serialize(ref buf); - starknet_keccak(buf.span()) + poseidon_hash_span(buf.span()) } } diff --git a/crates/ark-contracts/arkchain/src/order/types.cairo b/crates/ark-contracts/arkchain/src/order/types.cairo index e03407e48..e8075cba2 100644 --- a/crates/ark-contracts/arkchain/src/order/types.cairo +++ b/crates/ark-contracts/arkchain/src/order/types.cairo @@ -1,6 +1,6 @@ //! Order generic variables. use starknet::ContractAddress; -use arkchain::crypto::hash::starknet_keccak; +use poseidon::poseidon_hash_span; /// Order types. #[derive(Serde, Drop, PartialEq, Copy)] diff --git a/crates/ark-contracts/arkchain/src/orderbook.cairo b/crates/ark-contracts/arkchain/src/orderbook.cairo index 0b9e71b03..9ca033d89 100644 --- a/crates/ark-contracts/arkchain/src/orderbook.cairo +++ b/crates/ark-contracts/arkchain/src/orderbook.cairo @@ -131,8 +131,8 @@ mod orderbook { }; use arkchain::crypto::signer::{SignInfo, Signer, SignerValidator}; use arkchain::order::types::OrderStatus; - use arkchain::crypto::hash::{starknet_keccak, serialized_hash}; - + use arkchain::crypto::hash::{serialized_hash}; + use poseidon::poseidon_hash_span; const EXTENSION_TIME_IN_SECONDS: u64 = 600; const AUCTION_ACCEPTING_TIME_SECS: u64 = 172800; /// Storage struct for the Orderbook contract. diff --git a/crates/ark-contracts/arkchain/tests/unit/order/test_order_v1.cairo b/crates/ark-contracts/arkchain/tests/unit/order/test_order_v1.cairo index a66848602..69c3626b6 100644 --- a/crates/ark-contracts/arkchain/tests/unit/order/test_order_v1.cairo +++ b/crates/ark-contracts/arkchain/tests/unit/order/test_order_v1.cairo @@ -27,14 +27,12 @@ fn test_validate_common_data_with_valid_order() { fn test_order_signature() { let block_timestamp = 1700069210; let (order_listing, order_offer, order_auction, order_collection_offer) = setup_orders(); - let order_hash = order_listing.compute_order_hash(); - let signer = Signer::WEIERSTRESS_STARKNET( SignInfo { user_pubkey: 0x20c29f1c98f3320d56f01c13372c923123c35828bce54f2153aa1cfe61c44f2, - user_sig_r: 1489698813778371355144251950338771124997774846063061621537328046839877802074, - user_sig_s: 3108595483860555652530443442785498132521754673633088480599648397307624361634, + user_sig_r: 1604505843266718300714627301508508239046631480436842114329682515156485708769, + user_sig_s: 61902183293821228744451698818869796262719190329925918953383999133206977961, } ); SignerValidator::verify(order_hash, signer); diff --git a/scripts/signature/src/index.ts b/scripts/signature/src/index.ts index 9c03a18bc..a09e659a6 100644 --- a/scripts/signature/src/index.ts +++ b/scripts/signature/src/index.ts @@ -1,6 +1,6 @@ import { ec, encode } from "starknet"; import { OrderV1, RouteType, ExecutionInfo } from "./types"; -import { hashOrder, hashExecutionInfo } from "./utils"; +import { poseidonHashOrder } from "./utils"; function main() { const privateKey = "0x1234567890987654321"; @@ -24,13 +24,13 @@ function main() { quantity: { high: 0, low: 1 }, startAmount: { high: 0, low: 600000000000000000 }, endAmount: { high: 0, low: 0 }, - startDate: 1699556878, - endDate: 1702148878, + startDate: 1699556828, + endDate: 1702148828, brokerId: 123, additionalData: [], }; - const orderMessageHash = hashOrder(order); + const orderMessageHash = poseidonHashOrder(order); const signature = ec.starkCurve.sign(orderMessageHash, privateKey); console.log("Order Message Hash:", orderMessageHash); @@ -40,35 +40,36 @@ function main() { console.log("Signature S:", signature.s.toString()); } -function execution() { - const privateKey = "0x1234567890987654321"; - const starknetPublicKey = ec.starkCurve.getStarkKey(privateKey); - const fullPublicKey = encode.addHexPrefix( - encode.buf2hex(ec.starkCurve.getPublicKey(privateKey, false)) - ); +// function execution() { +// const privateKey = "0x1234567890987654321"; +// const starknetPublicKey = ec.starkCurve.getStarkKey(privateKey); +// const fullPublicKey = encode.addHexPrefix( +// encode.buf2hex(ec.starkCurve.getPublicKey(privateKey, false)) +// ); - const executionInfo: ExecutionInfo = { - orderHash: - "0x00E4769a444F7FF9C70951A333eBA5c32707Cef3CdfB6B27cA63567f51cdd078", - fulfiller: - "0x00E4769a4d2F7F69C70951A333eBA5c32707Cef3CdfB6B27cA63567f51cdd078", - tokenChainId: "0x534e5f4d41494e", - tokenAddress: - "0x01435498bf393da86b4733b9264a86b58a42b31f8d8b8ba309593e5c17847672", - tokenId: { high: 0, low: 10 }, - }; +// const executionInfo: ExecutionInfo = { +// orderHash: +// "0x00E4769a444F7FF9C70951A333eBA5c32707Cef3CdfB6B27cA63567f51cdd078", +// fulfiller: +// "0x00E4769a4d2F7F69C70951A333eBA5c32707Cef3CdfB6B27cA63567f51cdd078", +// tokenChainId: "0x534e5f4d41494e", +// tokenAddress: +// "0x01435498bf393da86b4733b9264a86b58a42b31f8d8b8ba309593e5c17847672", +// tokenId: { high: 0, low: 10 }, +// }; - const executionInfoMessageHash = hashExecutionInfo(executionInfo); - const signature = ec.starkCurve.sign(executionInfoMessageHash, privateKey); +// const executionInfoMessageHash = hashExecutionInfo(executionInfo); +// const signature = ec.starkCurve.sign(executionInfoMessageHash, privateKey); - console.log("Order Message Hash:", executionInfoMessageHash); - console.log("StarkNet Public Key:", starknetPublicKey); - console.log("Full Public Key:", fullPublicKey); - console.log("Signature R:", signature.r.toString()); - console.log("Signature S:", signature.s.toString()); -} +// console.log("Order Message Hash:", executionInfoMessageHash); +// console.log("StarkNet Public Key:", starknetPublicKey); +// console.log("Full Public Key:", fullPublicKey); +// console.log("Signature R:", signature.r.toString()); +// console.log("Signature S:", signature.s.toString()); +// } console.log("------ ORDER -------"); main(); -console.log("------ Execution ------"); -execution(); + +// console.log("------ Execution ------"); +// execution(); diff --git a/scripts/signature/src/utils.ts b/scripts/signature/src/utils.ts index b55372eb9..6abb6986e 100644 --- a/scripts/signature/src/utils.ts +++ b/scripts/signature/src/utils.ts @@ -8,13 +8,41 @@ export function bigNumberishToUint8Array(value: BigNumberish): Uint8Array { return num.hexToBytes(`0x${hex}`); } +export function poseidonHashOrder(order: OrderV1): string { + const elements: bigint[] = [ + BigInt(order.route), + BigInt(order.currencyAddress), + BigInt(order.currencyChainId), + BigInt(order.salt), + BigInt(order.offerer), + BigInt(order.tokenChainId), + BigInt(order.tokenAddress), + ...(order.tokenId + ? [BigInt(0), BigInt(order.tokenId.low), BigInt(order.tokenId.high)] + : [BigInt(1)]), + BigInt(order.quantity.low), + BigInt(order.quantity.high), + BigInt(order.startAmount.low), + BigInt(order.startAmount.high), + BigInt(order.endAmount.low), + BigInt(order.endAmount.high), + BigInt(order.startDate), + BigInt(order.endDate), + BigInt(order.brokerId), + BigInt(order.additionalData.length), + ]; + + const generatedHash = starknet.poseidonHashMany(elements); + return num.toHex(generatedHash); +} + /** * Constructs a Keccak hash from the order's properties. * Property order is important as it determines the hash output. * @param {OrderV1} order - The order to hash. * @returns {string} The Keccak hash of the order as a hex string. */ -export function hashOrder(order: OrderV1): string { +export function keccakHashOrder(order: OrderV1): string { // Convert order properties to Uint8Array and concatenate them. // The array is initialized with a fixed set of properties. let results = Uint8Array.from([ @@ -63,27 +91,27 @@ export function hashOrder(order: OrderV1): string { * @param {OrderV1} order - The order to hash. * @returns {string} The Keccak hash of the order as a hex string. */ -export function hashExecutionInfo(execution: ExecutionInfo): string { - // Convert order properties to Uint8Array and concatenate them. - // The array is initialized with a fixed set of properties. - let results = Uint8Array.from([ - ...bigNumberishToUint8Array(execution.orderHash), - ...bigNumberishToUint8Array(execution.fulfiller), - ...bigNumberishToUint8Array(execution.offerHash || 1), - ...bigNumberishToUint8Array(execution.tokenChainId), - ...bigNumberishToUint8Array(execution.tokenAddress), - ...(execution.tokenId - ? [ - ...bigNumberishToUint8Array(0), - ...bigNumberishToUint8Array(execution.tokenId.low), - ...bigNumberishToUint8Array(execution.tokenId.high), - ] - : [...bigNumberishToUint8Array(1)]), - ]); +// export function hashExecutionInfo(execution: ExecutionInfo): string { +// // Convert order properties to Uint8Array and concatenate them. +// // The array is initialized with a fixed set of properties. +// let results = Uint8Array.from([ +// ...bigNumberishToUint8Array(execution.orderHash), +// ...bigNumberishToUint8Array(execution.fulfiller), +// ...bigNumberishToUint8Array(execution.offerHash || 1), +// ...bigNumberishToUint8Array(execution.tokenChainId), +// ...bigNumberishToUint8Array(execution.tokenAddress), +// ...(execution.tokenId +// ? [ +// ...bigNumberishToUint8Array(0), +// ...bigNumberishToUint8Array(execution.tokenId.low), +// ...bigNumberishToUint8Array(execution.tokenId.high), +// ] +// : [...bigNumberishToUint8Array(1)]), +// ]); - // Generate the hash from the concatenated Uint8Arrays. - const generatedHash = starknet.keccak(results); +// // Generate the hash from the concatenated Uint8Arrays. +// const generatedHash = starknet.keccak(results); - // Convert the generated hash to a hexadecimal string. - return num.toHex(generatedHash); -} \ No newline at end of file +// // Convert the generated hash to a hexadecimal string. +// return num.toHex(generatedHash); +// }