Skip to content

Commit

Permalink
feat(blockifier): add Secp256r1 cairo native syscalls
Browse files Browse the repository at this point in the history
  • Loading branch information
PearsonWhite committed Nov 8, 2024
1 parent f1915c4 commit 6558eea
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 25 deletions.
2 changes: 1 addition & 1 deletion crates/blockifier/cairo_native
Submodule cairo_native updated 51 files
+36 −0 .github/actions/install-linux-deps/action.yml
+5 −5 .github/workflows/bench-hyperfine.yml
+12 −12 .github/workflows/ci.yml
+95 −0 .github/workflows/daily.yml
+2 −2 .github/workflows/publish.yml
+2 −2 .github/workflows/release.yml
+2 −2 .github/workflows/rustdoc.yml
+132 −0 .github/workflows/starknet-blocks.yml
+51 −51 Cargo.lock
+6 −80 benches/benches.rs
+17 −0 benches/compile_time.rs
+54 −3 benches/libfuncs.rs
+33 −45 docs/implementing_libfuncs.md
+0 −5 programs/benches/factorial_2M.c
+0 −5 programs/benches/fib_2M.c
+0 −5 programs/benches/logistic_map.c
+42 −48 programs/compile_benches/dijkstra.cairo
+8 −17 programs/compile_benches/extended_euclidean_algorithm.cairo
+78 −27 programs/compile_benches/fast_power.cairo
+0 −285 programs/compile_benches/sha256.cairo
+539 −0 programs/compile_benches/sha512.cairo
+5 −2 runtime/Cargo.toml
+236 −6 runtime/src/lib.rs
+1 −1 rust-toolchain.toml
+48 −0 scripts/cmp_state_dumps.sh
+32 −0 scripts/diff-check.sh
+6 −6 src/arch.rs
+2 −26 src/compiler.rs
+15 −1 src/context.rs
+0 −3 src/error.rs
+30 −58 src/executor.rs
+0 −12 src/executor/aot.rs
+13 −66 src/executor/contract.rs
+0 −13 src/executor/jit.rs
+25 −0 src/ffi.rs
+5 −6 src/libfuncs/array.rs
+5 −3 src/libfuncs/circuit.rs
+18 −121 src/libfuncs/gas.rs
+290 −595 src/libfuncs/starknet.rs
+676 −1,507 src/libfuncs/starknet/secp256.rs
+6 −9 src/metadata/gas.rs
+4 −6 src/types.rs
+19 −24 src/types/array.rs
+2 −6 src/types/builtin_costs.rs
+3 −45 src/utils.rs
+87 −1 src/utils/block_ext.rs
+2 −2 src/values.rs
+6 −6 tests/alexandria/Scarb.lock
+4 −4 tests/alexandria/Scarb.toml
+6 −12 tests/common.rs
+0 −2 tests/tests/boolean.rs
236 changes: 212 additions & 24 deletions crates/blockifier/src/execution/native/syscall_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ use cairo_native::starknet::{
SyscallResult,
U256,
};
use cairo_native::starknet_stub::u256_to_biguint;
use cairo_native::starknet_stub::{big4int_to_u256, encode_str_as_felts, u256_to_biguint};
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use cairo_vm::with_std::fmt;
use num_traits::Zero;
use starknet_api::state::StorageKey;
use starknet_types_core::felt::Felt;

