Skip to content

Commit

Permalink
Dori/ec op builtin (#1657)
Browse files Browse the repository at this point in the history
  • Loading branch information
dorimedini-starkware committed Jan 9, 2023
1 parent 0574b78 commit 0cc4760
Show file tree
Hide file tree
Showing 15 changed files with 124 additions and 7 deletions.
10 changes: 10 additions & 0 deletions corelib/ec.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
extern type EcOp;
#[derive(Copy, Drop)]
extern type EcPoint;
#[derive(Copy, Drop)]
Expand All @@ -10,3 +11,12 @@ extern fn ec_init_state() -> EcState nopanic;
extern fn ec_add_to_state(s: EcState, p: EcPoint) -> EcState nopanic;
#[panic_with('not on EC', ec_finalize_state)]
extern fn ec_try_finalize_state(s: EcState) -> Option::<EcPoint> nopanic;
extern fn ec_op_builtin(s: EcState, m: felt, p: EcPoint) -> EcState implicits(EcOp) nopanic;

#[panic_with('not on EC', ec_op)]
fn ec_try_op(p: EcPoint, m: felt, q: EcPoint) -> Option::<EcPoint> implicits(EcOp) nopanic {
let s = ec_init_state();
let sp = ec_add_to_state(s, p);
let final_state = ec_op_builtin(sp, m, q);
ec_try_finalize_state(final_state)
}
4 changes: 4 additions & 0 deletions corelib/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,19 @@ use option::Option;

// EC.
mod ec;
use ec::EcOp;
use ec::EcPoint;
use ec::EcState;
use ec::ec_add_to_state;
use ec::ec_finalize_state;
use ec::ec_init_state;
use ec::ec_op;
use ec::ec_op_builtin;
use ec::ec_point_from_felts;
use ec::ec_point_try_create;
use ec::ec_point_unwrap;
use ec::ec_try_finalize_state;
use ec::ec_try_op;

// Integer.
mod integer;
Expand Down
17 changes: 17 additions & 0 deletions corelib/test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ fn test_ec_operations() {
let (qx, qy) = ec_point_unwrap(q);
assert(qx == x, 'bad finalize x');
assert(qy == y, 'bad finalize y');
// Try doing the same thing with the EC op builtin.
let state3 = ec_op_builtin(state, 1, p);
let q3 = ec_finalize_state(state3);
let (qx, qy) = ec_point_unwrap(q);
assert(qx == x, 'bad EC op x');
assert(qy == y, 'bad EC op y');
// Try computing `p + p` using the ec_op function.
let double_p = ec_op(p, 1, p);
let (double_x, double_y) = ec_point_unwrap(double_p);
assert(
double_x == 75984168971785666410219869038140038216102669781812169677875295511117260233,
'bad double x'
);
assert(
double_y == 3572434102142093425782752266058856056057826477682467661647843687948039943621,
'bad double y'
);
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions crates/cairo-lang-runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ impl SierraCasmRunner {
} else {
*ty != "RangeCheck".into()
&& *ty != "Bitwise".into()
&& *ty != "EcOp".into()
&& *ty != "Pedersen".into()
&& *ty != "System".into()
&& *ty != "DictManager".into()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub fn core_libfunc_ap_change(libfunc: &CoreConcreteLibfunc) -> Vec<ApChange> {
EcConcreteLibfunc::CreatePoint(_) => vec![ApChange::Known(6), ApChange::Known(6)],
EcConcreteLibfunc::FinalizeState(_) => vec![ApChange::Known(11), ApChange::Known(3)],
EcConcreteLibfunc::InitState(_) => vec![ApChange::Known(8)],
EcConcreteLibfunc::Op(_) => vec![ApChange::Known(0)],
EcConcreteLibfunc::UnwrapPoint(_) => vec![ApChange::Known(0)],
},
CoreConcreteLibfunc::Drop(_) | CoreConcreteLibfunc::Dup(_) => vec![ApChange::Known(0)],
Expand Down
3 changes: 3 additions & 0 deletions crates/cairo-lang-sierra-gas/src/core_libfunc_cost_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ pub fn core_libfunc_cost_base<Ops: CostOperations>(
Ec(EcConcreteLibfunc::CreatePoint(_)) => vec![ops.const_cost(6), ops.const_cost(6)],
Ec(EcConcreteLibfunc::FinalizeState(_)) => vec![ops.const_cost(13), ops.const_cost(6)],
Ec(EcConcreteLibfunc::InitState(_)) => vec![ops.const_cost(8)],
Ec(EcConcreteLibfunc::Op(_)) => {
vec![ops.add(ops.const_cost(5), ops.const_cost_token(1, CostTokenType::EcOp))]
}
Ec(EcConcreteLibfunc::UnwrapPoint(_)) => vec![ops.const_cost(0)],
Gas(GetGas(_)) => {
vec![
Expand Down
35 changes: 35 additions & 0 deletions crates/cairo-lang-sierra-to-casm/src/invocations/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn build(
EcConcreteLibfunc::CreatePoint(_) => build_ec_point_try_create(builder),
EcConcreteLibfunc::FinalizeState(_) => build_ec_try_finalize_state(builder),
EcConcreteLibfunc::InitState(_) => build_ec_init_state(builder),
EcConcreteLibfunc::Op(_) => build_ec_op_builtin(builder),
EcConcreteLibfunc::UnwrapPoint(_) => build_ec_point_unwrap(builder),
}
}
Expand Down Expand Up @@ -239,3 +240,37 @@ fn build_ec_try_finalize_state(
],
))
}

/// Handles instruction for computing `S + M * Q` where `S` is an EC state, `M` is a scalar (felt)
/// and `Q` is an EC point.
fn build_ec_op_builtin(
builder: CompiledInvocationBuilder<'_>,
) -> Result<CompiledInvocation, InvocationError> {
let [ec_builtin_expr, expr_state, expr_m, expr_point] = builder.try_get_refs()?;
let ec_builtin = ec_builtin_expr.try_unpack_single()?.to_buffer(6)?;
let [sx, sy, random_ptr] = expr_state.try_unpack()?;
let [m] = expr_m.try_unpack()?;
let [px, py] = expr_point.try_unpack()?;

let mut casm_builder = CasmBuilder::default();
let ec_builtin = casm_builder.add_var(ec_builtin);
let sx = casm_builder.add_var(ResOperand::Deref(sx.to_deref()?));
let sy = casm_builder.add_var(ResOperand::Deref(sy.to_deref()?));
let random_ptr = casm_builder.add_var(ResOperand::Deref(random_ptr.to_deref()?));
let px = casm_builder.add_var(ResOperand::Deref(px.to_deref()?));
let py = casm_builder.add_var(ResOperand::Deref(py.to_deref()?));
let m = casm_builder.add_var(ResOperand::Deref(m.to_deref()?));
casm_build_extend! {casm_builder,
assert sx = *(ec_builtin++);
assert sy = *(ec_builtin++);
assert px = *(ec_builtin++);
assert py = *(ec_builtin++);
assert m = *(ec_builtin++);
let result_x = *(ec_builtin++);
let result_y = *(ec_builtin++);
};
Ok(builder.build_from_casm_builder(
casm_builder,
[("Fallthrough", &[&[ec_builtin], &[result_x, result_y, random_ptr]], None)],
))
}
1 change: 1 addition & 0 deletions crates/cairo-lang-sierra-to-casm/src/type_sizes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn get_type_size_map(
| CoreTypeConcrete::GasBuiltin(_)
| CoreTypeConcrete::Bitwise(_)
| CoreTypeConcrete::BuiltinCosts(_)
| CoreTypeConcrete::EcOp(_)
| CoreTypeConcrete::Nullable(_)
| CoreTypeConcrete::Uint128(_)
| CoreTypeConcrete::RangeCheck(_)
Expand Down
3 changes: 2 additions & 1 deletion crates/cairo-lang-sierra/src/extensions/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::dict_felt_to::{DictFeltToLibfunc, DictFeltToType};
use super::dict_manager::DictManagerType;
use super::drop::DropLibfunc;
use super::duplicate::DupLibfunc;
use super::ec::{EcLibfunc, EcPointType, EcStateType};
use super::ec::{EcLibfunc, EcOpType, EcPointType, EcStateType};
use super::enm::{EnumLibfunc, EnumType};
use super::modules::boxing::{BoxLibfunc, BoxType};
use super::modules::felt::{FeltLibfunc, FeltType};
Expand All @@ -33,6 +33,7 @@ define_type_hierarchy! {
Array(ArrayType),
Bitwise(BitwiseType),
Box(BoxType),
EcOp(EcOpType),
EcPoint(EcPointType),
EcState(EcStateType),
Felt(FeltType),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ pub enum CostTokenType {
Pedersen,
/// One invocation of the bitwise builtin.
Bitwise,
/// One invocation of the EC op builtin.
EcOp,
}
impl CostTokenType {
pub fn iter() -> std::slice::Iter<'static, Self> {
[CostTokenType::Step, CostTokenType::Pedersen, CostTokenType::Bitwise].iter()
[CostTokenType::Step, CostTokenType::Pedersen, CostTokenType::Bitwise, CostTokenType::EcOp]
.iter()
}

/// Returns the name of the token type, in snake_case.
Expand All @@ -34,6 +37,7 @@ impl CostTokenType {
CostTokenType::Step => "step",
CostTokenType::Pedersen => "pedersen",
CostTokenType::Bitwise => "bitwise",
CostTokenType::EcOp => "ec_op",
}
.into()
}
Expand All @@ -47,6 +51,7 @@ impl CostTokenType {
CostTokenType::Step => panic!("offset_in_builtin_costs is not supported for 'Step'."),
CostTokenType::Pedersen => 0,
CostTokenType::Bitwise => 1,
CostTokenType::EcOp => 2,
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions crates/cairo-lang-sierra/src/extensions/modules/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ define_libfunc_hierarchy! {
CreatePoint(EcCreatePointLibfunc),
FinalizeState(EcFinalizeStateLibfunc),
InitState(EcInitStateLibfunc),
Op(EcOpLibfunc),
UnwrapPoint(EcUnwrapPointLibfunc),
}, EcConcreteLibfunc
}
Expand Down Expand Up @@ -189,3 +190,40 @@ impl NoGenericArgsGenericLibfunc for EcFinalizeStateLibfunc {
})
}
}

