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

Implement basic cheat codes. #28472

Merged
merged 4 commits into from
Dec 6, 2024
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ version = "2.4.0"

[workspace.dependencies.leo-interpreter]
path = "./interpreter"
version = "2.3.0"
version = "2.4.0"

[workspace.dependencies.leo-package]
path = "./leo/package"
Expand Down
13 changes: 12 additions & 1 deletion compiler/ast/src/functions/core_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ pub enum CoreFunction {

SignatureVerify,
FutureAwait,

CheatCodePrintMapping,
CheatCodeSetBlockHeight,
}

impl CoreFunction {
Expand Down Expand Up @@ -566,6 +569,9 @@ impl CoreFunction {

(sym::signature, sym::verify) => Self::SignatureVerify,
(sym::Future, sym::Await) => Self::FutureAwait,

(sym::CheatCode, sym::print_mapping) => Self::CheatCodePrintMapping,
(sym::CheatCode, sym::set_block_height) => Self::CheatCodeSetBlockHeight,
_ => return None,
})
}
Expand Down Expand Up @@ -844,6 +850,9 @@ impl CoreFunction {

Self::SignatureVerify => 3,
Self::FutureAwait => 1,

Self::CheatCodePrintMapping => 1,
Self::CheatCodeSetBlockHeight => 1,
}
}

