This repository has been archived by the owner on Dec 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 76
Add cairo1 compilers #348
Draft
ericnordelo
wants to merge
3
commits into
OpenZeppelin:main
Choose a base branch
from
ericnordelo:feat/cairo-0.11-#304
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Add cairo1 compilers #348
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
include LICENSE | ||
recursive-include src/nile/artifacts/ * | ||
recursive-include src/nile/base_project/ * | ||
recursive-include src/nile/core/cairo1/compilers/ * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
"""Cairo1 common.""" | ||
|
||
from pathlib import Path | ||
|
||
COMPILERS_BIN_PATH = Path(__file__).parent / "./compilers/src/bin" | ||
BUILD_DIRECTORY = "artifacts/cairo1" | ||
ABIS_DIRECTORY = f"{BUILD_DIRECTORY}/abis" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
"""Command to compile cairo1 files.""" | ||
import json | ||
import logging | ||
import os | ||
import subprocess | ||
|
||
from nile.common import CONTRACTS_DIRECTORY, get_all_contracts | ||
from nile.core.cairo1.common import ABIS_DIRECTORY, BUILD_DIRECTORY, COMPILERS_BIN_PATH | ||
|
||
|
||
def compile( | ||
contracts, | ||
directory=None, | ||
): | ||
"""Compile command.""" | ||
contracts_directory = directory if directory else CONTRACTS_DIRECTORY | ||
|
||
if not os.path.exists(ABIS_DIRECTORY): | ||
logging.info(f"📁 Creating {ABIS_DIRECTORY} to store compilation artifacts") | ||
os.makedirs(ABIS_DIRECTORY, exist_ok=True) | ||
|
||
all_contracts = contracts | ||
|
||
if len(contracts) == 0: | ||
logging.info( | ||
f"🤖 Compiling all Cairo contracts in the {contracts_directory} directory" | ||
) | ||
all_contracts = get_all_contracts(directory=contracts_directory) | ||
|
||
results = [] | ||
|
||
for contract in all_contracts: | ||
status_code, sierra_file, filename = _compile_to_sierra( | ||
contract, contracts_directory | ||
) | ||
results.append(status_code) | ||
if status_code == 0: | ||
_extract_abi(sierra_file, filename) | ||
_compile_to_casm(sierra_file, filename) | ||
|
||
failed_contracts = [c for (c, r) in zip(all_contracts, results) if r != 0] | ||
failures = len(failed_contracts) | ||
|
||
if failures == 0: | ||
logging.info("✅ Done") | ||
else: | ||
exp = f"{failures} contract" | ||
if failures > 1: | ||
exp += "s" # pluralize | ||
logging.info(f"🛑 Failed to compile the following {exp}:") | ||
for contract in failed_contracts: | ||
logging.info(f" {contract}") | ||
|
||
|
||
def _compile_to_sierra( | ||
path, | ||
directory=None, | ||
): | ||
"""Compile from Cairo1 to Sierra.""" | ||
base = os.path.basename(path) | ||
filename = os.path.splitext(base)[0] | ||
logging.info(f"🔨 Compiling {path}") | ||
|
||
sierra_file = f"{BUILD_DIRECTORY}/{filename}.sierra" | ||
|
||
cmd = f""" | ||
{COMPILERS_BIN_PATH}/starknet-compile {path} \ | ||
{sierra_file} | ||
""" | ||
|
||
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) | ||
process.communicate() | ||
|
||
return process.returncode, sierra_file, filename | ||
|
||
|
||
def _compile_to_casm(sierra_file, filename): | ||
"""Compile from Sierra to Casm.""" | ||
cmd = f""" | ||
{COMPILERS_BIN_PATH}/starknet-sierra-compile {sierra_file} \ | ||
{BUILD_DIRECTORY}/{filename}.casm | ||
""" | ||
|
||
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) | ||
process.communicate() | ||
return process.returncode | ||
|
||
|
||
def _extract_abi(sierra_file, filename): | ||
with open(sierra_file, "r") as f: | ||
data = json.load(f) | ||
|
||
with open(f"{ABIS_DIRECTORY}/{filename}.json", "w") as f: | ||
json.dump(data["abi"], f, indent=2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
extern type Array<T>; | ||
extern fn array_new<T>() -> Array::<T> nopanic; | ||
extern fn array_append<T>(ref arr: Array::<T>, value: T) nopanic; | ||
extern fn array_pop_front<T>(ref arr: Array::<T>) -> Option::<T> nopanic; | ||
#[panic_with('Array out of bounds', array_at)] | ||
extern fn array_get<T>( | ||
ref arr: Array::<T>, index: usize | ||
) -> Option::<T> implicits(RangeCheck) nopanic; | ||
extern fn array_len<T>(ref arr: Array::<T>) -> usize nopanic; | ||
|
||
trait ArrayTrait<T> { | ||
fn new() -> Array::<T>; | ||
fn append(ref self: Array::<T>, value: T); | ||
fn pop_front(ref self: Array::<T>) -> Option::<T>; | ||
fn get(ref self: Array::<T>, index: usize) -> Option::<T>; | ||
fn at(ref self: Array::<T>, index: usize) -> T; | ||
fn len(ref self: Array::<T>) -> usize; | ||
fn is_empty(ref self: Array::<T>) -> bool; | ||
} | ||
impl ArrayImpl<T> of ArrayTrait::<T> { | ||
#[inline(always)] | ||
fn new() -> Array::<T> { | ||
array_new() | ||
} | ||
#[inline(always)] | ||
fn append(ref self: Array::<T>, value: T) { | ||
array_append(ref self, value) | ||
} | ||
#[inline(always)] | ||
fn pop_front(ref self: Array::<T>) -> Option::<T> { | ||
array_pop_front(ref self) | ||
} | ||
#[inline(always)] | ||
fn get(ref self: Array::<T>, index: usize) -> Option::<T> { | ||
array_get(ref self, index) | ||
} | ||
fn at(ref self: Array::<T>, index: usize) -> T { | ||
array_at(ref self, index) | ||
} | ||
#[inline(always)] | ||
fn len(ref self: Array::<T>) -> usize { | ||
array_len(ref self) | ||
} | ||
#[inline(always)] | ||
fn is_empty(ref self: Array::<T>) -> bool { | ||
self.len() == 0_usize | ||
} | ||
} | ||
|
||
// Impls for common generic types | ||
impl ArrayFeltDrop of Drop::<Array::<felt>>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
extern type Box<T>; | ||
impl BoxFeltCopy of Copy::<Box::<felt>>; | ||
impl BoxFeltDrop of Drop::<Box::<felt>>; | ||
|
||
extern fn into_box<T>(value: T) -> Box::<T> nopanic; | ||
extern fn unbox<T>(box: Box::<T>) -> T nopanic; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[crate_roots] | ||
core = "." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
use array::ArrayTrait; | ||
|
||
extern fn print(message: Array::<felt>) nopanic; | ||
|
||
fn print_felt(message: felt) { | ||
let mut arr = ArrayTrait::new(); | ||
arr.append(message); | ||
print(arr); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
extern type DictManager; | ||
extern type DictFeltTo<T>; | ||
extern type SquashedDictFeltTo<T>; | ||
impl SquashedDictFeltToFeltDrop of Drop::<SquashedDictFeltTo::<felt>>; | ||
|
||
extern fn dict_felt_to_new<T>() -> DictFeltTo::<T> implicits(DictManager) nopanic; | ||
extern fn dict_felt_to_write<T>(ref dict: DictFeltTo::<T>, key: felt, value: T) nopanic; | ||
extern fn dict_felt_to_read<T>(ref dict: DictFeltTo::<T>, key: felt) -> T nopanic; | ||
extern fn dict_felt_to_squash<T>( | ||
dict: DictFeltTo::<T> | ||
) -> SquashedDictFeltTo::<T> implicits(RangeCheck, GasBuiltin, DictManager) nopanic; | ||
|
||
trait DictFeltToTrait<T> { | ||
fn new() -> DictFeltTo::<T>; | ||
fn insert(ref self: DictFeltTo::<T>, key: felt, value: T); | ||
fn get(ref self: DictFeltTo::<T>, key: felt) -> T; | ||
fn squash(self: DictFeltTo::<T>) -> SquashedDictFeltTo::<T>; | ||
} | ||
impl DictFeltToImpl<T> of DictFeltToTrait::<T> { | ||
fn new() -> DictFeltTo::<T> { | ||
dict_felt_to_new() | ||
} | ||
fn insert(ref self: DictFeltTo::<T>, key: felt, value: T) { | ||
dict_felt_to_write(ref self, key, value) | ||
} | ||
fn get(ref self: DictFeltTo::<T>, key: felt) -> T { | ||
dict_felt_to_read(ref self, key) | ||
} | ||
fn squash(self: DictFeltTo::<T>) -> SquashedDictFeltTo::<T> { | ||
dict_felt_to_squash(self) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
mod StarkCurve { | ||
/// The STARK Curve is defined by the equation `y^2 = x^3 + ALPHA*x + BETA`. | ||
const ALPHA: felt = 1; | ||
/// The STARK Curve is defined by the equation `y^2 = x^3 + ALPHA*x + BETA`. | ||
const BETA: felt = 0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89; | ||
/// The order (number of points) of the STARK Curve. | ||
const ORDER: felt = 0x800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f; | ||
/// The x coordinate of the generator point used in the ECDSA signature. | ||
const GEN_X: felt = 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca; | ||
/// The y coordinate of the generator point used in the ECDSA signature. | ||
const GEN_Y: felt = 0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f; | ||
} | ||
|
||
extern type EcOp; | ||
#[derive(Copy, Drop)] | ||
extern type EcPoint; | ||
type NonZeroEcPoint = NonZero::<EcPoint>; | ||
|
||
impl NonZeroEcPointCopy of Copy::<NonZeroEcPoint>; | ||
impl OptionNonZeroEcPointCopy of Copy::<Option::<NonZeroEcPoint>>; | ||
impl NonZeroEcPointDrop of Drop::<NonZeroEcPoint>; | ||
|
||
/// Returns the zero point of the curve ("the point at infinity"). | ||
extern fn ec_point_zero() -> EcPoint nopanic; | ||
/// Constructs a non-zero point from its (x, y) coordinates. | ||
/// | ||
/// * `ec_point_try_new_nz` returns `None` if the point (x, y) is not on the curve. | ||
/// * `ec_point_new_nz` panics in that case. | ||
#[panic_with('not on EC', ec_point_new_nz)] | ||
extern fn ec_point_try_new_nz(x: felt, y: felt) -> Option::<NonZeroEcPoint> nopanic; | ||
|
||
#[inline(always)] | ||
fn ec_point_try_new(x: felt, y: felt) -> Option::<EcPoint> { | ||
match ec_point_try_new_nz(:x, :y) { | ||
Option::Some(pt) => Option::Some(unwrap_nz(pt)), | ||
Option::None(()) => Option::None(()), | ||
} | ||
} | ||
|
||
fn ec_point_new(x: felt, y: felt) -> EcPoint { | ||
unwrap_nz(ec_point_new_nz(:x, :y)) | ||
} | ||
|
||
extern fn ec_point_from_x_nz(x: felt) -> Option::<NonZeroEcPoint> nopanic; | ||
|
||
#[inline(always)] | ||
fn ec_point_from_x(x: felt) -> Option::<EcPoint> { | ||
match ec_point_from_x_nz(:x) { | ||
Option::Some(pt) => Option::Some(unwrap_nz(pt)), | ||
Option::None(()) => Option::None(()), | ||
} | ||
} | ||
|
||
extern fn ec_point_unwrap(p: NonZeroEcPoint) -> (felt, felt) nopanic; | ||
/// Computes the negation of an elliptic curve point (-p). | ||
extern fn ec_neg(p: EcPoint) -> EcPoint nopanic; | ||
/// Checks whether the given `EcPoint` is the zero point. | ||
extern fn ec_point_is_zero(p: EcPoint) -> IsZeroResult::<EcPoint> nopanic; | ||
|
||
/// Converts `p` to `NonZeroEcPoint`. Panics if `p` is the zero point. | ||
fn ec_point_non_zero(p: EcPoint) -> NonZeroEcPoint { | ||
match ec_point_is_zero(p) { | ||
IsZeroResult::Zero(()) => { | ||
let mut data = array_new(); | ||
array_append(ref data, 'Zero point'); | ||
panic(data) | ||
}, | ||
IsZeroResult::NonZero(p_nz) => p_nz, | ||
} | ||
} | ||
|
||
// EC state. | ||
|
||
// TODO(lior): Allow explicit clone() for EcState, since we don't allow implicit dup (Copy). | ||
#[derive(Drop)] | ||
extern type EcState; | ||
|
||
/// Initializes an EC computation with the zero point. | ||
extern fn ec_state_init() -> EcState nopanic; | ||
/// Adds a point to the computation. | ||
extern fn ec_state_add(ref s: EcState, p: NonZeroEcPoint) nopanic; | ||
/// Finalizes the EC computation and returns the result (returns `None` if the result is the | ||
/// zero point). | ||
extern fn ec_state_try_finalize_nz(s: EcState) -> Option::<NonZeroEcPoint> nopanic; | ||
/// Adds the product p * m to the state. | ||
extern fn ec_state_add_mul(ref s: EcState, m: felt, p: NonZeroEcPoint) implicits(EcOp) nopanic; | ||
|
||
/// Finalizes the EC computation and returns the result. | ||
#[inline(always)] | ||
fn ec_state_finalize(s: EcState) -> EcPoint nopanic { | ||
match ec_state_try_finalize_nz(s) { | ||
Option::Some(pt) => unwrap_nz(pt), | ||
Option::None(()) => ec_point_zero(), | ||
} | ||
} | ||
|
||
/// Computes the product of an EC point `p` by the given scalar `m`. | ||
fn ec_mul(p: EcPoint, m: felt) -> EcPoint { | ||
match ec_point_is_zero(p) { | ||
IsZeroResult::Zero(()) => p, | ||
IsZeroResult::NonZero(p_nz) => { | ||
let mut state = ec_state_init(); | ||
ec_state_add_mul(ref state, m, p_nz); | ||
ec_state_finalize(state) | ||
} | ||
} | ||
} | ||
|
||
impl EcPointAdd of Add::<EcPoint> { | ||
/// Computes the sum of two points on the curve. | ||
// TODO(lior): Implement using a libfunc to make it more efficient. | ||
fn add(p: EcPoint, q: EcPoint) -> EcPoint { | ||
let p_nz = match ec_point_is_zero(p) { | ||
IsZeroResult::Zero(()) => { | ||
return q; | ||
}, | ||
IsZeroResult::NonZero(pt) => pt, | ||
}; | ||
let q_nz = match ec_point_is_zero(q) { | ||
IsZeroResult::Zero(()) => { | ||
return p; | ||
}, | ||
IsZeroResult::NonZero(pt) => pt, | ||
}; | ||
let mut state = ec_state_init(); | ||
ec_state_add(ref state, p_nz); | ||
ec_state_add(ref state, q_nz); | ||
ec_state_finalize(state) | ||
} | ||
} | ||
|
||
impl EcPointSub of Sub::<EcPoint> { | ||
/// Computes the difference between two points on the curve. | ||
fn sub(p: EcPoint, q: EcPoint) -> EcPoint { | ||
match ec_point_is_zero(q) { | ||
IsZeroResult::Zero(()) => { | ||
// p - 0 = p. | ||
return p; | ||
}, | ||
IsZeroResult::NonZero(_) => {}, | ||
}; | ||
// p - q = p + (-q). | ||
p + ec_neg(q) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we can remove this file and just replace variables in good ol'
common/
if we drop Cairo0 support