/// Libfunc for applying the EC op builtin: given an EC state `S`, a scalar `M` and an EC point `Q`,
/// computes a new EC state `S + M * Q`.
#[derive(Default)]
pub struct EcOpLibfunc {}
impl NoGenericArgsGenericLibfunc for EcOpLibfunc {
const ID: GenericLibfuncId = GenericLibfuncId::new_inline("ec_op_builtin");

fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
) -> Result<LibfuncSignature, SpecializationError> {
let ec_builtin_ty = context.get_concrete_type(EcOpType::id(), &[])?;
let ec_state_ty = context.get_concrete_type(EcStateType::id(), &[])?;
Ok(LibfuncSignature::new_non_branch(
vec![
ec_builtin_ty.clone(),
ec_state_ty.clone(),
context.get_concrete_type(FeltType::id(), &[])?,
context.get_concrete_type(EcPointType::id(), &[])?,
],
vec![
OutputVarInfo {
ty: ec_builtin_ty,
ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::AddConst {
param_idx: 0,
}),
},
OutputVarInfo {
ty: ec_state_ty,
ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
},
],
SierraApChange::Known { new_vars_only: true },
))
}
}
3 changes: 2 additions & 1 deletion crates/cairo-lang-sierra/src/simulation/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::extensions::core::CoreConcreteLibfunc::{
};
use crate::extensions::dict_felt_to::DictFeltToConcreteLibfunc;
use crate::extensions::ec::EcConcreteLibfunc::{
AddToState, CreatePoint, FinalizeState, InitState, UnwrapPoint,
AddToState, CreatePoint, FinalizeState, InitState, Op, UnwrapPoint,
};
use crate::extensions::enm::{EnumConcreteLibfunc, EnumInitConcreteLibfunc};
use crate::extensions::felt::{
Expand Down Expand Up @@ -91,6 +91,7 @@ pub fn simulate<
},
Ec(FinalizeState(_)) => todo!(),
Ec(InitState(_)) => todo!(),
Ec(Op(_)) => todo!(),
Ec(UnwrapPoint(_)) => match &inputs[..] {
[CoreValue::EcPoint(x, y)] => {
Ok((vec![CoreValue::Felt(x.clone()), CoreValue::Felt(y.clone())], 0))
Expand Down
2 changes: 1 addition & 1 deletion crates/cairo-lang-starknet/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn get_starknet_database() -> RootDatabase {

// Override implicit precedence for compatibility with the StarkNet OS.
db.set_implicit_precedence(Arc::new(
["Pedersen", "RangeCheck", "Bitwise", "GasBuiltin", "System"]
["Pedersen", "RangeCheck", "Bitwise", "EcOp", "GasBuiltin", "System"]
.iter()
.map(|name| get_core_ty_by_name(db, name.into(), vec![]))
.collect_vec(),
Expand Down
2 changes: 1 addition & 1 deletion tests/examples_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ fn lowering_test(name: &str) {
"hash_chain")]
#[test_case(
"hash_chain_gas",
&[3].map(BigInt::from), Some(100000), Some(9327 + 3 * DUMMY_BUILTIN_GAS_COST) =>
&[3].map(BigInt::from), Some(100000), Some(9330 + 3 * DUMMY_BUILTIN_GAS_COST) =>
RunResultValue::Success(vec![BigInt::parse_bytes(
b"2dca1ad81a6107a9ef68c69f791bcdbda1df257aab76bd43ded73d96ed6227d", 16).unwrap()]);
"hash_chain_gas")]
Expand Down
4 changes: 2 additions & 2 deletions tests/test_data/hash_chain_gas.casm
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ ret;
call rel 72;
[ap + 0] = [ap + -1] + 71, ap++;
[ap + 0] = [[ap + -1] + 0], ap++;
ap += 3;
[ap + 1] = [[ap + -4] + 0], ap++;
ap += 6;
[ap + 1] = [[ap + -7] + 0], ap++;
[ap + 1] = [ap + 0] + 3100, ap++;
%{ memory[ap + 1] = memory[ap + 0] <= memory[fp + -5] %}
jmp rel 8 if [ap + 1] != 0, ap++;
Expand Down

0 comments on commit 0cc4760

Please sign in to comment.