Skip to content

Commit

Permalink
renegade_contracts: darkpool: verifying VALID_WALLET_UPDATE statement…
Browse files Browse the repository at this point in the history
… signature
  • Loading branch information
akirillo committed Aug 29, 2023
1 parent 0105b04 commit 119c249
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 36 deletions.
19 changes: 17 additions & 2 deletions src/darkpool.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ trait IDarkpool<TContractState> {
ref self: TContractState,
wallet_blinder_share: Scalar,
statement: ValidWalletUpdateStatement,
statement_signature: (Scalar, Scalar),
witness_commitments: Array<EcPoint>,
proof: Proof,
verification_job_id: felt252,
Expand All @@ -97,10 +98,12 @@ trait IDarkpool<TContractState> {
#[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,
Expand All @@ -116,15 +119,16 @@ 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},
};

use super::{
types::{
ExternalTransfer, MatchPayload, NewWalletCallbackElems, UpdateWalletCallbackElems,
ProcessMatchCallbackElems, Circuit,
ProcessMatchCallbackElems, Circuit, PublicSigningKeyTrait,
},
statements::{ValidWalletCreateStatement, ValidWalletUpdateStatement, ValidSettleStatement}
};
Expand Down Expand Up @@ -559,6 +563,7 @@ mod Darkpool {
ref self: ContractState,
wallet_blinder_share: Scalar,
statement: ValidWalletUpdateStatement,
statement_signature: (Scalar, Scalar),
mut witness_commitments: Array<EcPoint>,
proof: Proof,
verification_job_id: felt252,
Expand All @@ -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);

Expand Down
7 changes: 4 additions & 3 deletions src/darkpool/statements.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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<Scalar>,
old_pk_root: PublicSigningKey,
/// The timestamp this update was applied at
timestamp: u64,
}
Expand All @@ -69,7 +69,8 @@ impl ValidWalletUpdateStatementToScalarsImpl of ScalarSerializable<ValidWalletUp
scalars.append_all(ref new_public_shares);
scalars.append(*self.merkle_root);
scalars.append_all(ref external_transfer_scalars);
scalars.append_all(ref old_pk_root);
scalars.append_all(ref old_pk_root.x);
scalars.append_all(ref old_pk_root.y);
scalars.append((*self.timestamp).into());

scalars
Expand Down
21 changes: 20 additions & 1 deletion src/darkpool/types.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use array::ArrayTrait;
use starknet::ContractAddress;

use renegade_contracts::{
verifier::{scalar::{Scalar, ScalarSerializable}, types::Proof}, utils::serde::EcPointSerde
verifier::{scalar::{Scalar, ScalarSerializable}, types::Proof},
utils::{serde::EcPointSerde, eq::ArrayTPartialEq}
};

use super::statements::{ValidReblindStatement, ValidCommitmentsStatement};
Expand Down Expand Up @@ -67,6 +68,24 @@ struct MatchPayload {
valid_reblind_proof: Proof,
}

/// Represents the affine coordinates of an ECDSA public key over the STARK curve.
/// Since each coordinate is an element of the base field, it takes 2 scalars to represent it.
#[derive(Drop, Serde, Clone, PartialEq)]
struct PublicSigningKey {
x: Array<Scalar>,
y: Array<Scalar>,
}

#[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 |
// --------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/merkle.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
27 changes: 18 additions & 9 deletions src/testing/test_contracts/statement_serde_wrapper.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -222,12 +225,18 @@ fn dummy_public_wallet_shares() -> Array<Scalar> {
public_wallet_shares
}

fn dummy_public_signing_key() -> Array<Scalar> {
// 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 }
}
6 changes: 1 addition & 5 deletions src/utils/constants.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
37 changes: 32 additions & 5 deletions src/utils/crypto.cairo
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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())
}
Expand Down Expand Up @@ -67,10 +68,36 @@ fn commit_public(B: EcPoint, B_blind: EcPoint, mut inputs: Span<Scalar>) -> 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<T, impl TScalarSerializable: ScalarSerializable<T>>(
B: EcPoint, B_blind: EcPoint, statement: @T, ref witness_commitments: Array<EcPoint>
) {
let statement_scalars = statement.to_scalars();
let mut statement_commitments = commit_public(B, B_blind, statement_scalars.span());
witness_commitments.append_all(ref statement_commitments);
}

/// Computes the Keccak256 hash of the given statement, reducing the result into a Scalar.
/// We manually mirrors the implementation of keccak::keccak_u256s_le_inputs so that we can
/// avoid an extra loop over the input to map it into u256s.
fn hash_statement<T, impl TScalarSerializable: ScalarSerializable<T>>(statement: @T) -> Scalar {
let mut statement_scalars = statement.to_scalars();
let mut keccak_input: Array<u64> = 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()
}
29 changes: 19 additions & 10 deletions src/verifier/scalar.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 }
}
}
Expand All @@ -44,7 +47,7 @@ impl ScalarImpl of ScalarTrait {
impl ScalarAdd of Add<Scalar> {
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 }
Expand All @@ -64,7 +67,7 @@ impl ScalarAddEq of AddEq<Scalar> {
impl ScalarSub of Sub<Scalar> {
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 }
Expand All @@ -84,7 +87,7 @@ impl ScalarSubEq of SubEq<Scalar> {
impl ScalarMul of Mul<Scalar> {
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 }
Expand All @@ -106,7 +109,7 @@ impl ScalarDiv of Div<Scalar> {
// 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 }
Expand All @@ -126,7 +129,7 @@ impl ScalarDivEq of DivEq<Scalar> {
impl ScalarNeg of Neg<Scalar> {
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 }
}
}
Expand All @@ -141,7 +144,7 @@ impl ScalarNeg of Neg<Scalar> {

impl U256IntoScalar of Into<u256, Scalar> {
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() }
}
Expand All @@ -167,6 +170,12 @@ impl ScalarIntoFelt of Into<Scalar, felt252> {
}
}

impl ScalarTryIntoU128 of TryInto<Scalar, u128> {
fn try_into(self: Scalar) -> Option<u128> {
self.inner.try_into()
}
}

// ------------
// | ZEROABLE |
// ------------
Expand Down

0 comments on commit 119c249

Please sign in to comment.