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

feat: add printing hints #1476

Merged
merged 15 commits into from
Nov 23, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#### Upcoming Changes

* feat: add debugging capabilities behind `print` feature flag. [#1476](https://github.com/lambdaclass/cairo-vm/pull/1476)

* chore: bump starknet-crypto to v0.6.1 [#1469](https://github.com/lambdaclass/cairo-vm/pull/1469)

* feat: Implement the Serialize and Deserialize methods for the Program struct [#1458](https://github.com/lambdaclass/cairo-vm/pull/1458)
Expand Down
20 changes: 20 additions & 0 deletions cairo_programs/print_array.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
%builtins range_check

from starkware.cairo.common.alloc import alloc

func main{range_check_ptr: felt}() {
let name = 0x4b4b5254;
let (arr: felt*) = alloc();
assert arr[0] = 1;
assert arr[1] = 2;
assert arr[2] = 3;
assert arr[3] = 4;
assert arr[4] = 5;
let arr_len = 5;
%{
print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00',''))
arr = [memory[ids.arr + i] for i in range(ids.arr_len)]
print(arr)
%}
return();
}
34 changes: 34 additions & 0 deletions cairo_programs/print_dict_array.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
%builtins range_check

from starkware.cairo.common.dict_access import DictAccess
from starkware.cairo.common.default_dict import default_dict_new, default_dict_finalize
from starkware.cairo.common.dict import dict_write

struct MyStruct {
a: felt,
b: felt,
c: felt,
}

func main{range_check_ptr: felt}() {
let name = 0x4b4b5254;
let (dict_ptr) = default_dict_new(0);
let pointer_size = 3;

tempvar one = new MyStruct(1,2,3);
dict_write{dict_ptr=dict_ptr}(0, cast(one, felt));
tempvar two = new MyStruct(2,3,4);
dict_write{dict_ptr=dict_ptr}(1, cast(two, felt));
tempvar three = new MyStruct(3,4,5);
dict_write{dict_ptr=dict_ptr}(2, cast(three, felt));
tempvar four = new MyStruct(4,5,6);
dict_write{dict_ptr=dict_ptr}(3, cast(four, felt));
%{
print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00',''))
data = __dict_manager.get_dict(ids.dict_ptr)
print(
{k: v if isinstance(v, int) else [memory[v + i] for i in range(ids.pointer_size)] for k, v in data.items()}
)
%}
return();
}
24 changes: 24 additions & 0 deletions cairo_programs/print_dict_felt.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
%builtins range_check

from starkware.cairo.common.dict_access import DictAccess
from starkware.cairo.common.default_dict import default_dict_new, default_dict_finalize
from starkware.cairo.common.dict import dict_write

func main{range_check_ptr: felt}() {
let name = 0x4b4b5254;
let (dict_ptr) = default_dict_new(0);
let pointer_size = 1;
dict_write{dict_ptr=dict_ptr}(0, 1);
dict_write{dict_ptr=dict_ptr}(1, 2);
dict_write{dict_ptr=dict_ptr}(2, 3);
dict_write{dict_ptr=dict_ptr}(3, 4);
dict_write{dict_ptr=dict_ptr}(4, 5);
%{
print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00',''))
data = __dict_manager.get_dict(ids.dict_ptr)
print(
{k: v if isinstance(v, int) else [memory[v + i] for i in range(ids.pointer_size)] for k, v in data.items()}
)
%}
return();
}
9 changes: 9 additions & 0 deletions cairo_programs/print_felt.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
%builtins range_check

func main{range_check_ptr: felt}() {
let x = 123;
%{
print(ids.x)
%}
return();
}
2 changes: 2 additions & 0 deletions vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ lambdaworks-felt = ["felt/lambdaworks-felt"]
test_utils = [
"skip_next_instruction_hint",
"hooks",
"print",
] # This feature will reference every test-oriented feature
skip_next_instruction_hint = []
hooks = []
print = []

[dependencies]
mimalloc = { workspace = true, optional = true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ use felt::Felt252;
#[cfg(feature = "skip_next_instruction_hint")]
use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction;

#[cfg(feature = "print")]
use crate::hint_processor::builtin_hint_processor::print::{print_array, print_dict, print_felt};

use super::blake2s_utils::example_blake2s_compress;

pub struct HintProcessorData {
Expand Down Expand Up @@ -815,6 +818,14 @@ impl HintProcessorLogic for BuiltinHintProcessor {
hint_code::SPLIT_XX => split_xx(vm, &hint_data.ids_data, &hint_data.ap_tracking),
#[cfg(feature = "skip_next_instruction_hint")]
hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm),
#[cfg(feature = "print")]
hint_code::PRINT_FELT => print_felt(vm, &hint_data.ids_data, &hint_data.ap_tracking),
#[cfg(feature = "print")]
hint_code::PRINT_ARR => print_array(vm, &hint_data.ids_data, &hint_data.ap_tracking),
#[cfg(feature = "print")]
hint_code::PRINT_DICT => {
print_dict(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking)
}
code => Err(HintError::UnknownHint(code.to_string().into_boxed_str())),
}
}
Expand Down
12 changes: 12 additions & 0 deletions vm/src/hint_processor/builtin_hint_processor/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1409,3 +1409,15 @@ ids.x.low = x & ((1<<128)-1)
ids.x.high = x >> 128";
#[cfg(feature = "skip_next_instruction_hint")]
pub const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()";

pub const PRINT_FELT: &str = "print(ids.x)";

pub const PRINT_ARR: &str = r#"print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00',''))
arr = [memory[ids.arr + i] for i in range(ids.arr_len)]
print(arr)"#;

pub const PRINT_DICT: &str = r#"print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00',''))
data = __dict_manager.get_dict(ids.dict_ptr)
print(
{k: v if isinstance(v, int) else [memory[v + i] for i in range(ids.pointer_size)] for k, v in data.items()}
)"#;
2 changes: 2 additions & 0 deletions vm/src/hint_processor/builtin_hint_processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub mod memcpy_hint_utils;
pub mod memset_utils;
pub mod poseidon_utils;
pub mod pow_utils;
#[cfg(feature = "print")]
pub mod print;
pub mod secp;
pub mod segments;
pub mod set;
Expand Down
124 changes: 124 additions & 0 deletions vm/src/hint_processor/builtin_hint_processor/print.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use core::fmt::{Debug, Formatter};

use felt::Felt252;
use num_traits::ToPrimitive;

use crate::hint_processor::builtin_hint_processor::dict_manager::Dictionary;
use crate::hint_processor::builtin_hint_processor::hint_utils::{
get_integer_from_var_name, get_ptr_from_var_name,
};
use crate::serde::deserialize_program::ApTracking;
use crate::stdlib::collections::HashMap;

use crate::types::exec_scope::ExecutionScopes;
use crate::types::relocatable::MaybeRelocatable;
use crate::vm::errors::hint_errors::HintError;
use crate::{
hint_processor::hint_processor_definition::HintReference, vm::vm_core::VirtualMachine,
};

pub fn print_felt(
vm: &VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let val = get_integer_from_var_name("x", vm, ids_data, ap_tracking)?;
println!("{val}");
Ok(())
}

fn print_name(
vm: &VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let name = get_integer_from_var_name("name", vm, ids_data, ap_tracking)?;
let name = String::from_utf8(name.to_bigint().to_signed_bytes_be())
.map_err(|err| HintError::CustomHint(err.to_string().into_boxed_str()))?;
println!("{name}");
Ok(())
}

pub fn print_array(
vm: &VirtualMachine,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
print_name(vm, ids_data, ap_tracking)?;

let mut acc = Vec::new();
let arr = get_ptr_from_var_name("arr", vm, ids_data, ap_tracking)?;
let arr_len = get_integer_from_var_name("arr_len", vm, ids_data, ap_tracking)?;
let arr_len = arr_len.to_usize().ok_or_else(|| {
HintError::CustomHint(String::from("arr_len must be a positive integer").into_boxed_str())
})?;
for i in 0..arr_len {
let val = vm.get_integer((arr + i)?)?;
acc.push(val);
}
println!("{:?}", acc);
Ok(())
}

enum DictValue {
Int(Felt252),
Relocatable(Vec<Felt252>),
}

impl Debug for DictValue {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::Int(int) => write!(f, "{:?}", int),
Self::Relocatable(relocatable) => write!(f, "{:?}", relocatable),
}
}
}

pub fn print_dict(
vm: &VirtualMachine,
exec_scopes: &ExecutionScopes,
ids_data: &HashMap<String, HintReference>,
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
print_name(vm, ids_data, ap_tracking)?;

let dict_ptr = get_ptr_from_var_name("dict_ptr", vm, ids_data, ap_tracking)?;
let pointer_size = get_integer_from_var_name("pointer_size", vm, ids_data, ap_tracking)?;
let pointer_size = pointer_size.to_usize().ok_or_else(|| {
HintError::CustomHint(
String::from("pointer_size must be a positive integer").into_boxed_str(),
)
})?;

let dict_manager = exec_scopes.get_dict_manager()?;
let dict_manager = dict_manager.borrow();
let tracker = dict_manager.get_tracker(dict_ptr)?;

let map = match &tracker.data {
Dictionary::SimpleDictionary(dict) => dict,
Dictionary::DefaultDictionary { dict, .. } => dict,
};

let mut acc = HashMap::new();
for (k, v) in map.iter() {
let key = k.get_int_ref().ok_or_else(|| {
HintError::CustomHint(String::from("Expected felt key for dict").into_boxed_str())
})?;
match v {
MaybeRelocatable::Int(value) => {
acc.insert(key, DictValue::Int(value.clone()));
}
MaybeRelocatable::RelocatableValue(val) => {
let mut structure = Vec::new();
for i in 0..pointer_size {
let val = vm.get_integer((*val + i)?)?.as_ref().clone();
structure.push(val);
}
acc.insert(key, DictValue::Relocatable(structure));
}
}
}

println!("{:?}", acc);
Ok(())
}
23 changes: 23 additions & 0 deletions vm/src/tests/cairo_run_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,3 +1032,26 @@ fn divmod_igcdex_not_one() {
let error_msg = "Operation failed: divmod(1, 340282366920938463463374607431768211457, 340282366920938463463374607431768211457), igcdex(340282366920938463463374607431768211457, 340282366920938463463374607431768211457) != 1";
run_program_with_error(program_data.as_slice(), error_msg);
}

fn cairo_run_print_felt() {
Oppen marked this conversation as resolved.
Show resolved Hide resolved
let program_data = include_bytes!("../../../cairo_programs/print_felt.json");
run_program_simple(program_data);
}

#[test]
fn cairo_run_print_array() {
let program_data = include_bytes!("../../../cairo_programs/print_array.json");
run_program_simple(program_data);
}

#[test]
fn cairo_run_print_dict_felt() {
let program_data = include_bytes!("../../../cairo_programs/print_dict_felt.json");
run_program_simple_with_memory_holes(program_data, 5);
}

#[test]
fn cairo_run_print_dict_array() {
let program_data = include_bytes!("../../../cairo_programs/print_dict_array.json");
run_program_simple_with_memory_holes(program_data, 4);
}
Oppen marked this conversation as resolved.
Show resolved Hide resolved
Loading