diff --git a/src/darkpool.cairo b/src/darkpool.cairo index e8eda9cf..d8a51b7d 100644 --- a/src/darkpool.cairo +++ b/src/darkpool.cairo @@ -71,6 +71,7 @@ trait IDarkpool { ref self: TContractState, wallet_blinder_share: Scalar, statement: ValidWalletUpdateStatement, + statement_signature: (Scalar, Scalar), witness_commitments: Array, proof: Proof, verification_job_id: felt252, @@ -97,10 +98,12 @@ trait IDarkpool { #[starknet::contract] mod Darkpool { use option::OptionTrait; + use traits::Into; use clone::Clone; use array::{ArrayTrait, SpanTrait}; use box::BoxTrait; use zeroable::Zeroable; + use ecdsa::check_ecdsa_signature; use starknet::{ ClassHash, get_caller_address, get_contract_address, get_tx_info, ContractAddress, replace_class_syscall, contract_address::ContractAddressZeroable, @@ -116,7 +119,8 @@ mod Darkpool { merkle::{poseidon::poseidon_hash, IMerkleLibraryDispatcher, IMerkleDispatcherTrait}, nullifier_set::{INullifierSetLibraryDispatcher, INullifierSetDispatcherTrait}, utils::{ - serde::EcPointSerde, storage::StoreSerdeWrapper, crypto::append_statement_commitments, + serde::EcPointSerde, storage::StoreSerdeWrapper, + crypto::{append_statement_commitments, hash_statement} }, oz::erc20::{IERC20DispatcherTrait, IERC20Dispatcher}, }; @@ -124,7 +128,7 @@ mod Darkpool { use super::{ types::{ ExternalTransfer, MatchPayload, NewWalletCallbackElems, UpdateWalletCallbackElems, - ProcessMatchCallbackElems, Circuit, + ProcessMatchCallbackElems, Circuit, PublicSigningKeyTrait, }, statements::{ValidWalletCreateStatement, ValidWalletUpdateStatement, ValidSettleStatement} }; @@ -559,6 +563,7 @@ mod Darkpool { ref self: ContractState, wallet_blinder_share: Scalar, statement: ValidWalletUpdateStatement, + statement_signature: (Scalar, Scalar), mut witness_commitments: Array, proof: Proof, verification_job_id: felt252, @@ -570,6 +575,16 @@ mod Darkpool { 'invalid statement merkle root' ); + // Assert that statement signature is valid + let statement_hash = hash_statement(@statement); + let (r, s) = statement_signature; + assert( + check_ecdsa_signature( + statement_hash.into(), statement.old_pk_root.get_x(), r.into(), s.into() + ), + 'invalid statement signature' + ); + // Mark the `old_shares_nullifier` as in use _get_nullifier_set(@self).mark_nullifier_in_use(statement.old_shares_nullifier); diff --git a/src/darkpool/statements.cairo b/src/darkpool/statements.cairo index 995d505f..80f744ed 100644 --- a/src/darkpool/statements.cairo +++ b/src/darkpool/statements.cairo @@ -7,7 +7,7 @@ use renegade_contracts::{ verifier::scalar::{Scalar, ScalarSerializable}, utils::eq::ArrayTPartialEq }; -use super::types::ExternalTransfer; +use super::types::{ExternalTransfer, PublicSigningKey}; // ------------------- // | STATEMENT TYPES | @@ -52,7 +52,7 @@ struct ValidWalletUpdateStatement { /// The external transfer associated with this update external_transfer: ExternalTransfer, /// The public root key of the old wallet, rotated out after this update - old_pk_root: Array, + old_pk_root: PublicSigningKey, /// The timestamp this update was applied at timestamp: u64, } @@ -69,7 +69,8 @@ impl ValidWalletUpdateStatementToScalarsImpl of ScalarSerializable, + y: Array, +} + +#[generate_trait] +impl PublicSigningKeyImpl of PublicSigningKeyTrait { + fn get_x(self: @PublicSigningKey) -> felt252 { + let x_u256 = u256 { + low: (*self.x[0]).try_into().unwrap(), high: (*self.x[1]).try_into().unwrap() + }; + x_u256.try_into().unwrap() + } +} + // -------------------------- // | CALLBACK ELEMENT TYPES | // -------------------------- diff --git a/src/merkle.cairo b/src/merkle.cairo index 4ff3c033..f6e18b14 100644 --- a/src/merkle.cairo +++ b/src/merkle.cairo @@ -29,7 +29,7 @@ mod Merkle { /// The value of an empty leaf in the Merkle tree: /// 306932273398430716639340090025251549301604242969558673011416862133942957551 /// This value is computed as the keccak256 hash of the string 'renegade' - /// taken modulo the STARK scalar field's modulus (see `SCALAR_FIELD_ORDER` in src/utils/constants.cairo) + /// taken modulo the STARK curve order const EMPTY_LEAF_VAL_INNER: felt252 = 306932273398430716639340090025251550554329269971178413658580639401611971225; diff --git a/src/testing/test_contracts/statement_serde_wrapper.cairo b/src/testing/test_contracts/statement_serde_wrapper.cairo index c187bcaf..de062282 100644 --- a/src/testing/test_contracts/statement_serde_wrapper.cairo +++ b/src/testing/test_contracts/statement_serde_wrapper.cairo @@ -2,9 +2,12 @@ use traits::Into; use array::ArrayTrait; use renegade_contracts::{ - darkpool::statements::{ - ValidWalletCreateStatement, ValidWalletUpdateStatement, ValidReblindStatement, - ValidCommitmentsStatement, ValidSettleStatement + darkpool::{ + statements::{ + ValidWalletCreateStatement, ValidWalletUpdateStatement, ValidReblindStatement, + ValidCommitmentsStatement, ValidSettleStatement + }, + types::PublicSigningKey }, verifier::scalar::Scalar }; @@ -222,12 +225,18 @@ fn dummy_public_wallet_shares() -> Array { public_wallet_shares } -fn dummy_public_signing_key() -> Array { - // Public signing key is represented by 2 scalars - let mut public_signing_key = ArrayTrait::new(); +fn dummy_public_signing_key() -> PublicSigningKey { + // Public signing key coordinates are represented by 2 scalars + + let mut x = ArrayTrait::new(); + + x.append(DUMMY_VALUE.into()); + x.append(DUMMY_VALUE.into()); + + let mut y = ArrayTrait::new(); - public_signing_key.append(DUMMY_VALUE.into()); - public_signing_key.append(DUMMY_VALUE.into()); + y.append(DUMMY_VALUE.into()); + y.append(DUMMY_VALUE.into()); - public_signing_key + PublicSigningKey { x, y } } diff --git a/src/utils/constants.cairo b/src/utils/constants.cairo index edfde7a8..dd268e51 100644 --- a/src/utils/constants.cairo +++ b/src/utils/constants.cairo @@ -6,11 +6,7 @@ const BASE_FIELD_ORDER: u256 = 3618502788666131213697322783095070105623107215331596699973092056135872020481; -// Order of the group of points of the STARK curve -const SCALAR_FIELD_ORDER: u256 = - 3618502788666131213697322783095070105526743751716087489154079457884512865583; - -// 2^{256} % SCALAR_FIELD_ORDER +// 2^{256} % STARK curve order const SHIFT_256_SCALAR: u256 = 3618502788666127798953978732740734581940928362441851875681120813493230806863; diff --git a/src/utils/crypto.cairo b/src/utils/crypto.cairo index 40cdceed..1640c75e 100644 --- a/src/utils/crypto.cairo +++ b/src/utils/crypto.cairo @@ -1,13 +1,13 @@ use traits::{TryInto, Into}; use option::OptionTrait; use array::{ArrayTrait, SpanTrait}; -use keccak::keccak_u256s_le_inputs; +use keccak::{keccak_u256s_le_inputs, keccak_add_u256_le, add_padding}; use ec::ec_mul; - use alexandria_data_structures::array_ext::ArrayTraitExt; +use starknet::{syscalls::keccak_syscall, SyscallResultTrait}; use renegade_contracts::verifier::scalar::{Scalar, ScalarSerializable}; -use super::constants::{BASE_FIELD_ORDER, SCALAR_FIELD_ORDER, SHIFT_256_FELT, SHIFT_256_SCALAR}; +use super::constants::{BASE_FIELD_ORDER, SHIFT_256_FELT, SHIFT_256_SCALAR}; /// Reduces a hash to an element of the scalar field, @@ -25,8 +25,9 @@ fn hash_to_scalar(hash: u256) -> Scalar { data.append(hash); let high_u256 = keccak_u256s_le_inputs(data.span()); - let low_scalar: Scalar = hash.into(); // low_u256 % r - let high_scalar: Scalar = high_u256.into(); // high_u256 % r + let low_scalar: Scalar = hash.into(); // low_u256 % r (modular reduction occurs in `into`) + let high_scalar: Scalar = high_u256 + .into(); // high_u256 % r (modular reduction occurs in `into`) low_scalar + (high_scalar * SHIFT_256_SCALAR.into()) } @@ -67,6 +68,8 @@ fn commit_public(B: EcPoint, B_blind: EcPoint, mut inputs: Span) -> Arra commitments } +/// Computes Pedersen commitments of the given statement using the given generators, +/// and appends the commitments to the existing array of witness commitments fn append_statement_commitments>( B: EcPoint, B_blind: EcPoint, statement: @T, ref witness_commitments: Array ) { @@ -74,3 +77,27 @@ fn append_statement_commitments>(statement: @T) -> Scalar { + let mut statement_scalars = statement.to_scalars(); + let mut keccak_input: Array = ArrayTrait::new(); + + loop { + match statement_scalars.pop_front() { + Option::Some(scalar) => { + keccak_add_u256_le(ref keccak_input, scalar.into()); + }, + Option::None(()) => { + break; + }, + }; + }; + + add_padding(ref keccak_input, 0, 0); + + // This `into` call performs modular reduction of the hash into a scalar + keccak_syscall(keccak_input.span()).unwrap_syscall().into() +} diff --git a/src/verifier/scalar.cairo b/src/verifier/scalar.cairo index 17f50431..7860468d 100644 --- a/src/verifier/scalar.cairo +++ b/src/verifier/scalar.cairo @@ -3,13 +3,12 @@ use traits::{TryInto, Into}; use integer::NumericLiteral; use debug::PrintTrait; use hash::LegacyHash; +use ec::stark_curve; use alexandria_math::mod_arithmetics::{ mult_inverse, pow_mod, add_mod, sub_mod, mult_mod, div_mod, add_inverse_mod }; -use renegade_contracts::utils::constants::SCALAR_FIELD_ORDER; - // TODO: When deserializing Scalars from calldata / storage, we need to assert that they are // within the scalar field order. // Best way to do this is probably to accept felts for calldata, and call .into() then. @@ -22,13 +21,17 @@ struct Scalar { impl ScalarImpl of ScalarTrait { fn inverse(self: @Scalar) -> Scalar { // Safe to unwrap b/c scalar field is smaller than base field - let inner = mult_inverse((*self.inner).into(), SCALAR_FIELD_ORDER).try_into().unwrap(); + let inner = mult_inverse((*self.inner).into(), stark_curve::ORDER.into()) + .try_into() + .unwrap(); Scalar { inner } } fn pow(self: @Scalar, exponent: u256) -> Scalar { // Safe to unwrap b/c scalar field is smaller than base field - let inner = pow_mod((*self.inner).into(), exponent, SCALAR_FIELD_ORDER).try_into().unwrap(); + let inner = pow_mod((*self.inner).into(), exponent, stark_curve::ORDER.into()) + .try_into() + .unwrap(); Scalar { inner } } } @@ -44,7 +47,7 @@ impl ScalarImpl of ScalarTrait { impl ScalarAdd of Add { fn add(lhs: Scalar, rhs: Scalar) -> Scalar { // Safe to unwrap b/c scalar field is smaller than base field - let inner = add_mod(lhs.inner.into(), rhs.inner.into(), SCALAR_FIELD_ORDER) + let inner = add_mod(lhs.inner.into(), rhs.inner.into(), stark_curve::ORDER.into()) .try_into() .unwrap(); Scalar { inner } @@ -64,7 +67,7 @@ impl ScalarAddEq of AddEq { impl ScalarSub of Sub { fn sub(lhs: Scalar, rhs: Scalar) -> Scalar { // Safe to unwrap b/c scalar field is smaller than base field - let inner = sub_mod(lhs.inner.into(), rhs.inner.into(), SCALAR_FIELD_ORDER) + let inner = sub_mod(lhs.inner.into(), rhs.inner.into(), stark_curve::ORDER.into()) .try_into() .unwrap(); Scalar { inner } @@ -84,7 +87,7 @@ impl ScalarSubEq of SubEq { impl ScalarMul of Mul { fn mul(lhs: Scalar, rhs: Scalar) -> Scalar { // Safe to unwrap b/c scalar field is smaller than base field - let inner = mult_mod(lhs.inner.into(), rhs.inner.into(), SCALAR_FIELD_ORDER) + let inner = mult_mod(lhs.inner.into(), rhs.inner.into(), stark_curve::ORDER.into()) .try_into() .unwrap(); Scalar { inner } @@ -106,7 +109,7 @@ impl ScalarDiv of Div { // Under the hood, this is implemented as // lhs * rhs.inverse() // Safe to unwrap b/c scalar field is smaller than base field - let inner = div_mod(lhs.inner.into(), rhs.inner.into(), SCALAR_FIELD_ORDER) + let inner = div_mod(lhs.inner.into(), rhs.inner.into(), stark_curve::ORDER.into()) .try_into() .unwrap(); Scalar { inner } @@ -126,7 +129,7 @@ impl ScalarDivEq of DivEq { impl ScalarNeg of Neg { fn neg(a: Scalar) -> Scalar { // Safe to unwrap b/c scalar field is smaller than base field - let inner = add_inverse_mod(a.inner.into(), SCALAR_FIELD_ORDER).try_into().unwrap(); + let inner = add_inverse_mod(a.inner.into(), stark_curve::ORDER.into()).try_into().unwrap(); Scalar { inner } } } @@ -141,7 +144,7 @@ impl ScalarNeg of Neg { impl U256IntoScalar of Into { fn into(self: u256) -> Scalar { - let inner_u256 = self % SCALAR_FIELD_ORDER; + let inner_u256 = self % stark_curve::ORDER.into(); // Safe to unwrap b/c scalar field is smaller than base field Scalar { inner: inner_u256.try_into().unwrap() } } @@ -167,6 +170,12 @@ impl ScalarIntoFelt of Into { } } +impl ScalarTryIntoU128 of TryInto { + fn try_into(self: Scalar) -> Option { + self.inner.try_into() + } +} + // ------------ // | ZEROABLE | // ------------