Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

dev: os output #43

Merged
merged 6 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
<br>
</div>

## [Overview](https://hackmd.io/@pragma/ByP-iux1T)

Rust Library for running the Starknet OS via the [Cairo VM](https://github.com/lambdaclass/cairo-vm).
Rust Library for running the [Starknet OS](https://hackmd.io/@pragma/ByP-iux1T) via the [Cairo VM](https://github.com/lambdaclass/cairo-vm).

## Test Setup

Expand All @@ -17,15 +15,14 @@ Rust Library for running the Starknet OS via the [Cairo VM](https://github.com/l
cargo test
```

### Reset Tests
**Reset Tests**

```bash
./scripts/teardown-tests.sh
```

### Debug Single Cairo Program
**Debug Single Cairo Program**

```bash
./scripts/debug-hint.sh load_deprecated_class
```

14 changes: 13 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use starknet_api::block::{BlockNumber, BlockTimestamp};
use starknet_api::core::{ChainId, ContractAddress, PatriciaKey};
use starknet_api::hash::StarkHash;
use starknet_api::hash::{pedersen_hash_array, StarkFelt, StarkHash};
use starknet_api::{contract_address, patricia_key};
use starknet_crypto::FieldElement;

use crate::error::SnOsError;

const DEFAULT_CONFIG_PATH: &str = "cairo-lang/src/starkware/starknet/definitions/general_config.yml";

pub const STARKNET_OS_CONFIG_HASH_VERSION: &str = "StarknetOsConfig1";
pub const DEFAULT_LAYOUT: &str = "starknet_with_keccak";
pub const DEFAULT_COMPILED_OS: &str = "build/os_latest.json";
pub const DEFAULT_INPUT_PATH: &str = "build/input.json";
Expand All @@ -35,6 +37,16 @@ pub struct StarknetOsConfig {
pub fee_token_address: ContractAddress,
}

impl StarknetOsConfig {
pub fn hash(&self) -> StarkHash {
pedersen_hash_array(&[
StarkFelt::from(FieldElement::from_byte_slice_be(STARKNET_OS_CONFIG_HASH_VERSION.as_bytes()).unwrap()),
StarkFelt::from(u128::from_str_radix(&self.chain_id.0, 16).unwrap()),
*self.fee_token_address.0.key(),
])
}
}

#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct StarknetGeneralConfig {
pub starknet_os_config: StarknetOsConfig,
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum SnOsError {
SharpRequest(String),
#[error("Starknet Os Runner Error: {0}")]
Runner(CairoRunError),
#[error("SnOs Output Error: {0}")]
Output(String),
}

#[derive(thiserror::Error, Clone, Debug)]
Expand Down
54 changes: 14 additions & 40 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod classes;
pub mod output;

use std::collections::HashMap;
use std::io::Write;
Expand Down Expand Up @@ -32,6 +33,19 @@ pub struct StarknetOsInput {
pub block_hash: Felt252,
}

impl StarknetOsInput {
pub fn load(path: &str) -> Self {
let raw_input = fs::read_to_string(path::PathBuf::from(path)).unwrap();
serde_json::from_str(&raw_input).unwrap()
}
pub fn dump(&self, path: &str) -> Result<(), SnOsError> {
fs::File::create(path)
.unwrap()
.write_all(&serde_json::to_vec(&self).unwrap())
.map_err(|e| SnOsError::CatchAll(format!("{e}")))
}
}

#[serde_as]
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CommitmentInfo {
Expand Down Expand Up @@ -127,43 +141,3 @@ pub struct StarknetOsOutput {
/// List of the newly declared contract classes.
pub contract_class_diff: Vec<Felt252>,
}

impl StarknetOsOutput {
#[allow(clippy::too_many_arguments)]
pub fn new(
prev_state_root: Felt252,
new_state_root: Felt252,
block_number: Felt252,
block_hash: Felt252,
config_hash: Felt252,
messages_to_l1: Vec<Felt252>,
messages_to_l2: Vec<Felt252>,
state_updates: Vec<Felt252>,
contract_class_diff: Vec<Felt252>,
) -> Self {
Self {
prev_state_root,
new_state_root,
block_number,
block_hash,
config_hash,
messages_to_l1,
messages_to_l2,
state_updates,
contract_class_diff,
}
}
}

impl StarknetOsInput {
pub fn load(path: &str) -> Self {
let raw_input = fs::read_to_string(path::PathBuf::from(path)).unwrap();
serde_json::from_str(&raw_input).unwrap()
}
pub fn dump(&self, path: &str) -> Result<(), SnOsError> {
fs::File::create(path)
.unwrap()
.write_all(&serde_json::to_vec(&self).unwrap())
.map_err(|e| SnOsError::CatchAll(format!("{e}")))
}
}
66 changes: 66 additions & 0 deletions src/io/output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use cairo_felt::Felt252;
use cairo_vm::types::relocatable::MaybeRelocatable;
use cairo_vm::vm::runners::builtin_runner::BuiltinRunner;
use cairo_vm::vm::vm_core::VirtualMachine;

use super::StarknetOsOutput;
use crate::error::SnOsError;
use crate::utils::felt_vm2usize;

const PREVIOUS_MERKLE_UPDATE_OFFSET: usize = 0;
const NEW_MERKLE_UPDATE_OFFSET: usize = 1;
const BLOCK_NUMBER_OFFSET: usize = 2;
const BLOCK_HASH_OFFSET: usize = 3;
const CONFIG_HASH_OFFSET: usize = 4;
const HEADER_SIZE: usize = 5;

impl StarknetOsOutput {
pub fn from_run(vm: &VirtualMachine) -> Result<Self, SnOsError> {
// os_output = runner.vm_memory.get_range_as_ints(
// addr=runner.output_builtin.base, size=builtin_end_ptrs[0] - runner.output_builtin.base
// )
let builtin_end_ptrs = vm.get_return_values(8).map_err(|e| SnOsError::CatchAll(e.to_string()))?;
let output_base = vm
.get_builtin_runners()
.iter()
.find(|&elt| matches!(elt, BuiltinRunner::Output(_)))
.expect("Os vm should have the output builtin")
.base();
let size_bound_up = match builtin_end_ptrs.last().unwrap() {
MaybeRelocatable::Int(val) => val,
_ => panic!("Value should be an int"),
};

// Get is input and check that everything is an integer.
let raw_output = vm
.get_range((output_base as isize, 0).into(), felt_vm2usize(Some(&(size_bound_up.clone() - output_base)))?);
let raw_output: Vec<Felt252> = raw_output
.iter()
.map(|x| {
if let MaybeRelocatable::Int(val) = x.clone().unwrap().into_owned() {
val
} else {
panic!("Output should be all integers")
}
})
.collect();

decode_output(raw_output)
}
}

pub fn decode_output(mut os_output: Vec<Felt252>) -> Result<StarknetOsOutput, SnOsError> {
let header: Vec<Felt252> = os_output.drain(..HEADER_SIZE).collect();

Ok(StarknetOsOutput {
prev_state_root: header[PREVIOUS_MERKLE_UPDATE_OFFSET].clone(),
new_state_root: header[NEW_MERKLE_UPDATE_OFFSET].clone(),
block_number: header[BLOCK_NUMBER_OFFSET].clone(),
block_hash: header[BLOCK_HASH_OFFSET].clone(),
config_hash: header[CONFIG_HASH_OFFSET].clone(),
messages_to_l1: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(),
messages_to_l2: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(),
state_updates: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(),
contract_class_diff: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(),
})
}
104 changes: 20 additions & 84 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,21 @@ pub mod sharp;
pub mod state;
pub mod utils;

use core::panic;
use std::fs;

use blockifier::block_context::BlockContext;
use blockifier::state::state_api::StateReader;
use cairo_felt::Felt252;
use cairo_vm::cairo_run::CairoRunConfig;
use cairo_vm::types::program::Program;
use cairo_vm::types::relocatable::MaybeRelocatable;
use cairo_vm::vm::errors::vm_exception::VmException;
use cairo_vm::vm::runners::builtin_runner::BuiltinRunner;
use cairo_vm::vm::runners::cairo_pie::CairoPie;
use cairo_vm::vm::runners::cairo_runner::CairoRunner;
use cairo_vm::vm::vm_core::VirtualMachine;
use config::StarknetGeneralConfig;
use error::SnOsError;
use io::StarknetOsOutput;
use state::SharedState;

use crate::io::StarknetOsOutput;

pub struct SnOsRunner {
layout: String,
os_path: String,
Expand All @@ -34,22 +29,6 @@ pub struct SnOsRunner {
}

impl SnOsRunner {
pub fn with_layout(layout: &str) -> Self {
Self { layout: layout.to_string(), ..Self::default() }
}

pub fn with_os_path(os_path: &str) -> Self {
Self { os_path: os_path.to_string(), ..Self::default() }
}

pub fn with_input_path(input_path: &str) -> Self {
Self { input_path: input_path.to_string(), ..Self::default() }
}

pub fn with_block_context(block_context: BlockContext) -> Self {
Self { block_context, ..Self::default() }
}

pub fn run(&self, shared_state: SharedState<impl StateReader>) -> Result<CairoPie, SnOsError> {
// Init CairoRunConfig
let cairo_run_config = CairoRunConfig {
Expand Down Expand Up @@ -91,68 +70,9 @@ impl SnOsRunner {
}

// Prepare and check expected output.
// os_output = runner.vm_memory.get_range_as_ints(
// addr=runner.output_builtin.base, size=builtin_end_ptrs[0] - runner.output_builtin.base
// )
let builtin_end_ptrs = vm.get_return_values(8).map_err(|e| SnOsError::CatchAll(e.to_string()))?;
let output_base = vm
.get_builtin_runners()
.iter()
.find(|&elt| matches!(elt, BuiltinRunner::Output(_)))
.expect("Os vm should have the output builtin")
.base();
let size_bound_up = match builtin_end_ptrs.last().unwrap() {
MaybeRelocatable::Int(val) => val,
_ => panic!("Value should be an int"),
};
// Get is input and check that everything is an integer.
let os_output = vm.get_range(
(output_base as isize, 0).into(),
<usize>::from_be_bytes((size_bound_up.clone() - output_base).to_be_bytes()[..8].try_into().unwrap()),
);
let os_output: Vec<Felt252> = os_output
.iter()
.map(|x| {
if let MaybeRelocatable::Int(val) = x.clone().unwrap().into_owned() {
val
} else {
panic!("Output should be all integers")
}
})
.collect();

let prev_state_root = os_output[0].clone();
let new_state_root = os_output[1].clone();
let block_number = os_output[2].clone();
let block_hash = os_output[3].clone();
let config_hash = os_output[4].clone();
let os_output = &os_output[5..];
let messages_to_l1_size = <usize>::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap());
let messages_to_l1 = os_output[1..1 + messages_to_l1_size].to_vec();

let os_output = &os_output[messages_to_l1_size + 1..];
let messages_to_l2_size = <usize>::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap());
let messages_to_l2 = os_output[1..1 + messages_to_l2_size].to_vec();
let os_output = &os_output[messages_to_l2_size + 1..];

let state_updates_size = <usize>::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap());
let state_updates = os_output[1..1 + state_updates_size].to_vec();
let os_output = &os_output[state_updates_size + 1..];

let contract_class_diff_size = <usize>::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap());
let contract_class_diff = os_output[1..1 + contract_class_diff_size].to_vec();
let real_output = StarknetOsOutput::new(
prev_state_root,
new_state_root,
block_number,
block_hash,
config_hash,
messages_to_l1,
messages_to_l2,
state_updates,
contract_class_diff,
);
println!("{:?}", real_output);
let _os_output = StarknetOsOutput::from_run(&vm)?;

println!("{:?}", _os_output);

vm.verify_auto_deductions().map_err(|e| SnOsError::Runner(e.into()))?;
cairo_runner.read_return_values(&mut vm).map_err(|e| SnOsError::Runner(e.into()))?;
Expand All @@ -163,6 +83,22 @@ impl SnOsRunner {

Ok(pie)
}

pub fn with_layout(layout: &str) -> Self {
Self { layout: layout.to_string(), ..Self::default() }
}

pub fn with_os_path(os_path: &str) -> Self {
Self { os_path: os_path.to_string(), ..Self::default() }
}

pub fn with_input_path(input_path: &str) -> Self {
Self { input_path: input_path.to_string(), ..Self::default() }
}

pub fn with_block_context(block_context: BlockContext) -> Self {
Self { block_context, ..Self::default() }
}
}

impl Default for SnOsRunner {
Expand Down
12 changes: 12 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use starknet_api::hash::{pedersen_hash, StarkFelt, StarkHash};
use starknet_api::stark_felt;

use crate::config::DEFAULT_COMPILER_VERSION;
use crate::error::SnOsError;

lazy_static! {
static ref RE: Regex = Regex::new(r"^[A-Fa-f0-9]+$").unwrap();
Expand Down Expand Up @@ -60,6 +61,17 @@ pub fn felt_api2vm(felt: StarkFelt) -> Felt252 {
felt_str!(felt.to_string().trim_start_matches("0x"), 16)
}

pub fn felt_vm2usize(felt_op: Option<&Felt252>) -> Result<usize, SnOsError> {
match felt_op {
Some(felt) => {
let big_num: u16 = felt.to_bigint().try_into().map_err(|e| SnOsError::Output(format!("{e}")))?;

Ok(big_num.into())
}
None => Err(SnOsError::CatchAll("no length available".to_string())),
}
}

pub fn deprecated_class_vm2api(class: ContractClassV0) -> DeprecatedContractClass {
DeprecatedContractClass {
abi: None,
Expand Down
Loading