diff --git a/.tool-versions b/.tool-versions index 07df84b..7d9da02 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1,2 @@ -scarb 2.6.3 \ No newline at end of file +scarb 2.6.3 +starknet-foundry 0.24.0 \ No newline at end of file diff --git a/README.md b/README.md index 2f4bef4..ef8aed7 100644 --- a/README.md +++ b/README.md @@ -14,44 +14,57 @@ To set up the project environment and run the bootloader, follow these steps: 3. **Cairo1 Compilation**: Convert Cairo1 Scarb projects into the Sierra format by running `python compile.py`. -4. **Running the Bootloader**: Start the bootloader with `python run.py`, which will load and execute the compiled Cairo1 program within the Cairo0 environment. +4. **Running the Bootloader**: Start the bootloader with `python run.py`, which will load and execute the compiled Cairo1 contract within the Cairo0 environment. --- -This example showcases the exchange of data between a Cairo0 host provable environment and a Cairo1 program: +This example showcases merge of Cairo0 host provable environment and a Cairo1 developer frendly language: ```cairo -#[derive(Drop, Serde)] -struct Input { - a: u32, - b: u32, - c: u32, +#[starknet::interface] +pub trait IHelloBootloader { + fn main(ref self: TContractState, input: Array) -> Array; } -struct Output { - a_2: u32, - b_2: u32, - c_2: u32, -} +#[starknet::contract] +mod HelloBootloader { + #[derive(Drop, Serde)] + struct Input { + a: u32, + b: u32, + c: u32, + } + + #[derive(Drop, Serde)] + struct Output { + a_2: u32, + b_2: u32, + c_2: u32, + } + + #[storage] + struct Storage {} -fn main(input: Array) -> Output { - let mut input_span = input.span(); - let input = Serde::::deserialize(ref input_span).unwrap(); + #[abi(embed_v0)] + impl HelloBootloaderImpl of super::IHelloBootloader { + fn main(ref self: ContractState, input: Array) -> Array { + let mut input_span = input.span(); + let input = Serde::::deserialize(ref input_span).unwrap(); - let a_2 = input.a * input.a; - let b_2 = input.b * input.b; - let c_2 = input.c * input.c; - assert (a_2 + b_2 == c_2, 'invalid value'); + let a_2 = input.a * input.a; + let b_2 = input.b * input.b; + let c_2 = input.c * input.c; + assert(a_2 + b_2 == c_2, 'invalid value'); - Output { - a_2, - b_2, - c_2, + let mut output = array![]; + Output { a_2, b_2, c_2, }.serialize(ref output); + output + } } } ``` -The Cairo0 bootloader's execution can be verified using a STARK prover like [stone-prover](https://github.com/starkware-libs/stone-prover). +The Cairo0 bootloader's execution can be proven using a STARK prover like [stone-prover](https://github.com/starkware-libs/stone-prover). ## Work in Progress diff --git a/bootloader_input.json b/bootloader_input.json index 508f1ed..6fa3e3b 100644 --- a/bootloader_input.json +++ b/bootloader_input.json @@ -1,10 +1,1486 @@ { - "tasks": [ - { - "type": "CairoSierra", - "path": "cairo1/target/dev/example.sierra.json", - "use_poseidon": true + "compiled_class": { + "prime": "0x800000000000011000000000000000000000000000000000000000000000001", + "compiler_version": "2.6.3", + "bytecode": [ + "0xa0680017fff8000", + "0x7", + "0x482680017ffa8000", + "0x100000000000000000000000000000000", + "0x400280007ff97fff", + "0x10780017fff7fff", + "0xb6", + "0x4825800180007ffa", + "0x0", + "0x400280007ff97fff", + "0x482680017ff98000", + "0x1", + "0x48297ffc80007ffd", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482680017ffc8000", + "0x1", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x0", + "0x480a7ffc7fff8000", + "0x10780017fff7fff", + "0x8", + "0x480a7ffc7fff8000", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0x20", + "0x40780017fff7fff", + "0x1", + "0x48127ff97fff8000", + "0x48127ff77fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ffb7fff8000", + "0x48127ffa7fff8000", + "0x480080007ff88000", + "0x1104800180018000", + "0xa0", + "0x20680017fff7ffa", + "0xb", + "0x48127ff87fff8000", + "0x48127ff87fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x10780017fff7fff", + "0x14", + "0x48127ff87fff8000", + "0x48127ff87fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x48127ffa7fff8000", + "0x48127ff87fff8000", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffd", + "0x64", + "0x48307ffb80007ffc", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0x10", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473", + "0x400080007ffe7fff", + "0x48127ff67fff8000", + "0x48127ff67fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x1104800180018000", + "0x27a", + "0x482480017fff8000", + "0x279", + "0x480080007fff8000", + "0xa0680017fff8000", + "0x9", + "0x4824800180007ff4", + "0x254e", + "0x482480017fff8000", + "0x100000000000000000000000000000000", + "0x400080007ff17fff", + "0x10780017fff7fff", + "0x34", + "0x4824800180007ff4", + "0x254e", + "0x400080007ff27fff", + "0x482480017ff28000", + "0x1", + "0x48127ff67fff8000", + "0x48127ff67fff8000", + "0x1104800180018000", + "0xb3", + "0x20680017fff7ffd", + "0x21", + "0x40780017fff7fff", + "0x1", + "0x48307ffd80007ffe", + "0x400080007ffe7fff", + "0x48127ffa7fff8000", + "0x48127fb67fff8000", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x1104800180018000", + "0x146", + "0x20680017fff7ffd", + "0xa", + "0x48127ffb7fff8000", + "0x48127ffb7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x48127ffb7fff8000", + "0x48127ffb7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x48127ffc7fff8000", + "0x48127fb87fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482480017fef8000", + "0x1", + "0x48127fef7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4661696c656420746f20646573657269616c697a6520706172616d202331", + "0x400080007ffe7fff", + "0x48127ff77fff8000", + "0x48127ff77fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482680017ff98000", + "0x1", + "0x480a7ffa7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0xa0680017fff8000", + "0x7", + "0x482680017ff88000", + "0xfffffffffffffffffffffffffffff6be", + "0x400280007ff77fff", + "0x10780017fff7fff", + "0x43", + "0x4825800180007ff8", + "0x942", + "0x400280007ff77fff", + "0x482680017ff78000", + "0x1", + "0x20780017fff7ffd", + "0xd", + "0x48127fff7fff8000", + "0x48127ffd7fff8000", + "0x480680017fff8000", + "0x0", + "0x480a7ff97fff8000", + "0x480a7ffa7fff8000", + "0x480680017fff8000", + "0x0", + "0x480a7ffb7fff8000", + "0x480a7ffc7fff8000", + "0x208b7fff7fff7ffe", + "0x48297ff980007ffa", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482680017ff98000", + "0x1", + "0x480a7ffa7fff8000", + "0x480680017fff8000", + "0x0", + "0x480280007ff98000", + "0x10780017fff7fff", + "0x8", + "0x480a7ff97fff8000", + "0x480a7ffa7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0xf", + "0x400280007ffc7fff", + "0x48127ffa7fff8000", + "0x48127ff87fff8000", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x480a7ffb7fff8000", + "0x482680017ffc8000", + "0x1", + "0x4825800180007ffd", + "0x1", + "0x1104800180018000", + "0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffc9", + "0x208b7fff7fff7ffe", + "0x48127ffa7fff8000", + "0x48127ff87fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482680017ff78000", + "0x1", + "0x480a7ff87fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x48127ff87fff8000", + "0x482480017ff78000", + "0x1", + "0x208b7fff7fff7ffe", + "0x480a7ffb7fff8000", + "0x480a7ffc7fff8000", + "0x480a7ffd7fff8000", + "0x1104800180018000", + "0xe1", + "0x20680017fff7ffc", + "0x8f", + "0x48507ffd7ffd8000", + "0xa0680017fff8000", + "0x7", + "0x4824800180007ffe", + "0x100000000", + "0x400080007ff67fff", + "0x10780017fff7fff", + "0x78", + "0x482480017ffe8000", + "0xffffffffffffffffffffffff00000000", + "0x400080007ff67fff", + "0x48507ffb7ffb8000", + "0xa0680017fff8000", + "0x7", + "0x4824800180007ffe", + "0x100000000", + "0x400080017ff37fff", + "0x10780017fff7fff", + "0x5e", + "0x482480017ffe8000", + "0xffffffffffffffffffffffff00000000", + "0x400080017ff37fff", + "0x48507ff97ff98000", + "0xa0680017fff8000", + "0x7", + "0x4824800180007ffe", + "0x100000000", + "0x400080027ff07fff", + "0x10780017fff7fff", + "0x44", + "0x482480017ffe8000", + "0xffffffffffffffffffffffff00000000", + "0x400080027ff07fff", + "0xa0680017fff8000", + "0x8", + "0x48307ff97ff68000", + "0x4824800180007fff", + "0x100000000", + "0x400080037fed7fff", + "0x10780017fff7fff", + "0x2a", + "0x48307ff97ff68001", + "0x4824800180007fff", + "0xffffffffffffffffffffffff00000000", + "0x400080037fed7ffe", + "0x482480017fed8000", + "0x4", + "0x48307ff980007ffe", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0x10", + "0x40780017fff7fff", + "0x2", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x696e76616c69642076616c7565", + "0x400080007ffe7fff", + "0x48127ffa7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffc7fff8000", + "0x482480017ffb8000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x48127ff17fff8000", + "0x48127ff37fff8000", + "0x48127ff57fff8000", + "0x400080007ffc7ffd", + "0x400080017ffc7ffe", + "0x400080027ffc7fff", + "0x48127ffa7fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x3", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x4", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x7533325f616464204f766572666c6f77", + "0x400080007ffe7fff", + "0x482480017fe78000", + "0x4", + "0x480680017fff8000", + "0x1", + "0x48127ffc7fff8000", + "0x482480017ffb8000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x7", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x7533325f6d756c204f766572666c6f77", + "0x400080007ffe7fff", + "0x482480017fe78000", + "0x3", + "0x480680017fff8000", + "0x1", + "0x48127ffc7fff8000", + "0x482480017ffb8000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0xa", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x7533325f6d756c204f766572666c6f77", + "0x400080007ffe7fff", + "0x482480017fe78000", + "0x2", + "0x480680017fff8000", + "0x1", + "0x48127ffc7fff8000", + "0x482480017ffb8000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0xd", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x7533325f6d756c204f766572666c6f77", + "0x400080007ffe7fff", + "0x482480017fe78000", + "0x1", + "0x480680017fff8000", + "0x1", + "0x48127ffc7fff8000", + "0x482480017ffb8000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x10", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7074696f6e3a3a756e77726170206661696c65642e", + "0x400080007ffe7fff", + "0x48127fe77fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffc7fff8000", + "0x482480017ffb8000", + "0x1", + "0x208b7fff7fff7ffe", + "0xa0680017fff8000", + "0x7", + "0x482680017ff98000", + "0xfffffffffffffffffffffffffffff722", + "0x400280007ff87fff", + "0x10780017fff7fff", + "0x2f", + "0x4825800180007ff9", + "0x8de", + "0x400280007ff87fff", + "0x482680017ff88000", + "0x1", + "0x48297ffa80007ffb", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482680017ffa8000", + "0x1", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x0", + "0x480a7ffa7fff8000", + "0x10780017fff7fff", + "0x8", + "0x480a7ffa7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0xe", + "0x480080007fff8000", + "0x400280007ffd7fff", + "0x48127ff97fff8000", + "0x48127ff77fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x480a7ffc7fff8000", + "0x482680017ffd8000", + "0x1", + "0x1104800180018000", + "0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffd7", + "0x208b7fff7fff7ffe", + "0x48127ffa7fff8000", + "0x48127ff87fff8000", + "0x480680017fff8000", + "0x0", + "0x480a7ffc7fff8000", + "0x480a7ffd7fff8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482680017ff88000", + "0x1", + "0x480a7ff97fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffb7fff8000", + "0x482480017ffa8000", + "0x1", + "0x208b7fff7fff7ffe", + "0x48297ffc80007ffd", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482680017ffc8000", + "0x1", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x0", + "0x480a7ffc7fff8000", + "0x10780017fff7fff", + "0x8", + "0x480a7ffc7fff8000", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0xad", + "0x480080007fff8000", + "0xa0680017fff8000", + "0x12", + "0x4824800180007ffe", + "0x100000000", + "0x4844800180008002", + "0x8000000000000110000000000000000", + "0x4830800080017ffe", + "0x480280007ffb7fff", + "0x482480017ffe8000", + "0xefffffffffffffde00000000ffffffff", + "0x480280017ffb7fff", + "0x400280027ffb7ffb", + "0x402480017fff7ffb", + "0xffffffffffffffffffffffffffffffff", + "0x20680017fff7fff", + "0x96", + "0x402780017fff7fff", + "0x1", + "0x400280007ffb7ffe", + "0x482480017ffe8000", + "0xffffffffffffffffffffffff00000000", + "0x400280017ffb7fff", + "0x482680017ffb8000", + "0x2", + "0x48307ff880007ff9", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482480017ff78000", + "0x1", + "0x48127ff77fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ff47fff8000", + "0x10780017fff7fff", + "0x8", + "0x48127ff77fff8000", + "0x48127ff77fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0x6b", + "0x480080007fff8000", + "0xa0680017fff8000", + "0x12", + "0x4824800180007ffe", + "0x100000000", + "0x4844800180008002", + "0x8000000000000110000000000000000", + "0x4830800080017ffe", + "0x480080007ff57fff", + "0x482480017ffe8000", + "0xefffffffffffffde00000000ffffffff", + "0x480080017ff37fff", + "0x400080027ff27ffb", + "0x402480017fff7ffb", + "0xffffffffffffffffffffffffffffffff", + "0x20680017fff7fff", + "0x54", + "0x402780017fff7fff", + "0x1", + "0x400080007ff87ffe", + "0x482480017ffe8000", + "0xffffffffffffffffffffffff00000000", + "0x400080017ff77fff", + "0x482480017ff78000", + "0x2", + "0x48307ff880007ff9", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482480017ff78000", + "0x1", + "0x48127ff77fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ff47fff8000", + "0x10780017fff7fff", + "0x8", + "0x48127ff77fff8000", + "0x48127ff77fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0x29", + "0x480080007fff8000", + "0xa0680017fff8000", + "0x12", + "0x4824800180007ffe", + "0x100000000", + "0x4844800180008002", + "0x8000000000000110000000000000000", + "0x4830800080017ffe", + "0x480080007ff57fff", + "0x482480017ffe8000", + "0xefffffffffffffde00000000ffffffff", + "0x480080017ff37fff", + "0x400080027ff27ffb", + "0x402480017fff7ffb", + "0xffffffffffffffffffffffffffffffff", + "0x20680017fff7fff", + "0x14", + "0x402780017fff7fff", + "0x1", + "0x400080007ff87ffe", + "0x482480017ffe8000", + "0xffffffffffffffffffffffff00000000", + "0x400080017ff77fff", + "0x40780017fff7fff", + "0x5", + "0x482480017ff28000", + "0x2", + "0x48127ff37fff8000", + "0x48127ff37fff8000", + "0x480680017fff8000", + "0x0", + "0x48127fe27fff8000", + "0x48127fea7fff8000", + "0x48127ff27fff8000", + "0x208b7fff7fff7ffe", + "0x482480017ff28000", + "0x3", + "0x10780017fff7fff", + "0x5", + "0x40780017fff7fff", + "0x8", + "0x48127ff27fff8000", + "0x48127ff37fff8000", + "0x48127ff37fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x9", + "0x482480017fe98000", + "0x3", + "0x10780017fff7fff", + "0x5", + "0x40780017fff7fff", + "0x11", + "0x48127fe97fff8000", + "0x48127fea7fff8000", + "0x48127fea7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x12", + "0x482680017ffb8000", + "0x3", + "0x10780017fff7fff", + "0x5", + "0x40780017fff7fff", + "0x1a", + "0x480a7ffb7fff8000", + "0x48127fe17fff8000", + "0x48127fe17fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x208b7fff7fff7ffe", + "0x208b7fff7fff7ffe" + ], + "bytecode_segment_lengths": [ + 202, + 92, + 162, + 66, + 207 + ], + "hints": [ + [ + 0, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x0" + }, + "rhs": { + "Deref": { + "register": "FP", + "offset": -6 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 33, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 80, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 99, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x254e" + }, + "rhs": { + "Deref": { + "register": "AP", + "offset": -11 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 119, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 158, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 173, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 187, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 202, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x942" + }, + "rhs": { + "Deref": { + "register": "FP", + "offset": -8 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 274, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 302, + [ + { + "TestLessThan": { + "lhs": { + "Deref": { + "register": "AP", + "offset": -1 + } + }, + "rhs": { + "Immediate": "0x100000000" + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 313, + [ + { + "TestLessThan": { + "lhs": { + "Deref": { + "register": "AP", + "offset": -1 + } + }, + "rhs": { + "Immediate": "0x100000000" + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 324, + [ + { + "TestLessThan": { + "lhs": { + "Deref": { + "register": "AP", + "offset": -1 + } + }, + "rhs": { + "Immediate": "0x100000000" + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 334, + [ + { + "TestLessThan": { + "lhs": { + "BinOp": { + "op": "Add", + "a": { + "register": "AP", + "offset": -9 + }, + "b": { + "Deref": { + "register": "AP", + "offset": -6 + } + } + } + }, + "rhs": { + "Immediate": "0x100000000" + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 355, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 367, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 384, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 399, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 414, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 429, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 444, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 456, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x8de" + }, + "rhs": { + "Deref": { + "register": "FP", + "offset": -7 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 508, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 544, + [ + { + "TestLessThan": { + "lhs": { + "BinOp": { + "op": "Add", + "a": { + "register": "AP", + "offset": -1 + }, + "b": { + "Immediate": "0x0" + } + } + }, + "rhs": { + "Immediate": "0x100000000" + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 548, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "AP", + "offset": -1 + } + }, + "scalar": { + "Immediate": "0x8000000000000110000000000000000" + }, + "max_x": { + "Immediate": "0xfffffffffffffffffffffffffffffffe" + }, + "x": { + "register": "AP", + "offset": 0 + }, + "y": { + "register": "AP", + "offset": 1 + } + } + } + ] + ], + [ + 590, + [ + { + "TestLessThan": { + "lhs": { + "BinOp": { + "op": "Add", + "a": { + "register": "AP", + "offset": -1 + }, + "b": { + "Immediate": "0x0" + } + } + }, + "rhs": { + "Immediate": "0x100000000" + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 594, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "AP", + "offset": -1 + } + }, + "scalar": { + "Immediate": "0x8000000000000110000000000000000" + }, + "max_x": { + "Immediate": "0xfffffffffffffffffffffffffffffffe" + }, + "x": { + "register": "AP", + "offset": 0 + }, + "y": { + "register": "AP", + "offset": 1 + } + } + } + ] + ], + [ + 636, + [ + { + "TestLessThan": { + "lhs": { + "BinOp": { + "op": "Add", + "a": { + "register": "AP", + "offset": -1 + }, + "b": { + "Immediate": "0x0" + } + } + }, + "rhs": { + "Immediate": "0x100000000" + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 640, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "AP", + "offset": -1 + } + }, + "scalar": { + "Immediate": "0x8000000000000110000000000000000" + }, + "max_x": { + "Immediate": "0xfffffffffffffffffffffffffffffffe" + }, + "x": { + "register": "AP", + "offset": 0 + }, + "y": { + "register": "AP", + "offset": 1 + } + } + } + ] + ] + ], + "pythonic_hints": [ + [ + 0, + [ + "memory[ap + 0] = 0 <= memory[fp + -6]" + ] + ], + [ + 33, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 80, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 99, + [ + "memory[ap + 0] = 9550 <= memory[ap + -11]" + ] + ], + [ + 119, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 158, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 173, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 187, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 202, + [ + "memory[ap + 0] = 2370 <= memory[fp + -8]" + ] + ], + [ + 274, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 302, + [ + "memory[ap + 0] = memory[ap + -1] < 4294967296" + ] + ], + [ + 313, + [ + "memory[ap + 0] = memory[ap + -1] < 4294967296" + ] + ], + [ + 324, + [ + "memory[ap + 0] = memory[ap + -1] < 4294967296" + ] + ], + [ + 334, + [ + "memory[ap + 0] = (memory[ap + -9] + memory[ap + -6]) % PRIME < 4294967296" + ] + ], + [ + 355, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 367, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 384, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 399, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 414, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 429, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 444, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 456, + [ + "memory[ap + 0] = 2270 <= memory[fp + -7]" + ] + ], + [ + 508, + [ + "memory[ap + 0] = segments.add()" + ] + ], + [ + 544, + [ + "memory[ap + 0] = (memory[ap + -1] + 0) % PRIME < 4294967296" + ] + ], + [ + 548, + [ + "\n(value, scalar) = (memory[ap + -1], 10633823966279327296825105735305134080)\nx = min(value // scalar, 340282366920938463463374607431768211454)\ny = value - x * scalar\nmemory[ap + 0] = x\nmemory[ap + 1] = y\n" + ] + ], + [ + 590, + [ + "memory[ap + 0] = (memory[ap + -1] + 0) % PRIME < 4294967296" + ] + ], + [ + 594, + [ + "\n(value, scalar) = (memory[ap + -1], 10633823966279327296825105735305134080)\nx = min(value // scalar, 340282366920938463463374607431768211454)\ny = value - x * scalar\nmemory[ap + 0] = x\nmemory[ap + 1] = y\n" + ] + ], + [ + 636, + [ + "memory[ap + 0] = (memory[ap + -1] + 0) % PRIME < 4294967296" + ] + ], + [ + 640, + [ + "\n(value, scalar) = (memory[ap + -1], 10633823966279327296825105735305134080)\nx = min(value // scalar, 340282366920938463463374607431768211454)\ny = value - x * scalar\nmemory[ap + 0] = x\nmemory[ap + 1] = y\n" + ] + ] + ], + "entry_points_by_type": { + "EXTERNAL": [ + { + "selector": "0xe2054f8a912367e38a22ce773328ff8aabf8082c4120bad9ef085e1dbf29a7", + "offset": 0, + "builtins": [ + "range_check" + ] + } + ], + "L1_HANDLER": [], + "CONSTRUCTOR": [] } - ], - "single_page": true + } } \ No newline at end of file diff --git a/cairo-lang-0.13.1.zip b/cairo-lang-0.13.1.zip new file mode 100644 index 0000000..02c43b3 Binary files /dev/null and b/cairo-lang-0.13.1.zip differ diff --git a/cairo0-bootloader/bootloader/starknet/__init__.py b/cairo0-bootloader/bootloader/contract/__init__.py similarity index 100% rename from cairo0-bootloader/bootloader/starknet/__init__.py rename to cairo0-bootloader/bootloader/contract/__init__.py diff --git a/cairo0-bootloader/bootloader/contract/contract_bootloader.cairo b/cairo0-bootloader/bootloader/contract/contract_bootloader.cairo new file mode 100644 index 0000000..c101a40 --- /dev/null +++ b/cairo0-bootloader/bootloader/contract/contract_bootloader.cairo @@ -0,0 +1,61 @@ +%builtins output pedersen range_check ecdsa bitwise ec_op keccak poseidon + +from bootloader.contract.run_contract_bootloader import run_contract_bootloader +from starkware.cairo.common.cairo_builtins import ( + HashBuiltin, + PoseidonBuiltin, + BitwiseBuiltin, + KeccakBuiltin, +) +from starkware.cairo.common.registers import get_fp_and_pc +from contract_class.compiled_class import CompiledClass + +func main{ + output_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr, + ecdsa_ptr, + bitwise_ptr: BitwiseBuiltin*, + ec_op_ptr, + keccak_ptr: KeccakBuiltin*, + poseidon_ptr: PoseidonBuiltin*, +}() { + alloc_locals; + local compiled_class: CompiledClass*; + + %{ + from bootloader.objects import ContractBootloaderInput + contract_bootloader_input = ContractBootloaderInput.Schema().load(program_input) + %} + + // Fetch contract data form hints. + %{ + from starkware.starknet.core.os.contract_class.compiled_class_hash import create_bytecode_segment_structure + from contract_class.compiled_class_hash_utils import get_compiled_class_struct + + bytecode_segment_structure = create_bytecode_segment_structure( + bytecode=contract_bootloader_input.compiled_class.bytecode, + bytecode_segment_lengths=contract_bootloader_input.compiled_class.bytecode_segment_lengths, + visited_pcs=None, + ) + + cairo_contract = get_compiled_class_struct( + compiled_class=contract_bootloader_input.compiled_class, + bytecode=bytecode_segment_structure.bytecode_with_skipped_segments() + ) + ids.compiled_class = segments.gen_arg(cairo_contract) + %} + + assert compiled_class.bytecode_ptr[compiled_class.bytecode_length] = 0x208b7fff7fff7ffe; + + %{ + vm_load_program( + contract_bootloader_input.compiled_class.get_runnable_program(entrypoint_builtins=[]), + ids.compiled_class.bytecode_ptr + ) + %} + + run_contract_bootloader(compiled_class); + + return (); +} diff --git a/cairo0-bootloader/bootloader/contract/execute_entry_point.cairo b/cairo0-bootloader/bootloader/contract/execute_entry_point.cairo new file mode 100644 index 0000000..8628324 --- /dev/null +++ b/cairo0-bootloader/bootloader/contract/execute_entry_point.cairo @@ -0,0 +1,281 @@ +from starkware.cairo.builtin_selection.select_input_builtins import select_input_builtins +from starkware.cairo.builtin_selection.validate_builtins import validate_builtins +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_builtins import KeccakBuiltin +from starkware.cairo.common.dict import dict_read +from starkware.cairo.common.dict_access import DictAccess +from starkware.cairo.common.find_element import find_element, search_sorted +from starkware.cairo.common.math import assert_not_zero +from starkware.cairo.common.registers import get_ap +from starkware.starknet.builtins.segment_arena.segment_arena import ( + SegmentArenaBuiltin, + validate_segment_arena, +) +from starkware.starknet.common.syscalls import TxInfo as DeprecatedTxInfo +from starkware.starknet.core.os.block_context import BlockContext +from starkware.starknet.core.os.builtins import ( + BuiltinEncodings, + BuiltinParams, + BuiltinPointers, + NonSelectableBuiltins, + SelectableBuiltins, + update_builtin_ptrs, +) +from starkware.starknet.core.os.constants import ( + DEFAULT_ENTRY_POINT_SELECTOR, + ENTRY_POINT_GAS_COST, + ENTRY_POINT_TYPE_CONSTRUCTOR, + ENTRY_POINT_TYPE_EXTERNAL, + ENTRY_POINT_TYPE_L1_HANDLER, + NOP_ENTRY_POINT_OFFSET, +) +from contract_class.compiled_class import CompiledClass, CompiledClassEntryPoint, CompiledClassFact +from starkware.starknet.core.os.output import OsCarriedOutputs + +struct ExecutionInfo { + selector: felt, +} + +// Represents the execution context during the execution of contract code. +struct ExecutionContext { + entry_point_type: felt, + calldata_size: felt, + calldata: felt*, + // Additional information about the execution. + execution_info: ExecutionInfo*, +} + +// Represents the arguments pushed to the stack before calling an entry point. +struct EntryPointCallArguments { + gas_builtin: felt, + syscall_ptr: felt*, + calldata_start: felt*, + calldata_end: felt*, +} + +// Represents the values returned by a call to an entry point. +struct EntryPointReturnValues { + gas_builtin: felt, + syscall_ptr: felt*, + // The failure_flag is 0 if the execution succeeded and 1 if it failed. + failure_flag: felt, + retdata_start: felt*, + retdata_end: felt*, +} + +// Performs a Cairo jump to the function 'execute_syscalls'. +// This function's signature must match the signature of 'execute_syscalls'. +func call_execute_syscalls{ + range_check_ptr, + syscall_ptr: felt*, + builtin_ptrs: BuiltinPointers*, + contract_state_changes: DictAccess*, + contract_class_changes: DictAccess*, + outputs: OsCarriedOutputs*, +}(block_context: BlockContext*, execution_context: ExecutionContext*, syscall_ptr_end: felt*) { + %{ print("call_execute_syscalls") %} +} + +// Returns the CompiledClassEntryPoint, based on 'compiled_class' and 'execution_context'. +func get_entry_point{range_check_ptr}( + compiled_class: CompiledClass*, execution_context: ExecutionContext* +) -> (entry_point: CompiledClassEntryPoint*) { + alloc_locals; + // Get the entry points corresponding to the transaction's type. + local entry_points: CompiledClassEntryPoint*; + local n_entry_points: felt; + + tempvar entry_point_type = execution_context.entry_point_type; + if (entry_point_type == ENTRY_POINT_TYPE_L1_HANDLER) { + entry_points = compiled_class.l1_handlers; + n_entry_points = compiled_class.n_l1_handlers; + } else { + if (entry_point_type == ENTRY_POINT_TYPE_EXTERNAL) { + entry_points = compiled_class.external_functions; + n_entry_points = compiled_class.n_external_functions; + } else { + assert entry_point_type = ENTRY_POINT_TYPE_CONSTRUCTOR; + entry_points = compiled_class.constructors; + n_entry_points = compiled_class.n_constructors; + + if (n_entry_points == 0) { + return (entry_point=cast(0, CompiledClassEntryPoint*)); + } + } + } + + // The key must be at offset 0. + static_assert CompiledClassEntryPoint.selector == 0; + let (entry_point_desc: CompiledClassEntryPoint*, success) = search_sorted( + array_ptr=cast(entry_points, felt*), + elm_size=CompiledClassEntryPoint.SIZE, + n_elms=n_entry_points, + key=execution_context.execution_info.selector, + ); + if (success != 0) { + return (entry_point=entry_point_desc); + } + + // If the selector was not found, verify that the first entry point is the default entry point, + // and call it. + assert_not_zero(n_entry_points); + assert entry_points[0].selector = DEFAULT_ENTRY_POINT_SELECTOR; + return (entry_point=&entry_points[0]); +} + +// Executes an entry point in a contract. +// The contract entry point is selected based on execution_context.entry_point_type +// and execution_context.execution_info.selector. +// +// Arguments: +// block_context - a global context that is fixed throughout the block. +// execution_context - The context for the current execution. +func execute_entry_point{ + range_check_ptr, builtin_ptrs: BuiltinPointers*, builtin_params: BuiltinParams* +}(compiled_class: CompiledClass*, execution_context: ExecutionContext*) -> ( + retdata_size: felt, retdata: felt* +) { + alloc_locals; + + let (compiled_class_entry_point: CompiledClassEntryPoint*) = get_entry_point( + compiled_class=compiled_class, execution_context=execution_context + ); + + if (compiled_class_entry_point == cast(0, CompiledClassEntryPoint*)) { + // Assert that there is no call data in the case of NOP entry point. + assert execution_context.calldata_size = 0; + %{ execution_helper.skip_call() %} + return (retdata_size=0, retdata=cast(0, felt*)); + } + + let entry_point_offset = compiled_class_entry_point.offset; + local range_check_ptr = range_check_ptr; + local contract_entry_point: felt* = compiled_class.bytecode_ptr + entry_point_offset; + + local syscall_ptr: felt*; + + %{ + print("contract_entry_point:" , ids.contract_entry_point) + ids.syscall_ptr = segments.add() + from bootloader.contract.syscall_handler import SyscallHandler + syscall_handler = SyscallHandler(segments=segments) + syscall_handler.set_syscall_ptr(syscall_ptr=ids.syscall_ptr) + %} + + let builtin_ptrs: BuiltinPointers* = prepare_builtin_ptrs_for_execute(builtin_ptrs); + + let n_builtins = BuiltinEncodings.SIZE; + local calldata_start: felt* = execution_context.calldata; + local calldata_end: felt* = calldata_start + execution_context.calldata_size; + local entry_point_n_builtins = compiled_class_entry_point.n_builtins; + local entry_point_builtin_list: felt* = compiled_class_entry_point.builtin_list; + // Call select_input_builtins to push the relevant builtin pointer arguments on the stack. + select_input_builtins( + all_encodings=builtin_params.builtin_encodings, + all_ptrs=builtin_ptrs, + n_all_builtins=n_builtins, + selected_encodings=entry_point_builtin_list, + n_selected_builtins=entry_point_n_builtins, + ); + + // Use tempvar to pass the rest of the arguments to contract_entry_point(). + let current_ap = ap; + tempvar args = EntryPointCallArguments( + gas_builtin=1000000, + syscall_ptr=syscall_ptr, + calldata_start=calldata_start, + calldata_end=calldata_end, + ); + static_assert ap == current_ap + EntryPointCallArguments.SIZE; + + %{ + print("builtin_ptrs:" , ids.builtin_ptrs) + print("syscall_ptr:" , ids.syscall_ptr) + print("calldata_start:" , ids.calldata_start) + print("calldata_end:" , ids.calldata_end) + %} + + %{ vm_enter_scope({'syscall_handler': syscall_handler}) %} + call abs contract_entry_point; + %{ vm_exit_scope() %} + + // Retrieve returned_builtin_ptrs_subset. + // Note that returned_builtin_ptrs_subset cannot be set in a hint because doing so will allow a + // malicious prover to lie about the storage changes of a valid contract. + let (ap_val) = get_ap(); + local return_values_ptr: felt* = ap_val - EntryPointReturnValues.SIZE; + local returned_builtin_ptrs_subset: felt* = return_values_ptr - entry_point_n_builtins; + local entry_point_return_values: EntryPointReturnValues* = cast( + return_values_ptr, EntryPointReturnValues* + ); + + assert entry_point_return_values.failure_flag = 0; + + let remaining_gas = entry_point_return_values.gas_builtin; + let retdata_start = entry_point_return_values.retdata_start; + let retdata_end = entry_point_return_values.retdata_end; + + let return_builtin_ptrs = update_builtin_ptrs( + builtin_params=builtin_params, + builtin_ptrs=builtin_ptrs, + n_selected_builtins=entry_point_n_builtins, + selected_encodings=entry_point_builtin_list, + selected_ptrs=returned_builtin_ptrs_subset, + ); + + // Validate the segment_arena builtin. + // Note that as the segment_arena pointer points to the first unused element, we need to + // take segment_arena[-1] to get the actual values. + tempvar prev_segment_arena = &builtin_ptrs.selectable.segment_arena[-1]; + tempvar current_segment_arena = &return_builtin_ptrs.selectable.segment_arena[-1]; + assert prev_segment_arena.infos = current_segment_arena.infos; + validate_segment_arena(segment_arena=current_segment_arena); + + let builtin_ptrs = return_builtin_ptrs; + // with syscall_ptr { + // call_execute_syscalls( + // block_context=block_context, + // execution_context=execution_context, + // syscall_ptr_end=entry_point_return_values.syscall_ptr, + // ); + // } + + %{ + print(ids.entry_point_return_values.failure_flag) + for i in range(0, 4): + print(memory[ids.retdata_start + i]) + %} + + return (retdata_size=0, retdata=cast(0, felt*)); +} + +// Prepares the builtin pointer for the execution of an entry point. +// In particular, restarts the SegmentArenaBuiltin struct if it was previously used. +func prepare_builtin_ptrs_for_execute(builtin_ptrs: BuiltinPointers*) -> BuiltinPointers* { + let selectable_builtins = &builtin_ptrs.selectable; + tempvar segment_arena_ptr = selectable_builtins.segment_arena; + tempvar prev_segment_arena = &segment_arena_ptr[-1]; + + // If no segment was allocated, we don't need to restart the struct. + tempvar prev_n_segments = prev_segment_arena.n_segments; + if (prev_n_segments == 0) { + return builtin_ptrs; + } + + assert segment_arena_ptr[0] = SegmentArenaBuiltin( + infos=&prev_segment_arena.infos[prev_n_segments], n_segments=0, n_finalized=0 + ); + let segment_arena_ptr = &segment_arena_ptr[1]; + return new BuiltinPointers( + selectable=SelectableBuiltins( + pedersen=selectable_builtins.pedersen, + range_check=selectable_builtins.range_check, + ecdsa=selectable_builtins.ecdsa, + bitwise=selectable_builtins.bitwise, + ec_op=selectable_builtins.ec_op, + poseidon=selectable_builtins.poseidon, + segment_arena=segment_arena_ptr, + ), + non_selectable=builtin_ptrs.non_selectable, + ); +} diff --git a/cairo0-bootloader/bootloader/contract/run_contract_bootloader.cairo b/cairo0-bootloader/bootloader/contract/run_contract_bootloader.cairo new file mode 100644 index 0000000..f5453f5 --- /dev/null +++ b/cairo0-bootloader/bootloader/contract/run_contract_bootloader.cairo @@ -0,0 +1,116 @@ +from starkware.cairo.common.cairo_builtins import ( + HashBuiltin, + PoseidonBuiltin, + BitwiseBuiltin, + KeccakBuiltin, + SignatureBuiltin, + EcOpBuiltin, +) +from starkware.cairo.common.registers import get_fp_and_pc +from contract_class.compiled_class import CompiledClass +from starkware.starknet.builtins.segment_arena.segment_arena import new_arena, SegmentArenaBuiltin +from starkware.starknet.core.os.builtins import ( + BuiltinEncodings, + BuiltinParams, + BuiltinPointers, + NonSelectableBuiltins, + BuiltinInstanceSizes, + SelectableBuiltins, + update_builtin_ptrs, +) +from bootloader.contract.execute_entry_point import ( + execute_entry_point, + ExecutionContext, + ExecutionInfo, +) +from starkware.starknet.core.os.constants import ENTRY_POINT_TYPE_EXTERNAL + +// Loads the programs and executes them. +// +// Hint Arguments: +// compiled_class - contains the contract to execute. +// +// Returns: +// Updated builtin pointers after executing all programs. +// fact_topologies - that corresponds to the tasks (hint variable). +func run_contract_bootloader{ + output_ptr: felt*, + pedersen_ptr: HashBuiltin*, + range_check_ptr, + ecdsa_ptr, + bitwise_ptr: BitwiseBuiltin*, + ec_op_ptr, + keccak_ptr: KeccakBuiltin*, + poseidon_ptr: PoseidonBuiltin*, +}(compiled_class: CompiledClass*) { + alloc_locals; + + // Prepare builtin pointers. + let segment_arena_ptr = new_arena(); + + let (__fp__, _) = get_fp_and_pc(); + local local_builtin_ptrs: BuiltinPointers = BuiltinPointers( + selectable=SelectableBuiltins( + pedersen=pedersen_ptr, + range_check=nondet %{ segments.add() %}, + ecdsa=ecdsa_ptr, + bitwise=bitwise_ptr, + ec_op=ec_op_ptr, + poseidon=poseidon_ptr, + segment_arena=segment_arena_ptr, + ), + non_selectable=NonSelectableBuiltins(keccak=keccak_ptr), + ); + let builtin_ptrs = &local_builtin_ptrs; + + %{ print("builtin_ptrs.selectable.range_check: ", ids.builtin_ptrs.selectable.range_check) %} + + local local_builtin_encodings: BuiltinEncodings = BuiltinEncodings( + pedersen='pedersen', + range_check='range_check', + ecdsa='ecdsa', + bitwise='bitwise', + ec_op='ec_op', + poseidon='poseidon', + segment_arena='segment_arena', + ); + + local local_builtin_instance_sizes: BuiltinInstanceSizes = BuiltinInstanceSizes( + pedersen=HashBuiltin.SIZE, + range_check=1, + ecdsa=SignatureBuiltin.SIZE, + bitwise=BitwiseBuiltin.SIZE, + ec_op=EcOpBuiltin.SIZE, + poseidon=PoseidonBuiltin.SIZE, + segment_arena=SegmentArenaBuiltin.SIZE, + ); + + local local_builtin_params: BuiltinParams = BuiltinParams( + builtin_encodings=&local_builtin_encodings, + builtin_instance_sizes=&local_builtin_instance_sizes, + ); + let builtin_params = &local_builtin_params; + + local calldata: felt*; + %{ ids.calldata = segments.add() %} + + assert calldata[0] = 0x3; + assert calldata[1] = 0x3; + assert calldata[2] = 0x4; + assert calldata[3] = 0x5; + + local execution_info: ExecutionInfo = ExecutionInfo(selector=0x00e2054f8a912367e38a22ce773328ff8aabf8082c4120bad9ef085e1dbf29a7); + + local execution_context: ExecutionContext = ExecutionContext( + entry_point_type=ENTRY_POINT_TYPE_EXTERNAL, + calldata_size=4, + calldata=calldata, + execution_info=&execution_info, + ); + + with builtin_ptrs, builtin_params { + let (retdata_size, retdata) = execute_entry_point(compiled_class, &execution_context); + } + + return (); +} diff --git a/cairo0-bootloader/bootloader/contract/syscall_handler.py b/cairo0-bootloader/bootloader/contract/syscall_handler.py new file mode 100644 index 0000000..3867364 --- /dev/null +++ b/cairo0-bootloader/bootloader/contract/syscall_handler.py @@ -0,0 +1,80 @@ +from typing import Iterable +from starkware.starknet.core.os.syscall_handler import ( + SyscallHandlerBase, + OsExecutionHelper, +) +from starkware.cairo.lang.vm.relocatable import RelocatableValue, MaybeRelocatable +from starkware.cairo.lang.vm.memory_segments import MemorySegmentManager + + +class SyscallHandler(SyscallHandlerBase): + """ + A handler for system calls; used by the BusinessLogic entry point execution. + """ + + def __init__( + self, + segments: MemorySegmentManager, + ): + super().__init__(segments=segments, initial_syscall_ptr=None) + + def set_syscall_ptr(self, syscall_ptr: RelocatableValue): + assert self._syscall_ptr is None, "syscall_ptr is already set." + self._syscall_ptr = syscall_ptr + + def allocate_segment(self, data: Iterable[MaybeRelocatable]) -> RelocatableValue: + segment_start = self.segments.add() + self.segments.write_arg(ptr=segment_start, arg=data) + return segment_start + + def _allocate_segment_for_retdata(self): + # Implementation here + pass + + def _call_contract_helper(self): + # Implementation here + pass + + def _count_syscall(self): + # Implementation here + pass + + def _deploy(self): + # Implementation here + pass + + def _emit_event(self): + # Implementation here + pass + + def _get_block_hash(self): + # Implementation here + pass + + def _get_execution_info_ptr(self): + # Implementation here + pass + + def _keccak(self): + # Implementation here + pass + + def _replace_class(self): + # Implementation here + pass + + def _send_message_to_l1(self): + # Implementation here + pass + + def _storage_read(self): + # Implementation here + pass + + def _storage_write(self): + # Implementation here + pass + + def current_block_number(self): + # Implementation here + pass diff --git a/cairo0-bootloader/bootloader/objects.py b/cairo0-bootloader/bootloader/objects.py index 6f27ac8..95f6179 100644 --- a/cairo0-bootloader/bootloader/objects.py +++ b/cairo0-bootloader/bootloader/objects.py @@ -1,178 +1,13 @@ -import os -import tempfile -import subprocess -import dataclasses -from abc import abstractmethod -from dataclasses import field -from typing import ClassVar, Dict, List, Optional, Type - -import marshmallow -import marshmallow.fields as mfields import marshmallow_dataclass -from marshmallow_oneofschema import OneOfSchema - -from starkware.cairo.lang.compiler.program import Program, ProgramBase, StrippedProgram -from starkware.cairo.lang.vm.cairo_pie import CairoPie -from starkware.starkware_utils.marshmallow_dataclass_fields import additional_metadata from starkware.starkware_utils.validated_dataclass import ValidatedMarshmallowDataclass - - -class TaskSpec(ValidatedMarshmallowDataclass): - """ - Contains task's specification. - """ - - @abstractmethod - def load_task(self, memory=None, args_start=None, args_len=None) -> "Task": - """ - Returns the corresponding task. - """ - - -class Task: - @abstractmethod - def get_program(self) -> ProgramBase: - """ - Returns the task's Cairo program. - """ - - -@marshmallow_dataclass.dataclass(frozen=True) -class RunProgramTask(TaskSpec, Task): - TYPE: ClassVar[str] = "RunProgramTask" - program: Program - program_input: dict - use_poseidon: bool - - def get_program(self) -> Program: - return self.program - - def load_task(self, memory=None, args_start=None, args_len=None) -> "Task": - return self - - -@marshmallow_dataclass.dataclass(frozen=True) -class CairoPiePath(TaskSpec): - TYPE: ClassVar[str] = "CairoPiePath" - path: str - use_poseidon: bool - - def load_task(self, memory=None, args_start=None, args_len=None) -> "CairoPieTask": - """ - Loads the PIE to memory. - """ - return CairoPieTask( - cairo_pie=CairoPie.from_file(self.path), use_poseidon=self.use_poseidon - ) - - -@marshmallow_dataclass.dataclass(frozen=True) -class Cairo1ProgramPath(TaskSpec): - TYPE: ClassVar[str] = "Cairo1ProgramPath" - path: str - use_poseidon: bool - - def load_task(self, memory=None, args_start=None, args_len=None) -> "CairoPieTask": - """ - Builds and Loads the PIE to memory. - """ - with tempfile.NamedTemporaryFile() as cairo_pie_file: - cairo_pie_file_path = cairo_pie_file.name - - args = [memory[args_start.address_ + i] for i in range(args_len)] - formatted_args = f'[{" ".join(map(str, args))}]' - - subprocess.run( - [ - "cairo1-run", - self.path, - "--layout", - "all_cairo", - "--args", - formatted_args, - "--cairo_pie_output", - cairo_pie_file_path, - "--append_return_values", - ], - check=True, - ) - - return CairoPieTask( - cairo_pie=CairoPie.from_file(cairo_pie_file_path), - use_poseidon=self.use_poseidon, - ) +from contract_class.contract_class import ( + CompiledClass, +) +from starkware.starkware_utils.validated_dataclass import ( + ValidatedMarshmallowDataclass, +) @marshmallow_dataclass.dataclass(frozen=True) -class CairoSierra(TaskSpec): - TYPE: ClassVar[str] = "CairoSierra" - path: str - use_poseidon: bool - - def load_task(self, memory=None, args_start=None, args_len=None) -> "CairoPieTask": - """ - Builds and Loads the PIE to memory. - """ - with tempfile.NamedTemporaryFile() as cairo_pie_file: - cairo_pie_file_path = cairo_pie_file.name - - args = [memory[args_start.address_ + i] for i in range(args_len)] - formatted_args = f'[{" ".join(map(str, args))}]' - - subprocess.run( - [ - "runner", - "--sierra_program", - self.path, - "--args", - formatted_args, - "--cairo_pie_output", - cairo_pie_file_path, - ], - check=True, - ) - - return CairoPieTask( - cairo_pie=CairoPie.from_file(cairo_pie_file_path), - use_poseidon=self.use_poseidon, - ) - - -class TaskSchema(OneOfSchema): - """ - Schema for Task/CairoPiePath/Cairo1ProgramPath/CairoSierra - OneOfSchema adds a "type" field. - """ - - type_schemas: Dict[str, Type[marshmallow.Schema]] = { - RunProgramTask.TYPE: RunProgramTask.Schema, - CairoPiePath.TYPE: CairoPiePath.Schema, - Cairo1ProgramPath.TYPE: Cairo1ProgramPath.Schema, - CairoSierra.TYPE: CairoSierra.Schema, - } - - def get_obj_type(self, obj): - return obj.TYPE - - -@dataclasses.dataclass(frozen=True) -class CairoPieTask(Task): - cairo_pie: CairoPie - use_poseidon: bool - - def get_program(self) -> StrippedProgram: - return self.cairo_pie.program - - -@marshmallow_dataclass.dataclass(frozen=True) -class SimpleBootloaderInput(ValidatedMarshmallowDataclass): - tasks: List[TaskSpec] = field( - metadata=additional_metadata( - marshmallow_field=mfields.List(mfields.Nested(TaskSchema)) - ) - ) - fact_topologies_path: Optional[str] - - # If true, the bootloader will put all the outputs in a single page, ignoring the - # tasks' fact topologies. - single_page: bool +class ContractBootloaderInput(ValidatedMarshmallowDataclass): + compiled_class: CompiledClass = CompiledClass diff --git a/cairo0-bootloader/contract_class/__init__.py b/cairo0-bootloader/contract_class/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cairo0-bootloader/contract_class/compiled_class.cairo b/cairo0-bootloader/contract_class/compiled_class.cairo new file mode 100644 index 0000000..86afc53 --- /dev/null +++ b/cairo0-bootloader/contract_class/compiled_class.cairo @@ -0,0 +1,416 @@ +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_builtins import PoseidonBuiltin +from starkware.cairo.common.hash_state_poseidon import ( + HashState, + hash_finalize, + hash_init, + hash_update_single, + hash_update_with_nested_hash, + poseidon_hash_many, +) +from starkware.cairo.common.math import assert_lt_felt +from starkware.cairo.common.poseidon_state import PoseidonBuiltinState +from starkware.cairo.common.registers import get_fp_and_pc + +const COMPILED_CLASS_VERSION = 'COMPILED_CLASS_V1'; + +struct CompiledClassEntryPoint { + // A field element that encodes the signature of the called function. + selector: felt, + // The offset of the instruction that should be called within the contract bytecode. + offset: felt, + // The number of builtins in 'builtin_list'. + n_builtins: felt, + // 'builtin_list' is a continuous memory segment containing the ASCII encoding of the (ordered) + // builtins used by the function. + builtin_list: felt*, +} + +struct CompiledClass { + compiled_class_version: felt, + + // The length and pointer to the external entry point table of the contract. + n_external_functions: felt, + external_functions: CompiledClassEntryPoint*, + + // The length and pointer to the L1 handler entry point table of the contract. + n_l1_handlers: felt, + l1_handlers: CompiledClassEntryPoint*, + + // The length and pointer to the constructor entry point table of the contract. + n_constructors: felt, + constructors: CompiledClassEntryPoint*, + + // The length and pointer of the bytecode. + bytecode_length: felt, + bytecode_ptr: felt*, +} + +// Checks that the list of selectors is sorted. +func validate_entry_points{range_check_ptr}( + n_entry_points: felt, entry_points: CompiledClassEntryPoint* +) { + if (n_entry_points == 0) { + return (); + } + + return validate_entry_points_inner( + n_entry_points=n_entry_points - 1, + entry_points=&entry_points[1], + prev_selector=entry_points[0].selector, + ); +} + +// Inner function for validate_entry_points. +func validate_entry_points_inner{range_check_ptr}( + n_entry_points: felt, entry_points: CompiledClassEntryPoint*, prev_selector +) { + if (n_entry_points == 0) { + return (); + } + + assert_lt_felt(prev_selector, entry_points[0].selector); + + return validate_entry_points_inner( + n_entry_points=n_entry_points - 1, + entry_points=&entry_points[1], + prev_selector=entry_points[0].selector, + ); +} + +func compiled_class_hash{range_check_ptr, poseidon_ptr: PoseidonBuiltin*}( + compiled_class: CompiledClass* +) -> (hash: felt) { + alloc_locals; + assert compiled_class.compiled_class_version = COMPILED_CLASS_VERSION; + + let hash_state: HashState = hash_init(); + with hash_state { + hash_update_single(item=compiled_class.compiled_class_version); + + // Hash external entry points. + hash_entry_points( + entry_points=compiled_class.external_functions, + n_entry_points=compiled_class.n_external_functions, + ); + + // Hash L1 handler entry points. + hash_entry_points( + entry_points=compiled_class.l1_handlers, n_entry_points=compiled_class.n_l1_handlers + ); + + // Hash constructor entry points. + hash_entry_points( + entry_points=compiled_class.constructors, n_entry_points=compiled_class.n_constructors + ); + + // Hash bytecode. + let bytecode_hash = bytecode_hash_node( + data_ptr=compiled_class.bytecode_ptr, data_length=compiled_class.bytecode_length + ); + hash_update_single(item=bytecode_hash); + } + + let hash: felt = hash_finalize(hash_state=hash_state); + return (hash=hash); +} + +// Returns the hash of the contract class bytecode according to its segments. +// +// The hash is computed according to a segment tree. Each segment may be either a leaf or divided +// into smaller segments (internal node). +// For example, the bytecode may be divided into functions and each function can be divided +// according to its branches. +// +// The hash of a leaf is the Poseidon hash the data. +// The hash of an internal node is `1 + poseidon(len0, hash0, len1, hash1, ...)` where +// len0 is the total length of the first segment, hash0 is the hash of the first segment, and so on. +// +// For each segment, the *prover* can choose whether to load or skip the segment. +// +// * Loaded segment: +// For leaves, the data will be fully loaded into memory. +// For internal nodes, the prover can choose to load/skip each of the children separately. +// +// * Skipped segment: +// The inner structure of that segment is ignored. +// The only guarantee is that the first field element is enforced to be -1. +// The rest of the field elements are unconstrained. +// The fact that a skipped segment is guaranteed to begin with -1 implies that the execution of +// the program cannot visit the start of the segment, as -1 is not a valid Cairo opcode. +// +// In the example above of division according to functions and branches, a function may be skipped +// entirely or partially. +// As long as one function does not jump into the middle of another function and as long as there +// are no jumps into the middle of a branch segment, the loading process described above will be +// sound. +func bytecode_hash_node{range_check_ptr, poseidon_ptr: PoseidonBuiltin*}( + data_ptr: felt*, data_length: felt +) -> felt { + alloc_locals; + + local is_leaf; + + %{ + from starkware.starknet.core.os.contract_class.compiled_class_hash_objects import ( + BytecodeLeaf, + ) + ids.is_leaf = 1 if isinstance(bytecode_segment_structure, BytecodeLeaf) else 0 + %} + + // Guess if the bytecode is a leaf or an internal node in the tree. + if (is_leaf != 0) { + // If the bytecode is a leaf, it must be loaded into memory. Compute its hash. + let (hash) = poseidon_hash_many(n=data_length, elements=data_ptr); + return hash; + } + + %{ bytecode_segments = iter(bytecode_segment_structure.segments) %} + + // Use the poseidon builtin directly for performance reasons. + let poseidon_state = PoseidonBuiltinState(s0=0, s1=0, s2=0); + bytecode_hash_internal_node{poseidon_state=poseidon_state}( + data_ptr=data_ptr, data_length=data_length + ); + + // Pad input with [1, 0]. See implementation of poseidon_hash_many(). + assert poseidon_ptr.input = PoseidonBuiltinState( + s0=poseidon_state.s0 + 1, s1=poseidon_state.s1, s2=poseidon_state.s2 + ); + let segmented_hash = poseidon_ptr.output.s0; + let poseidon_ptr = &poseidon_ptr[1]; + + // Add 1 to segmented_hash to avoid collisions with the hash of a leaf (domain separation). + return segmented_hash + 1; +} + +// Helper function for bytecode_hash_node. +// Computes the hash of an internal node by adding its children to the hash state. +func bytecode_hash_internal_node{ + range_check_ptr, poseidon_ptr: PoseidonBuiltin*, poseidon_state: PoseidonBuiltinState +}(data_ptr: felt*, data_length: felt) { + if (data_length == 0) { + %{ assert next(bytecode_segments, None) is None %} + return (); + } + + alloc_locals; + local is_used_leaf; + local is_segment_used; + local segment_length; + + %{ + current_segment_info = next(bytecode_segments) + + is_used = current_segment_info.is_used + ids.is_segment_used = 1 if is_used else 0 + + is_used_leaf = is_used and isinstance(current_segment_info.inner_structure, BytecodeLeaf) + ids.is_used_leaf = 1 if is_used_leaf else 0 + + ids.segment_length = current_segment_info.segment_length + vm_enter_scope(new_scope_locals={ + "bytecode_segment_structure": current_segment_info.inner_structure, + }) + %} + + if (is_used_leaf != 0) { + // Repeat the code of bytecode_hash_node() for performance reasons, instead of calling it. + let (current_segment_hash) = poseidon_hash_many(n=segment_length, elements=data_ptr); + // tempvar current_segment_hash = nondet %{ bytecode_segment_structure.hash() %}; + tempvar range_check_ptr = range_check_ptr; + tempvar poseidon_ptr = poseidon_ptr; + tempvar current_segment_hash = current_segment_hash; + } else { + if (is_segment_used != 0) { + let current_segment_hash = bytecode_hash_node( + data_ptr=data_ptr, data_length=segment_length + ); + } else { + // Set the first felt of the bytecode to -1 to make sure that the execution cannot jump + // to this segment (-1 is an invalid opcode). + // The hash in this case is guessed and the actual bytecode is unconstrained (except for + // the first felt). + assert data_ptr[0] = -1; + + assert [range_check_ptr] = segment_length; + tempvar range_check_ptr = range_check_ptr + 1; + tempvar poseidon_ptr = poseidon_ptr; + tempvar current_segment_hash = nondet %{ bytecode_segment_structure.hash() %}; + } + } + + // Add the segment length and hash to the hash state. + // Use the poseidon builtin directly for performance reasons. + assert poseidon_ptr.input = PoseidonBuiltinState( + s0=poseidon_state.s0 + segment_length, + s1=poseidon_state.s1 + current_segment_hash, + s2=poseidon_state.s2, + ); + let poseidon_state = poseidon_ptr.output; + let poseidon_ptr = &poseidon_ptr[1]; + + %{ vm_exit_scope() %} + + return bytecode_hash_internal_node( + data_ptr=&data_ptr[segment_length], data_length=data_length - segment_length + ); +} + +func hash_entry_points{poseidon_ptr: PoseidonBuiltin*, hash_state: HashState}( + entry_points: CompiledClassEntryPoint*, n_entry_points: felt +) { + let inner_hash_state = hash_init(); + hash_entry_points_inner{hash_state=inner_hash_state}( + entry_points=entry_points, n_entry_points=n_entry_points + ); + let hash: felt = hash_finalize(hash_state=inner_hash_state); + hash_update_single(item=hash); + + return (); +} + +func hash_entry_points_inner{poseidon_ptr: PoseidonBuiltin*, hash_state: HashState}( + entry_points: CompiledClassEntryPoint*, n_entry_points: felt +) { + if (n_entry_points == 0) { + return (); + } + + hash_update_single(item=entry_points.selector); + hash_update_single(item=entry_points.offset); + + // Hash builtins. + hash_update_with_nested_hash( + data_ptr=entry_points.builtin_list, data_length=entry_points.n_builtins + ); + + return hash_entry_points_inner( + entry_points=&entry_points[1], n_entry_points=n_entry_points - 1 + ); +} + +// A list entry that maps a hash to the corresponding contract classes. +struct CompiledClassFact { + // The hash of the contract. This member should be first, so that we can lookup items + // with the hash as key, using find_element(). + hash: felt, + compiled_class: CompiledClass*, +} + +// Loads the contract classes from the 'os_input' hint variable. +// Returns CompiledClassFact list that maps a hash to a CompiledClass. +func load_compiled_class_facts{poseidon_ptr: PoseidonBuiltin*, range_check_ptr}() -> ( + n_compiled_class_facts: felt, compiled_class_facts: CompiledClassFact* +) { + alloc_locals; + local n_compiled_class_facts; + local compiled_class_facts: CompiledClassFact*; + %{ + ids.compiled_class_facts = segments.add() + ids.n_compiled_class_facts = len(os_input.compiled_classes) + vm_enter_scope({ + 'compiled_class_facts': iter(os_input.compiled_classes.items()), + 'compiled_class_visited_pcs': os_input.compiled_class_visited_pcs, + }) + %} + + let (builtin_costs: felt*) = alloc(); + assert builtin_costs[0] = 0; + assert builtin_costs[1] = 0; + assert builtin_costs[2] = 0; + assert builtin_costs[3] = 0; + assert builtin_costs[4] = 0; + + load_compiled_class_facts_inner( + n_compiled_class_facts=n_compiled_class_facts, + compiled_class_facts=compiled_class_facts, + builtin_costs=builtin_costs, + ); + %{ vm_exit_scope() %} + + return ( + n_compiled_class_facts=n_compiled_class_facts, compiled_class_facts=compiled_class_facts + ); +} + +// Loads 'n_compiled_class_facts' from the hint 'compiled_class_facts' and appends the +// corresponding CompiledClassFact to compiled_class_facts. +func load_compiled_class_facts_inner{poseidon_ptr: PoseidonBuiltin*, range_check_ptr}( + n_compiled_class_facts, compiled_class_facts: CompiledClassFact*, builtin_costs: felt* +) { + if (n_compiled_class_facts == 0) { + return (); + } + alloc_locals; + + let compiled_class_fact = compiled_class_facts[0]; + let compiled_class = compiled_class_fact.compiled_class; + + // Fetch contract data form hints. + %{ + from starkware.starknet.core.os.contract_class.compiled_class_hash import ( + create_bytecode_segment_structure, + get_compiled_class_struct, + ) + + compiled_class_hash, compiled_class = next(compiled_class_facts) + + bytecode_segment_structure = create_bytecode_segment_structure( + bytecode=compiled_class.bytecode, + bytecode_segment_lengths=compiled_class.bytecode_segment_lengths, + visited_pcs=compiled_class_visited_pcs[compiled_class_hash], + ) + + cairo_contract = get_compiled_class_struct( + identifiers=ids._context.identifiers, + compiled_class=compiled_class, + bytecode=bytecode_segment_structure.bytecode_with_skipped_segments() + ) + ids.compiled_class = segments.gen_arg(cairo_contract) + %} + + validate_entry_points( + n_entry_points=compiled_class.n_external_functions, + entry_points=compiled_class.external_functions, + ); + + validate_entry_points( + n_entry_points=compiled_class.n_l1_handlers, entry_points=compiled_class.l1_handlers + ); + + %{ + vm_enter_scope({ + "bytecode_segment_structure": bytecode_segment_structure + }) + %} + let (hash) = compiled_class_hash(compiled_class); + %{ vm_exit_scope() %} + compiled_class_fact.hash = hash; + + // Compiled classes are expected to end with a `ret` opcode followed by a pointer to the + // builtin costs. + assert compiled_class.bytecode_ptr[compiled_class.bytecode_length] = 0x208b7fff7fff7ffe; + assert compiled_class.bytecode_ptr[compiled_class.bytecode_length + 1] = cast( + builtin_costs, felt + ); + + %{ + computed_hash = ids.compiled_class_fact.hash + expected_hash = compiled_class_hash + assert computed_hash == expected_hash, ( + "Computed compiled_class_hash is inconsistent with the hash in the os_input. " + f"Computed hash = {computed_hash}, Expected hash = {expected_hash}.") + + vm_load_program( + compiled_class.get_runnable_program(entrypoint_builtins=[]), + ids.compiled_class.bytecode_ptr + ) + %} + + return load_compiled_class_facts_inner( + n_compiled_class_facts=n_compiled_class_facts - 1, + compiled_class_facts=compiled_class_facts + CompiledClassFact.SIZE, + builtin_costs=builtin_costs, + ); +} diff --git a/cairo0-bootloader/contract_class/compiled_class_hash_utils.py b/cairo0-bootloader/contract_class/compiled_class_hash_utils.py new file mode 100644 index 0000000..489fbd0 --- /dev/null +++ b/cairo0-bootloader/contract_class/compiled_class_hash_utils.py @@ -0,0 +1,110 @@ +import itertools +import os +from functools import lru_cache +from typing import List + +from starkware.cairo.common.structs import CairoStructFactory, CairoStructProxy +from starkware.cairo.lang.cairo_constants import DEFAULT_PRIME +from starkware.cairo.lang.compiler.cairo_compile import compile_cairo_files +from starkware.cairo.lang.compiler.identifier_definition import ConstDefinition +from starkware.cairo.lang.compiler.identifier_manager import IdentifierManager +from starkware.cairo.lang.compiler.program import Program +from starkware.cairo.lang.compiler.scoped_name import ScopedName +from starkware.python.utils import as_non_optional, from_bytes +from contract_class.contract_class import ( + CompiledClass, + EntryPointType, +) + +CAIRO_FILE = os.path.join(os.path.dirname(__file__), "compiled_class.cairo") +COMPILED_CLASS_MODULE = "contract_class.compiled_class" + + +@lru_cache() +def load_compiled_class_cairo_program() -> Program: + return compile_cairo_files( + [CAIRO_FILE], + prime=DEFAULT_PRIME, + main_scope=ScopedName.from_string(COMPILED_CLASS_MODULE), + ) + + +@lru_cache() +def _get_empty_compiled_class_structs() -> CairoStructProxy: + program = load_compiled_class_cairo_program() + return CairoStructFactory( + identifiers=program.identifiers, + additional_imports=[ + f"{COMPILED_CLASS_MODULE}.CompiledClass", + f"{COMPILED_CLASS_MODULE}.CompiledClassEntryPoint", + ], + ).structs + + +def _get_contract_entry_points( + structs: CairoStructProxy, + compiled_class: CompiledClass, + entry_point_type: EntryPointType, +) -> List[CairoStructProxy]: + # Check validity of entry points. + program_length = len(compiled_class.bytecode) + entry_points = compiled_class.entry_points_by_type[entry_point_type] + for entry_point in entry_points: + assert ( + 0 <= entry_point.offset < program_length + ), f"Invalid entry point offset {entry_point.offset}, len(program_data)={program_length}." + + return [ + structs.CompiledClassEntryPoint( + selector=entry_point.selector, + offset=entry_point.offset, + n_builtins=len(as_non_optional(entry_point.builtins)), + builtin_list=[ + from_bytes(builtin.encode("ascii")) + for builtin in as_non_optional(entry_point.builtins) + ], + ) + for entry_point in entry_points + ] + + +def get_compiled_class_struct( + compiled_class: CompiledClass, bytecode: List[int] +) -> CairoStructProxy: + """ + Returns the serialization of a compiled class as a list of field elements. + Note that the bytecode is passed explicitly (rather than taken from the compiled class) to + allow skipping some code segments. + """ + structs = _get_empty_compiled_class_structs() + + external_functions, l1_handlers, constructors = ( + _get_contract_entry_points( + structs=structs, + compiled_class=compiled_class, + entry_point_type=entry_point_type, + ) + for entry_point_type in ( + EntryPointType.EXTERNAL, + EntryPointType.L1_HANDLER, + EntryPointType.CONSTRUCTOR, + ) + ) + flat_external_functions, flat_l1_handlers, flat_constructors = ( + list(itertools.chain.from_iterable(entry_points)) + for entry_points in (external_functions, l1_handlers, constructors) + ) + + assert len(bytecode) == len(compiled_class.bytecode) + + return structs.CompiledClass( + compiled_class_version="", + n_external_functions=len(external_functions), + external_functions=flat_external_functions, + n_l1_handlers=len(l1_handlers), + l1_handlers=flat_l1_handlers, + n_constructors=len(constructors), + constructors=flat_constructors, + bytecode_length=len(bytecode), + bytecode_ptr=bytecode, + ) diff --git a/cairo0-bootloader/contract_class/contract_class.cairo b/cairo0-bootloader/contract_class/contract_class.cairo new file mode 100644 index 0000000..9a8763b --- /dev/null +++ b/cairo0-bootloader/contract_class/contract_class.cairo @@ -0,0 +1,83 @@ +from starkware.cairo.common.cairo_builtins import PoseidonBuiltin +from starkware.cairo.common.hash_state_poseidon import ( + HashState, + hash_finalize, + hash_init, + hash_update_single, + hash_update_with_nested_hash, +) +from starkware.starknet.common.storage import normalize_address + +const CONTRACT_CLASS_VERSION = 'CONTRACT_CLASS_V0.1.0'; + +struct ContractEntryPoint { + // A field element that encodes the signature of the called function. + selector: felt, + function_idx: felt, +} + +struct ContractClass { + contract_class_version: felt, + + // The length and pointer to the external entry point table of the contract. + n_external_functions: felt, + external_functions: ContractEntryPoint*, + + // The length and pointer to the L1 handler entry point table of the contract. + n_l1_handlers: felt, + l1_handlers: ContractEntryPoint*, + + // The length and pointer to the constructor entry point table of the contract. + n_constructors: felt, + constructors: ContractEntryPoint*, + + // starknet_keccak of the contract ABI. + // Note that the OS does not enforce any constraints on this value. + abi_hash: felt, + + // The length and pointer of the Sierra program. + sierra_program_length: felt, + sierra_program_ptr: felt*, +} + +func class_hash{poseidon_ptr: PoseidonBuiltin*, range_check_ptr: felt}( + contract_class: ContractClass* +) -> (hash: felt) { + assert contract_class.contract_class_version = CONTRACT_CLASS_VERSION; + + let hash_state: HashState = hash_init(); + with hash_state { + hash_update_single(item=contract_class.contract_class_version); + + // Hash external entry points. + hash_update_with_nested_hash( + data_ptr=contract_class.external_functions, + data_length=contract_class.n_external_functions * ContractEntryPoint.SIZE, + ); + + // Hash L1 handler entry points. + hash_update_with_nested_hash( + data_ptr=contract_class.l1_handlers, + data_length=contract_class.n_l1_handlers * ContractEntryPoint.SIZE, + ); + + // Hash constructor entry points. + hash_update_with_nested_hash( + data_ptr=contract_class.constructors, + data_length=contract_class.n_constructors * ContractEntryPoint.SIZE, + ); + + // Hash abi_hash. + hash_update_single(item=contract_class.abi_hash); + + // Hash Sierra program. + hash_update_with_nested_hash( + data_ptr=contract_class.sierra_program_ptr, + data_length=contract_class.sierra_program_length, + ); + } + + let hash: felt = hash_finalize(hash_state=hash_state); + let (normalized_hash) = normalize_address(addr=hash); + return (hash=normalized_hash); +} diff --git a/cairo0-bootloader/contract_class/contract_class.py b/cairo0-bootloader/contract_class/contract_class.py new file mode 100644 index 0000000..9c23696 --- /dev/null +++ b/cairo0-bootloader/contract_class/contract_class.py @@ -0,0 +1,390 @@ +import dataclasses +import json +import re +from abc import abstractmethod +from dataclasses import field +from enum import Enum, auto +from typing import Any, Dict, List, Optional, Union + +import marshmallow +import marshmallow.fields as mfields +import marshmallow_dataclass + +from services.everest.definitions import fields as everest_fields +from starkware.cairo.lang.cairo_constants import DEFAULT_PRIME +from starkware.cairo.lang.compiler.identifier_manager import IdentifierManager +from starkware.cairo.lang.compiler.preprocessor.flow import ReferenceManager +from starkware.cairo.lang.compiler.program import CairoHint, Program +from starkware.cairo.lang.compiler.scoped_name import ScopedName +from starkware.python.utils import as_non_optional +from starkware.starknet.definitions import fields +from starkware.starknet.definitions.error_codes import StarknetErrorCode +from starkware.starknet.public.abi import AbiType +from starkware.starkware_utils.error_handling import stark_assert +from starkware.starkware_utils.marshmallow_dataclass_fields import ( + IntAsHex, + additional_metadata, +) +from starkware.starkware_utils.subsequence import is_subsequence +from starkware.starkware_utils.validated_dataclass import ( + ValidatedDataclass, + ValidatedMarshmallowDataclass, +) + +# An ordered list of the supported builtins. +SUPPORTED_BUILTINS = [ + "pedersen", + "range_check", + "ecdsa", + "bitwise", + "ec_op", + "poseidon", + "segment_arena", +] + +# Utilites. + + +def validate_builtins(builtins: Optional[List[str]]): + if builtins is None: + return + + stark_assert( + is_subsequence(builtins, SUPPORTED_BUILTINS), + code=StarknetErrorCode.INVALID_CONTRACT_CLASS, + message=f"{builtins} is not a subsequence of {SUPPORTED_BUILTINS}.", + ) + + +# Objects. + + +class EntryPointType(Enum): + EXTERNAL = 0 + L1_HANDLER = auto() + CONSTRUCTOR = auto() + + +@dataclasses.dataclass(frozen=True) +class ContractEntryPoint(ValidatedDataclass): + # A field element that encodes the signature of the called function. + selector: int = field(metadata=fields.entry_point_selector_metadata) + function_idx: int = field(metadata=fields.entry_point_function_idx_metadata) + + +@marshmallow_dataclass.dataclass(frozen=True) +class ContractClass(ValidatedMarshmallowDataclass): + """ + Represents a contract class in the StarkNet network. + """ + + contract_class_version: str + sierra_program: List[int] = field(metadata=everest_fields.felt_as_hex_list_metadata) + entry_points_by_type: Dict[EntryPointType, List[ContractEntryPoint]] + abi: str + + def get_bytecode_size(self) -> int: + return len(self.sierra_program) + + def get_abi_size(self) -> int: + return len(self.abi) + + +@marshmallow_dataclass.dataclass(frozen=True) +class CompiledClassEntryPoint(ValidatedDataclass): + # A field element that encodes the signature of the called function. + selector: int = field(metadata=fields.entry_point_selector_metadata) + # The offset of the instruction that should be called within the contract bytecode. + offset: int = field(metadata=fields.entry_point_offset_metadata) + # Builtins used by the entry point. + builtins: Optional[List[str]] + + @marshmallow.decorators.pre_load + def load_offset_formatted_as_hex( + self, data: Dict[str, Any], many: bool, **kwargs + ) -> Dict[str, Any]: + offset = data["offset"] + if isinstance(offset, str): + assert ( + re.match("^0x[0-9a-f]+$", offset) is not None + ), f"offset field is of unexpected format: {offset}." + data["offset"] = int(offset, 16) + + return data + + +# Mypy has a problem with dataclasses that contain unimplemented abstract methods. +# See https://github.com/python/mypy/issues/5374 for details on this problem. +@marshmallow_dataclass.dataclass(frozen=True) # type: ignore[misc] +class CompiledClassBase(ValidatedMarshmallowDataclass): + entry_points_by_type: Dict[EntryPointType, List[CompiledClassEntryPoint]] + + @abstractmethod + def get_builtins(self) -> List[str]: + """ + Returns the "builtins" attribute of the compiled class. + """ + + @abstractmethod + def get_prime(self) -> int: + """ + Returns the "prime" attribute of the compiled class. + """ + + @abstractmethod + def get_bytecode(self) -> List[int]: + """ + Returns the "bytecode" attribute of the compiled class. + """ + + def __post_init__(self): + super().__post_init__() + + for entry_points in self.entry_points_by_type.values(): + stark_assert( + all( + entry_points[i].selector < entry_points[i + 1].selector + for i in range(len(entry_points) - 1) + ), + code=StarknetErrorCode.INVALID_CONTRACT_CLASS, + message="Entry points must be unique and sorted.", + ) + + constructor_eps = self.entry_points_by_type.get(EntryPointType.CONSTRUCTOR) + stark_assert( + constructor_eps is not None, + code=StarknetErrorCode.INVALID_CONTRACT_CLASS, + message="The contract is missing constructor endpoints. Wrong compiler version?", + ) + + stark_assert( + len(as_non_optional(constructor_eps)) <= 1, + code=StarknetErrorCode.INVALID_CONTRACT_CLASS, + message="A contract may have at most 1 constructor.", + ) + + def validate(self): + validate_builtins(builtins=self.get_builtins()) + for entry_points in self.entry_points_by_type.values(): + for entry_point in entry_points: + validate_builtins(builtins=entry_point.builtins) + + stark_assert( + self.get_prime() == DEFAULT_PRIME, + code=StarknetErrorCode.INVALID_CONTRACT_CLASS, + message=( + f"Invalid value for field prime: {self.get_prime()}. Expected: {DEFAULT_PRIME}." + ), + ) + + @property + def n_entry_points(self) -> int: + """ + Returns the number of entry points (note that functions with multiple decorators are + counted more than once). + """ + return sum(len(eps) for eps in self.entry_points_by_type.values()) + + +# Represents a nested list of integers. E.g., [1, [2, [3], 4], 5, 6]. +NestedIntList = Union[int, List[Any]] + + +@marshmallow_dataclass.dataclass(frozen=True) +class CompiledClass(CompiledClassBase): + """ + Represents a compiled contract class in the Starknet network. + """ + + prime: int = field( + metadata=additional_metadata(marshmallow_field=IntAsHex(required=True)) + ) + bytecode: List[int] = field( + metadata=additional_metadata( + marshmallow_field=mfields.List(IntAsHex(), required=True) + ) + ) + # Represents the structure of the bytecode segments, using a nested list of segment lengths. + # For example, [2, [3, 4]] represents a bytecode with 2 segments, the first is a leaf of length + # 2 and the second is a node with 2 children of lengths 3 and 4. + bytecode_segment_lengths: NestedIntList = field( + metadata=additional_metadata(marshmallow_field=fields.NestedIntListField()) + ) + # Rust hints. + hints: List[Any] + pythonic_hints: Dict[int, List[CairoHint]] + compiler_version: str = field( + metadata=dict( + marshmallow_field=mfields.String(required=False, load_default=None) + ) + ) + + def get_builtins(self) -> List[str]: + return [] + + def get_prime(self) -> int: + return self.prime + + def get_bytecode(self) -> List[int]: + return self.bytecode + + def get_bytecode_size(self) -> int: + return len(self.get_bytecode()) + + @marshmallow.decorators.pre_load + def parse_pythonic_hints( + self, data: Dict[str, Any], many: bool, **kwargs + ) -> Dict[str, Any]: + """ + Parses Cairo 1.0 casm hints. + Each hint comprises a two-item List: an ID (int) and a List of hint codes (strings). + The returned CairoHint object takes empty "accessible_scopes" and "flow_tracking_data" + values as these are only relevant to Cairo 0 programs. + """ + # Invalidate 0.11.0-pre-release compiled classes. + stark_assert( + "program" not in data, + code=StarknetErrorCode.INVALID_COMPILED_CLASS, + message="Unsupported compiled class format. " + "Cairo 1.0 compiled class must not contain the attribute `program`.", + ) + # Invalidate compiled classes without pythonic hints. + stark_assert( + "pythonic_hints" in data, + code=StarknetErrorCode.INVALID_COMPILED_CLASS, + message="Unsupported compiled class format. " + "Cairo 1.0 compiled class must contain the attribute `pythonic_hints`.", + ) + + pythonic_hints = data["pythonic_hints"] + empty_accessible_scope: List = [] + empty_flow_tracking_data: Dict[str, Any] = { + "ap_tracking": {"group": 0, "offset": 0}, + "reference_ids": {}, + } + + data["pythonic_hints"] = { + hint_id: [ + { + "code": hint_code, + "accessible_scopes": empty_accessible_scope, + "flow_tracking_data": empty_flow_tracking_data, + } + for hint_code in hint_codes + ] + for hint_id, hint_codes in pythonic_hints + } + + return data + + @marshmallow.decorators.pre_load + def default_bytecode_segment_lengths( + self, data: Dict[str, Any], many: bool, **kwargs + ) -> Dict[str, Any]: + # If bytecode_segment_lengths is missing, use a single leaf (which forces loading the entire + # program). + + if "bytecode_segment_lengths" not in data: + data["bytecode_segment_lengths"] = len(data["bytecode"]) + return data + + @marshmallow.decorators.post_dump + def dump_pythonic_hints( + self, data: Dict[str, Any], many: bool, **kwargs + ) -> Dict[str, Any]: + data["pythonic_hints"] = [ + [hint_id, [hint_obj["code"] for hint_obj in hint_obj_list]] + for hint_id, hint_obj_list in data["pythonic_hints"].items() + ] + return data + + def __post_init__(self): + super().__post_init__() + + for entry_points in self.entry_points_by_type.values(): + for entry_point in entry_points: + stark_assert( + entry_point.builtins is not None, + code=StarknetErrorCode.INVALID_CONTRACT_CLASS, + message=f"Missing builtins for entry point {entry_point.selector}.", + ) + + def get_runnable_program(self, entrypoint_builtins: List[str]) -> Program: + """ + Converts the HintedProgram into a Program object that can be run by the Python CairoRunner. + """ + return Program( + prime=self.prime, + data=self.bytecode, + # Buitlins for the entrypoint to execute. + builtins=entrypoint_builtins, + hints=self.pythonic_hints, + compiler_version=self.compiler_version, + # Fill missing fields with empty values. + main_scope=ScopedName(), + identifiers=IdentifierManager(), + reference_manager=ReferenceManager(), + attributes=[], + debug_info=None, + ) + + +@marshmallow_dataclass.dataclass(frozen=True) +class DeprecatedCompiledClass(CompiledClassBase): + """ + Represents a contract in the StarkNet network that was compiled by the old (pythonic) compiler. + """ + + program: Program + abi: Optional[AbiType] = None + + def get_builtins(self) -> List[str]: + return self.program.builtins + + def get_prime(self) -> int: + return self.program.prime + + def get_bytecode(self) -> List[int]: + return self.program.data + + def get_bytecode_size(self) -> int: + return len(self.get_bytecode()) + + def get_abi_size(self) -> int: + return len(json.dumps(self.abi)) if self.abi is not None else 0 + + @marshmallow.decorators.post_dump + def remove_none_builtins( + self, data: Dict[str, Any], many: bool, **kwargs + ) -> Dict[str, Any]: + """ + Needed for backward compatibility of hash computation for deprecated contracts. + """ + for entry_points in data["entry_points_by_type"].values(): + for entry_point in entry_points: + # Verify that builtins is None and remove it. + stark_assert( + entry_point.pop("builtins") is None, + code=StarknetErrorCode.INVALID_CONTRACT_CLASS, + message="Entry point should not have builtins in deprecated contracts.", + ) + + return data + + def remove_debug_info(self) -> "DeprecatedCompiledClass": + """ + Sets debug_info in the Cairo contract program to None. + Returns an altered DeprecatedCompiledClass instance. + """ + altered_program = dataclasses.replace(self.program, debug_info=None) + return dataclasses.replace(self, program=altered_program) + + +@dataclasses.dataclass(frozen=True) +class RawCompiledClass: + """ + Represents a raw compiled contract class in the Starknet network. + """ + + raw_compiled_class: str + version: int diff --git a/cairo0-bootloader/contract_class/run.py b/cairo0-bootloader/contract_class/run.py new file mode 100644 index 0000000..a7cdaea --- /dev/null +++ b/cairo0-bootloader/contract_class/run.py @@ -0,0 +1,5 @@ +from contract_class.contract_class import * + +with open("../../cairo1/contract.json") as contract_file: + contract = CompiledClass.deserialize(contract_file.read().encode()) + print(contract.get_runnable_program("output")) diff --git a/cairo0-bootloader/setup.py b/cairo0-bootloader/setup.py index 7ca13a6..52dc298 100644 --- a/cairo0-bootloader/setup.py +++ b/cairo0-bootloader/setup.py @@ -13,10 +13,12 @@ "bootloader.recursive": ["*.cairo", "*/*.cairo"], "bootloader.starknet_with_keccak": ["*.cairo", "*/*.cairo"], "bootloader.starknet": ["*.cairo", "*/*.cairo"], + "bootloader.contract": ["*.cairo", "*/*.cairo"], "bootloader": ["*.cairo", "*/*.cairo"], "builtin_selection": ["*.cairo", "*/*.cairo"], "common.builtin_poseidon": ["*.cairo", "*/*.cairo"], "common": ["*.cairo", "*/*.cairo"], + "contract_class": ["*.cairo", "*/*.cairo"], "lang.compiler": ["cairo.ebnf", "lib/*.cairo"], }, ) diff --git a/cairo1/Scarb.lock b/cairo1/Scarb.lock deleted file mode 100644 index 81f37d4..0000000 --- a/cairo1/Scarb.lock +++ /dev/null @@ -1,6 +0,0 @@ -# Code generated by scarb DO NOT EDIT. -version = 1 - -[[package]] -name = "example" -version = "0.1.0" diff --git a/compile.py b/compile.py index bbab949..aa3048f 100644 --- a/compile.py +++ b/compile.py @@ -6,16 +6,8 @@ log_and_run( [ - f"cairo-compile --cairo_path=. bootloader/recursive_with_poseidon/simple_bootloader.cairo --output {current_dir}/bootloader.json --proof_mode", + f"cairo-compile --cairo_path=. bootloader/contract/contract_bootloader.cairo --output {current_dir}/bootloader.json --proof_mode", ], "Compile bootloader program", cwd="cairo0-bootloader", ) - - log_and_run( - [ - f"scarb build", - ], - "Compile cairo1 project", - cwd="cairo1", - ) diff --git a/contract/.gitignore b/contract/.gitignore new file mode 100644 index 0000000..a0d82f2 --- /dev/null +++ b/contract/.gitignore @@ -0,0 +1,4 @@ +target +.snfoundry_cache/ + +contract.* \ No newline at end of file diff --git a/contract/Scarb.lock b/contract/Scarb.lock new file mode 100644 index 0000000..e4f03d7 --- /dev/null +++ b/contract/Scarb.lock @@ -0,0 +1,14 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "contract" +version = "0.1.0" +dependencies = [ + "snforge_std", +] + +[[package]] +name = "snforge_std" +version = "0.24.0" +source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.24.0#95e9fb09cb91b3c05295915179ee1b55bf923653" diff --git a/contract/Scarb.toml b/contract/Scarb.toml new file mode 100644 index 0000000..65e57ab --- /dev/null +++ b/contract/Scarb.toml @@ -0,0 +1,18 @@ +[package] +name = "contract" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +starknet = "2.6.3" + +[dev-dependencies] +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.24.0" } + +[[target.starknet-contract]] +sierra = true + +[scripts] +test = "snforge test" diff --git a/contract/src/lib.cairo b/contract/src/lib.cairo new file mode 100644 index 0000000..853ec8a --- /dev/null +++ b/contract/src/lib.cairo @@ -0,0 +1,41 @@ +#[starknet::interface] +pub trait IHelloBootloader { + fn main(ref self: TContractState, input: Array) -> Array; +} + +#[starknet::contract] +mod HelloBootloader { + #[derive(Drop, Serde)] + struct Input { + a: u32, + b: u32, + c: u32, + } + + #[derive(Drop, Serde)] + struct Output { + a_2: u32, + b_2: u32, + c_2: u32, + } + + #[storage] + struct Storage {} + + #[abi(embed_v0)] + impl HelloBootloaderImpl of super::IHelloBootloader { + fn main(ref self: ContractState, input: Array) -> Array { + let mut input_span = input.span(); + let input = Serde::::deserialize(ref input_span).unwrap(); + + let a_2 = input.a * input.a; + let b_2 = input.b * input.b; + let c_2 = input.c * input.c; + assert(a_2 + b_2 == c_2, 'invalid value'); + + let mut output = array![]; + Output { a_2, b_2, c_2, }.serialize(ref output); + output + } + } +} diff --git a/install.py b/install.py index d0dfc27..70299a8 100644 --- a/install.py +++ b/install.py @@ -23,18 +23,10 @@ def log_and_run(commands, description, cwd=None): log_and_run( [ - "pip install cairo-lang==0.13.1", + "pip install cairo-lang-0.13.1.zip", "pip install aiofiles", "pip install cairo0-bootloader/", ], "Installing cairo-lang", cwd=".", ) - - log_and_run( - [ - "cargo install --path .", - ], - "Installing cairo-vm-runner", - cwd="runner", - ) diff --git a/run.py b/run.py index fb30e53..e14e30d 100644 --- a/run.py +++ b/run.py @@ -5,7 +5,7 @@ [ "cairo-run \ --program=bootloader.json \ - --layout=recursive_with_poseidon \ + --layout=starknet_with_keccak \ --program_input=bootloader_input.json \ --air_public_input=bootloader_public_input.json \ --air_private_input=bootloader_private_input.json \ @@ -15,6 +15,6 @@ --proof_mode \ --print_info" ], - "Running cairo1 pie in cairo0 bootloader", + "Running contract in cairo0 bootloader", cwd=".", )