Expand Down Expand Up @@ -1101,7 +1110,9 @@ impl CoreFunction {
| CoreFunction::SHA3_512HashToScalar
| CoreFunction::GroupToXCoordinate
| CoreFunction::GroupToYCoordinate
| CoreFunction::SignatureVerify => false,
| CoreFunction::SignatureVerify
| CoreFunction::CheatCodePrintMapping
| CoreFunction::CheatCodeSetBlockHeight => false,
}
}
}
4 changes: 4 additions & 0 deletions compiler/passes/src/code_generation/visit_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ impl<'a> CodeGenerator<'a> {
writeln!(instruction, " {};", arguments[0]).expect("failed to write to string");
(String::new(), instruction)
}
sym::CheatCode => {
(String::new(), String::new())
// Do nothing. Cheat codes do not generate instructions.
}
_ => {
unreachable!("All core functions should be known at this phase of compilation")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ impl ExpressionReconstructor for DeadCodeEliminator<'_> {

/// Reconstructs the associated function access expression.
fn reconstruct_associated_function(&mut self, input: AssociatedFunction) -> (Expression, Self::AdditionalOutput) {
// If the associated function manipulates a mapping, mark the statement as necessary.
// If the associated function manipulates a mapping, or a cheat code, mark the statement as necessary.
match (&input.variant.name, input.name.name) {
(&sym::Mapping, sym::remove) | (&sym::Mapping, sym::set) | (&sym::Future, sym::Await) => {
(&sym::Mapping, sym::remove)
| (&sym::Mapping, sym::set)
| (&sym::Future, sym::Await)
| (&sym::CheatCode, _) => {
self.is_necessary = true;
}
_ => {}
Expand Down
10 changes: 10 additions & 0 deletions compiler/passes/src/type_checking/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,16 @@ impl<'a, N: Network> TypeChecker<'a, N> {
Some(Type::Boolean)
}
CoreFunction::FutureAwait => Some(Type::Unit),
CoreFunction::CheatCodePrintMapping => {
// Check that the argument is a mapping.
let _ = self.assert_mapping_type(&arguments[0].0, arguments[0].1);
Some(Type::Unit)
}
CoreFunction::CheatCodeSetBlockHeight => {
// Assert that the argument is a u32.
self.assert_type(&arguments[0].0, &Type::Integer(IntegerType::U32), arguments[0].1);
Some(Type::Unit)
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions compiler/span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ symbols! {
verify,
Await: "await",

// CheatCodes
CheatCode,
print_mapping,
set_block_height,

// types
address,
bool,
Expand Down
2 changes: 1 addition & 1 deletion interpreter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "leo-interpreter"
version = "2.3.1"
version = "2.4.0"
authors = [ "The Leo Team <[email protected]>" ]
description = "Interpreter for the Leo programming language"
homepage = "https://leo-lang.org"
Expand Down
36 changes: 36 additions & 0 deletions interpreter/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ impl<'a> Cursor<'a> {
}
}

fn set_block_height(&mut self, block_height: u32) {
self.block_height = block_height;
}

/// Execute the whole step of the current Element.
///
/// That is, perform a step, and then finish all statements and expressions that have been pushed,
Expand Down Expand Up @@ -710,6 +714,9 @@ impl<'a> Cursor<'a> {
push!()(&function.arguments[2]);
push!()(&function.arguments[1]);
}
CoreFunction::CheatCodePrintMapping => {
// Do nothing, as we don't need to evaluate the mapping.
}
_ => function.arguments.iter().rev().for_each(push!()),
}
None
Expand Down Expand Up @@ -2158,6 +2165,35 @@ impl<'a> Cursor<'a> {
}
Value::Unit
}
CoreFunction::CheatCodePrintMapping => {
let (program, name) = match &arguments[0] {
Expression::Identifier(id) => (None, id.name),
Expression::Locator(locator) => (Some(locator.program.name.name), locator.name),
_ => tc_fail!(),
};
if let Some(mapping) = self.lookup_mapping(program, name) {
// TODO: What is the appropriate way to print this to the console.
// Print the name of the mapping.
println!(
"Mapping: {}",
if let Some(program) = program { format!("{}/{}", program, name) } else { name.to_string() }
);
// Print the contents of the mapping.
for (key, value) in mapping {
println!(" {} -> {}", key, value);
}
} else {
tc_fail!();
}
Value::Unit
}
CoreFunction::CheatCodeSetBlockHeight => {
let Value::U32(height) = self.pop_value()? else {
tc_fail!();
};
self.set_block_height(height);
Value::Unit
}
};

Ok(value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace = "Compile"
expectation = "Fail"
outputs = ["""
Error [ETYC0372005]: Unknown variable `Yo`
--> compiler-test:13:34
|
13 | CheatCode::print_mapping(Yo);
| ^^
Error [ETYC0372005]: Unknown variable `account`
--> compiler-test:14:34
|
14 | CheatCode::print_mapping(test_dep.aleo/account);
| ^^^^^^^^^^^^^^^^^^^^^
Error [ETYC0372003]: Expected type `u32` but type `u64` was found
--> compiler-test:15:37
|
15 | CheatCode::set_block_height(1u64);
| ^^^^
Error [ETYC0372003]: Expected type `u32` but type `u64` was found
--> compiler-test:16:37
|
16 | CheatCode::set_block_height(a);
| ^
"""]
44 changes: 44 additions & 0 deletions tests/expectations/compiler/core/cheatcodes/valid_cheatcodes.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace = "Compile"
expectation = "Pass"
outputs = [[{ compile = [
{ initial_symbol_table = "0fbe7b86610386bfb1c7f0f211b2043baae706b9195008f8553511968f9297e7", type_checked_symbol_table = "efc3324af11b2f3645010266f1a871d799b81b07bec594fa88402b3f6fe1330b", unrolled_symbol_table = "efc3324af11b2f3645010266f1a871d799b81b07bec594fa88402b3f6fe1330b", initial_ast = "472f984ad224e345de6a6a8cb7c4986b0bf8fa288713c38a506b41bad280faa5", unrolled_ast = "472f984ad224e345de6a6a8cb7c4986b0bf8fa288713c38a506b41bad280faa5", ssa_ast = "ff6501ea72e6a46b15d71a89b71181851fba9aa2e6ee2a36d70218ad1a089a68", flattened_ast = "ba4154876562575fc3f8b6106a3ed4ab331382a4538ebc9630c82ed9be48176b", destructured_ast = "a995365c129f150bc361a571e5a0810f014a62c170d39e904b7de473bcdac50f", inlined_ast = "3a2f11285208b9bd75048be921a23504d9389ae81e2bdc96f631943cfa4349c6", dce_ast = "ed19a1a5455d89e6a59914923e69d600b0fde7fa91cae652d70756eb59365e03", bytecode = """
program test_dep.aleo;

record yeets:
owner as address.private;
val as u32.private;

mapping Yo:
key as u32.public;
value as u32.public;

function main_dep:
input r0 as u32.private;
async main_dep r0 1u32 into r1;
cast self.caller 1u32 into r2 as yeets.record;
output r2 as yeets.record;
output r1 as test_dep.aleo/main_dep.future;

finalize main_dep:
input r0 as u32.public;
input r1 as u32.public;
set r1 into Yo[r0];
""", errors = "", warnings = "" },
{ initial_symbol_table = "1ff3afb19b60e93b29bcf302b325d787717fc9f72dc76ebf0e2f16a88c61f8e1", type_checked_symbol_table = "34fca725cd812896570be3b52571fda6af6ae081e686f1414d3c356e3a96f568", unrolled_symbol_table = "34fca725cd812896570be3b52571fda6af6ae081e686f1414d3c356e3a96f568", initial_ast = "308e8bf01c3d58f3833b54d7bd297cc34a47754b723fdb727b06aafd88c7322c", unrolled_ast = "cbebc3742af33ad850a585eb37f0e50dd4182317f89bf229083826d3a41a7719", ssa_ast = "c29e889749cbd936b56a07f85d7fa1cc932901ef0b89c5d9f81badf262122286", flattened_ast = "37eb161a0cfc90d08f16ea37e2a815476b11e7b03adf57361c97217807a49e58", destructured_ast = "36ad597d27bc588495a6168d7fabfd8750b8efab765f39992851530b48e04712", inlined_ast = "0152ae3ca21c99c5c59eb80d72832fcd8cb830f13ab4dfab8137af9dfaa5e43e", dce_ast = "0152ae3ca21c99c5c59eb80d72832fcd8cb830f13ab4dfab8137af9dfaa5e43e", bytecode = """
import test_dep.aleo;
program test.aleo;

mapping account:
key as address.public;
value as u64.public;

function main:
input r0 as u32.public;
async main r0 into r1;
output r1 as test.aleo/main.future;

finalize main:
input r0 as u32.public;
assert.eq true true;
""", errors = "", warnings = "" },
] }]]
43 changes: 43 additions & 0 deletions tests/tests/compiler/core/cheatcodes/invalid_cheatcodes_fail.leo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
namespace = "Compile"
expectation = "Fail"
*/

// The 'test_dep' program.
program test_dep.aleo {
mapping Yo: u32 => u32;
record yeets {
owner: address,
val: u32,
}

async transition main_dep(a:u32) -> (yeets, Future) {
let f: Future = finalize_main_dep(a, 1u32);
let l: yeets = yeets {owner: self.caller, val: 1u32};
return (l, f);
}

async function finalize_main_dep(a:u32, b:u32) {
Mapping::set(Yo, a, b);
let c:u32 = a + b;
}
}

// --- Next Program --- //

import test_dep.aleo;

program test.aleo {
mapping account: address => u64;

async transition main(public a: u64) -> Future {
return finish(a);
}

async function finish(public a: u64) {
CheatCode::print_mapping(Yo);
CheatCode::print_mapping(test_dep.aleo/account);
CheatCode::set_block_height(1u64);
CheatCode::set_block_height(a);
}
}
43 changes: 43 additions & 0 deletions tests/tests/compiler/core/cheatcodes/valid_cheatcodes.leo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
namespace = "Compile"
expectation = "Pass"
*/

// The 'test_dep' program.
program test_dep.aleo {
mapping Yo: u32 => u32;
record yeets {
owner: address,
val: u32,
}

async transition main_dep(a:u32) -> (yeets, Future) {
let f: Future = finalize_main_dep(a, 1u32);
let l: yeets = yeets {owner: self.caller, val: 1u32};
return (l, f);
}

async function finalize_main_dep(a:u32, b:u32) {
Mapping::set(Yo, a, b);
let c:u32 = a + b;
}
}

// --- Next Program --- //

import test_dep.aleo;

program test.aleo {
mapping account: address => u64;

async transition main(public a: u32) -> Future {
return finish(a);
}

async function finish(public a: u32) {
CheatCode::print_mapping(account);
CheatCode::print_mapping(test_dep.aleo/Yo);
CheatCode::set_block_height(1u32);
CheatCode::set_block_height(a);
}
}
Loading