Expand Down Expand Up @@ -275,55 +277,241 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> {

fn secp256r1_new(
&mut self,
_x: U256,
_y: U256,
_remaining_gas: &mut u128,
x: U256,
y: U256,
remaining_gas: &mut u128,
) -> SyscallResult<Option<Secp256r1Point>> {
todo!("Implement secp256r1_new syscall.");
self.substract_syscall_gas_cost(
remaining_gas,
SyscallSelector::Secp256r1New,
self.context.gas_costs().secp256r1_new_gas_cost,
)?;

Secp256Point::new(x, y).map(|op| op.map(|p| p.into()))
}

fn secp256r1_add(
&mut self,
_p0: Secp256r1Point,
_p1: Secp256r1Point,
_remaining_gas: &mut u128,
p0: Secp256r1Point,
p1: Secp256r1Point,
remaining_gas: &mut u128,
) -> SyscallResult<Secp256r1Point> {
todo!("Implement secp256r1_add syscall.");
self.substract_syscall_gas_cost(
remaining_gas,
SyscallSelector::Secp256r1Add,
self.context.gas_costs().secp256r1_new_gas_cost,
)?;
Ok(Secp256Point::add(p0.into(), p1.into()).into())
}

fn secp256r1_mul(
&mut self,
_p: Secp256r1Point,
_m: U256,
_remaining_gas: &mut u128,
p: Secp256r1Point,
m: U256,
remaining_gas: &mut u128,
) -> SyscallResult<Secp256r1Point> {
todo!("Implement secp256r1_mul syscall.");
self.substract_syscall_gas_cost(
remaining_gas,
SyscallSelector::Secp256r1Mul,
self.context.gas_costs().secp256r1_new_gas_cost,
)?;

Ok(Secp256Point::mul(p.into(), m).into())
}

fn secp256r1_get_point_from_x(
&mut self,
_x: U256,
_y_parity: bool,
_remaining_gas: &mut u128,
x: U256,
y_parity: bool,
remaining_gas: &mut u128,
) -> SyscallResult<Option<Secp256r1Point>> {
todo!("Implement secp256r1_get_point_from_x syscall.");
self.substract_syscall_gas_cost(
remaining_gas,
SyscallSelector::Secp256r1GetPointFromX,
self.context.gas_costs().secp256r1_new_gas_cost,
)?;

Secp256Point::get_point_from_x(x, y_parity).map(|op| op.map(|p| p.into()))
}

fn secp256r1_get_xy(
&mut self,
_p: Secp256r1Point,
_remaining_gas: &mut u128,
p: Secp256r1Point,
remaining_gas: &mut u128,
) -> SyscallResult<(U256, U256)> {
todo!("Implement secp256r1_get_xy syscall.");
self.substract_syscall_gas_cost(
remaining_gas,
SyscallSelector::Secp256r1GetXy,
self.context.gas_costs().secp256r1_get_xy_gas_cost,
)?;

Ok((p.x, p.y))
}

fn sha256_process_block(
&mut self,
_prev_state: &mut [u32; 8],
_current_block: &[u32; 16],
_remaining_gas: &mut u128,
prev_state: &mut [u32; 8],
current_block: &[u32; 16],
remaining_gas: &mut u128,
) -> SyscallResult<()> {
todo!("Implement sha256_process_block syscall.");
const SHA256_STATE_SIZE: usize = 8;
self.substract_syscall_gas_cost(
remaining_gas,
SyscallSelector::Sha256ProcessBlock,
self.context.gas_costs().sha256_process_block_gas_cost,
)?;

let data_as_bytes = sha2::digest::generic_array::GenericArray::from_exact_iter(
current_block.iter().flat_map(|x| x.to_be_bytes()),
)
.expect(
"u32.to_be_bytes() returns 4 bytes, and data.len() == 16. So data contains 64 bytes.",
);
let mut state: [u32; SHA256_STATE_SIZE] = *prev_state;
sha2::compress256(&mut state, &[data_as_bytes]);

prev_state.copy_from_slice(&state);

Ok(())
}
}

// From cairo_native/src/starknet_stub.rs

#[derive(PartialEq, Clone, Copy)]
struct Secp256Point<Curve: SWCurveConfig>(Affine<Curve>);

impl<Curve: SWCurveConfig> fmt::Debug for Secp256Point<Curve> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Secp256Point").field(&self.0).finish()
}
}

impl From<Secp256Point<ark_secp256k1::Config>> for Secp256k1Point {
fn from(Secp256Point(Affine { x, y, infinity }): Secp256Point<ark_secp256k1::Config>) -> Self {
Secp256k1Point {
x: big4int_to_u256(x.into()),
y: big4int_to_u256(y.into()),
is_infinity: infinity,
}
}
}

impl From<Secp256Point<ark_secp256r1::Config>> for Secp256r1Point {
fn from(Secp256Point(Affine { x, y, infinity }): Secp256Point<ark_secp256r1::Config>) -> Self {
Secp256r1Point {
x: big4int_to_u256(x.into()),
y: big4int_to_u256(y.into()),
is_infinity: infinity,
}
}
}

