Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

architecture refactor #33

Merged
merged 16 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bench/compliance_circuit_bench.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{:ok, program} = File.read("./native/cairo_vm/compliance.json")
{:ok, input} = File.read("./native/cairo_vm/compliance_input.json")
{:ok, program} = File.read("./juvix/compliance.json")
{:ok, input} = File.read("./juvix/compliance_input.json")

{_output, trace, memory, public_inputs} =
Cairo.cairo_vm_runner(
Expand Down
4 changes: 2 additions & 2 deletions bench/logic_circuit_bench.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{:ok, program} = File.read("./native/cairo_vm/trivial_resource_logic.json")
{:ok, program} = File.read("./juvix/trivial_resource_logic.json")

{:ok, input} =
File.read("./native/cairo_vm/trivial_resource_logic_input.json")
File.read("./juvix/trivial_resource_logic_input.json")

{_output, trace, memory, public_inputs} =
Cairo.cairo_vm_runner(
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion lib/cairo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ defmodule Cairo do
to: Cairo.CairoProver,
as: :program_hash

@spec felt_to_string(list(byte())) :: binary()
@spec felt_to_string(list(byte())) :: binary() | {:error, term()}
defdelegate felt_to_string(felt),
to: Cairo.CairoProver,
as: :cairo_felt_to_string
Expand Down
1 change: 1 addition & 0 deletions lib/cairo/cairo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ defmodule Cairo.CairoProver do
@spec program_hash(list(byte())) :: nif_result(list(byte()))
def program_hash(_public_inputs), do: error()

@spec cairo_felt_to_string(list(byte())) :: nif_result(binary())
def cairo_felt_to_string(_felt), do: error()

def cairo_generate_compliance_input_json(
Expand Down
9 changes: 5 additions & 4 deletions native/cairo_prover/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions native/cairo_prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ crate-type = ["cdylib"]

[dependencies]
rustler = "0.31.0"
cairo-platinum-prover = { git = "https://github.com/lambdaclass/lambdaworks", version = "0.9.0"}
stark-platinum-prover = { git = "https://github.com/lambdaclass/lambdaworks", version = "0.9.0"}
lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", version = "0.9.0"}
cairo-platinum-prover = { git = "https://github.com/heliaxdev/lambdaworks", branch = "cairo_rm"}
stark-platinum-prover = { git = "https://github.com/heliaxdev/lambdaworks", branch = "cairo_rm"}
lambdaworks-math = { git = "https://github.com/heliaxdev/lambdaworks", branch = "cairo_rm"}
bincode = "2.0.0-rc.3"
serde_json = { version = "1.0", features = ["preserve_order"] }
hashbrown = { version = "0.14.0", features = ["serde"] }
Expand All @@ -26,3 +26,4 @@ num-integer = { version = "0.1.45", default-features = false }
rand = "0.8.5"
lazy_static = "1.4"
serde = { version = "1.0.160", features = ["derive"] }
thiserror = "1.0"
117 changes: 117 additions & 0 deletions native/cairo_prover/src/binding_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use crate::{
error::CairoError,
utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec},
};
use num_bigint::BigInt;
use num_integer::Integer;
use num_traits::Zero;
use rand::{thread_rng, RngCore};
use rustler::NifResult;
use starknet_crypto::{poseidon_hash_many, sign, verify};
use starknet_curve::curve_params::{EC_ORDER, GENERATOR};
use starknet_types_core::{curve::ProjectivePoint, felt::Felt};
use std::ops::Add;

// The private_key_segments are random values used in delta commitments.
// The messages are nullifiers and resource commitments in the transaction.
#[rustler::nif]
fn cairo_binding_sig_sign(
private_key_segments: Vec<u8>,
messages: Vec<Vec<u8>>,
) -> NifResult<Vec<u8>> {
if private_key_segments.is_empty() || private_key_segments.len() % 32 != 0 {
return Err(CairoError::InvalidInputs.into());
}
// Compute private key
let private_key = {
let result = private_key_segments
.chunks(32)
.fold(BigInt::zero(), |acc, key_segment| {
let key = BigInt::from_bytes_be(num_bigint::Sign::Plus, key_segment);
acc.add(key)
})
.mod_floor(&EC_ORDER.to_bigint());

let (_, buffer) = result.to_bytes_be();
let mut result = [0u8; 32];
result[(32 - buffer.len())..].copy_from_slice(&buffer[..]);

Felt::from_bytes_be(&result)
};

// Message digest
let sig_hash = message_digest(messages)?;

// ECDSA sign
let mut rng = thread_rng();
let k = {
let mut felt: [u8; 32] = Default::default();
rng.fill_bytes(&mut felt);
Felt::from_bytes_be(&felt)
};
let signature = sign(&private_key, &sig_hash, &k).map_err(CairoError::from)?;

// Serialize signature
let mut ret = Vec::new();
ret.extend(signature.r.to_bytes_be());
ret.extend(signature.s.to_bytes_be());
// We don't need the v to recover pubkey
// ret.extend(signature.v.to_bytes_be());
Ok(ret)
}

// The pub_key_segments are delta commitments in compliance input inputs.
#[rustler::nif]
fn cairo_binding_sig_verify(
pub_key_segments: Vec<Vec<u8>>,
messages: Vec<Vec<u8>>,
signature: Vec<u8>,
) -> NifResult<bool> {
// Generate the public key
let mut pub_key = ProjectivePoint::identity();
for pk_seg_bytes in pub_key_segments.into_iter() {
let pk_seg = bytes_to_affine(pk_seg_bytes)?;
pub_key += pk_seg;
}
let pub_key_x = pub_key
.to_affine()
.map_err(|_| CairoError::InvalidAffinePoint)?
.x();

// Message digest
let msg = message_digest(messages)?;

// Decode the signature
if signature.len() != 64 {
return Err(CairoError::InvalidSignatureFormat.into());
}

let (r_bytes, s_bytes) = signature.split_at(32);
let r = bytes_to_felt(r_bytes.to_vec())?;
let s = bytes_to_felt(s_bytes.to_vec())?;

// Verify the signature
verify(&pub_key_x, &msg, &r, &s).map_err(|_| CairoError::SigVerifyError.into())
}

#[rustler::nif]
fn get_public_key(priv_key: Vec<u8>) -> NifResult<Vec<u8>> {
let priv_key_felt = bytes_to_felt(priv_key)?;

let generator = ProjectivePoint::from_affine(GENERATOR.x(), GENERATOR.y())
.map_err(|_| CairoError::InvalidAffinePoint)?;

let pub_key = (&generator * priv_key_felt)
.to_affine()
.map_err(|_| CairoError::InvalidAffinePoint)?;

let mut ret = pub_key.x().to_bytes_be().to_vec();
let mut y = pub_key.y().to_bytes_be().to_vec();
ret.append(&mut y);
Ok(ret)
}

fn message_digest(msg: Vec<Vec<u8>>) -> NifResult<Felt> {
let felt_msg_vec: Vec<Felt> = bytes_to_felt_vec(msg)?;
Ok(poseidon_hash_many(&felt_msg_vec))
}
120 changes: 77 additions & 43 deletions native/cairo_prover/src/compliance_input.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
use crate::utils::felt_to_string;
use crate::{error::CairoError, utils::felt_to_string};
use rustler::NifResult;
use serde::{Deserialize, Serialize};

#[rustler::nif]
fn cairo_generate_compliance_input_json(
input_resource: Vec<u8>,
output_resource: Vec<u8>,
path: Vec<Vec<u8>>,
pos: u64,
input_nf_key: Vec<u8>,
eph_root: Vec<u8>,
rcv: Vec<u8>,
) -> NifResult<String> {
Ok(ComplianceInputJson::to_json_string(
input_resource,
output_resource,
path,
pos,
input_nf_key,
eph_root,
rcv,
)?)
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ComplianceInputJson {
input: ResourceJson,
Expand Down Expand Up @@ -31,32 +53,31 @@ struct PathNode {

impl ComplianceInputJson {
pub fn to_json_string(
input_resource: &Vec<u8>,
output_resource: &Vec<u8>,
path: &Vec<Vec<u8>>,
input_resource: Vec<u8>,
output_resource: Vec<u8>,
path: Vec<Vec<u8>>,
pos: u64,
input_nf_key: &Vec<u8>,
eph_root: &Vec<u8>,
rcv: &Vec<u8>,
) -> String {
let input = ResourceJson::from_bytes(input_resource);
let output = ResourceJson::from_bytes(output_resource);
input_nf_key: Vec<u8>,
eph_root: Vec<u8>,
rcv: Vec<u8>,
) -> Result<String, CairoError> {
let input = ResourceJson::from_bytes(input_resource)?;
let output = ResourceJson::from_bytes(output_resource)?;

let rcv = felt_to_string(rcv);
let eph_root = felt_to_string(eph_root);
let input_nf_key = felt_to_string(input_nf_key);
let rcv = felt_to_string(rcv)?;
let eph_root = felt_to_string(eph_root)?;
let input_nf_key = felt_to_string(input_nf_key)?;
let mut next_pos = pos;
let merkle_path = path
.iter()
.map(|v| {
let snd = if next_pos % 2 == 0 { false } else { true };
next_pos >>= 1;
PathNode {
fst: felt_to_string(v),
snd,
}
})
.collect();
let mut merkle_path = Vec::new();
for node in path.into_iter() {
let snd = next_pos % 2 != 0;
next_pos >>= 1;
let node = PathNode {
fst: felt_to_string(node)?,
snd,
};
merkle_path.push(node);
}

let compliance_input = Self {
input,
Expand All @@ -66,22 +87,22 @@ impl ComplianceInputJson {
rcv,
eph_root,
};
serde_json::to_string(&compliance_input).unwrap()
Ok(serde_json::to_string(&compliance_input)?)
}
}

impl ResourceJson {
pub fn from_bytes(bytes: &Vec<u8>) -> Self {
Self {
logic: felt_to_string(&bytes[0..32].to_vec()),
label: felt_to_string(&bytes[32..64].to_vec()),
quantity: felt_to_string(&bytes[64..96].to_vec()),
data: felt_to_string(&bytes[96..128].to_vec()),
nonce: felt_to_string(&bytes[128..160].to_vec()),
npk: felt_to_string(&bytes[160..192].to_vec()),
rseed: felt_to_string(&bytes[192..224].to_vec()),
eph: if bytes[224] == 0 { false } else { true },
}
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, CairoError> {
Ok(Self {
logic: felt_to_string(bytes[0..32].to_vec())?,
label: felt_to_string(bytes[32..64].to_vec())?,
quantity: felt_to_string(bytes[64..96].to_vec())?,
data: felt_to_string(bytes[96..128].to_vec())?,
nonce: felt_to_string(bytes[128..160].to_vec())?,
npk: felt_to_string(bytes[160..192].to_vec())?,
rseed: felt_to_string(bytes[192..224].to_vec())?,
eph: bytes[224] != 0,
})
}
}

Expand All @@ -97,14 +118,27 @@ fn test_compliance_input_json() {
let path = (0..32).map(|_| random_felt()).collect();

let json = ComplianceInputJson::to_json_string(
&random_resouce.to_vec(),
&random_resouce.to_vec(),
&path,
random_resouce.to_vec(),
random_resouce.to_vec(),
path,
0,
&random_felt(),
&random_felt(),
&random_felt(),
);
random_felt(),
random_felt(),
random_felt(),
)
.unwrap();

println!("compliance_input_json: {}", json);
}

#[test]
fn generate_compliance_input_test_params() {
use starknet_crypto::poseidon_hash;
use starknet_types_core::felt::Felt;

println!("Felf one hex: {:?}", Felt::ONE.to_hex_string());
let input_nf_key = Felt::ONE;
let input_npk = poseidon_hash(input_nf_key, Felt::ZERO);
println!("input_npk: {:?}", input_npk.to_bytes_be());
println!("input_npk: {:?}", input_npk.to_hex_string());
}
Loading
Loading