impl From<Secp256k1Point> for Secp256Point<ark_secp256k1::Config> {
fn from(p: Secp256k1Point) -> Self {
Secp256Point(Affine {
x: u256_to_biguint(p.x).into(),
y: u256_to_biguint(p.y).into(),
infinity: p.is_infinity,
})
}
}

impl From<Secp256r1Point> for Secp256Point<ark_secp256r1::Config> {
fn from(p: Secp256r1Point) -> Self {
Secp256Point(Affine {
x: u256_to_biguint(p.x).into(),
y: u256_to_biguint(p.y).into(),
infinity: p.is_infinity,
})
}
}

// Implementation from: cairo_native/src/starknet_stub.rs

use ark_ff::PrimeField;

impl<Curve: SWCurveConfig> Secp256Point<Curve>
where
Curve::BaseField: PrimeField, // constraint for get_point_by_id
{
// Given a (x,y) pair it will
// - return the point at infinity for (0,0)
// - Err if either x or y is outside of the modulus
// - Ok(None) if (x,y) are within the modules but not on the curve
// - Ok(Some(Point)) if (x,y) are on the curve
fn new(x: U256, y: U256) -> Result<Option<Self>, Vec<Felt>> {
let x = u256_to_biguint(x);
let y = u256_to_biguint(y);
let modulos = Curve::BaseField::MODULUS.into();

if x >= modulos || y >= modulos {
let error = Felt::from_hex(
"0x00000000000000000000000000000000496e76616c696420617267756d656e74",
) // INVALID_ARGUMENT
.map_err(|err| encode_str_as_felts(&err.to_string()))?;

return Err(vec![error]);
}

Ok(maybe_affine(x.into(), y.into()))
}

fn add(p0: Self, p1: Self) -> Self {
let result: Projective<Curve> = p0.0 + p1.0;
Secp256Point(result.into())
}

fn mul(p: Self, m: U256) -> Self {
let result = p.0 * Curve::ScalarField::from(u256_to_biguint(m));
Secp256Point(result.into())
}

fn get_point_from_x(x: U256, y_parity: bool) -> Result<Option<Self>, Vec<Felt>> {
let modulos = Curve::BaseField::MODULUS.into();
let x = u256_to_biguint(x);

if x >= modulos {
let error = Felt::from_hex(
"0x00000000000000000000000000000000496e76616c696420617267756d656e74",
) // INVALID_ARGUMENT
.map_err(|err| encode_str_as_felts(&err.to_string()))?;

return Err(vec![error]);
}

let x = x.into();
let maybe_ec_point = Affine::<Curve>::get_ys_from_x_unchecked(x)
.map(|(smaller, greater)| {
// Return the correct y coordinate based on the parity.
if ark_ff::BigInteger::is_odd(&smaller.into_bigint()) == y_parity {
smaller
} else {
greater
}
})
.map(|y| Affine::<Curve>::new_unchecked(x, y))
.filter(|p| p.is_in_correct_subgroup_assuming_on_curve());

Ok(maybe_ec_point.map(Secp256Point))
}
}

/// Variation on [`Affine<Curve>::new`] that doesn't panic and maps (x,y) = (0,0) -> infinity
fn maybe_affine<Curve: SWCurveConfig>(
x: Curve::BaseField,
y: Curve::BaseField,
) -> Option<Secp256Point<Curve>> {
let ec_point = if x.is_zero() && y.is_zero() {
Affine::<Curve>::identity()
} else {
Affine::<Curve>::new_unchecked(x, y)
};

if ec_point.is_on_curve() && ec_point.is_in_correct_subgroup_assuming_on_curve() {
Some(Secp256Point(ec_point))
} else {
None
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn test_secp256k1(test_contract: FeatureContract, expected_gas: u64) {
);
}

#[test_case(FeatureContract::TestContract(CairoVersion::Native), 674500; "Native")]
#[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 27565680; "VM")]
fn test_secp256r1(test_contract: FeatureContract, expected_gas: u64) {
let chain_info = &ChainInfo::create_for_testing();
Expand Down

0 comments on commit 6558eea

Please sign in to comment.