Skip to content

Commit

Permalink
compiler to nullable (keep-starknet-strange#217)
Browse files Browse the repository at this point in the history
<!-- enter the gh issue after hash -->

- [✅] issue keep-starknet-strange#205 
- [✅] follows contribution
[guide](https://github.com/keep-starknet-strange/shinigami/blob/main/CONTRIBUTING.md)
- [✅] code change includes tests

<!-- PR description below --> 
Updated compiler from `opcodes: Felt252Dict<u8>` to `opcodes:
Felt252Dict<Nullable<u8>>`

Co-authored-by: Brandon Roberts <[email protected]>
  • Loading branch information
stevencartavia and b-j-roberts authored Sep 17, 2024
1 parent 921f3f9 commit b42984e
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 27 deletions.
28 changes: 18 additions & 10 deletions src/compiler.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::utils;
#[derive(Destruct)]
pub struct Compiler {
// Dict containing opcode names to their bytecode representation
opcodes: Felt252Dict<u8>
opcodes: Felt252Dict<Nullable<u8>>
}

pub trait CompilerTrait {
Expand All @@ -15,7 +15,7 @@ pub trait CompilerTrait {
// Adds an opcode "OP_XXX" to the opcodes dict under: "OP_XXX" and "XXX"
fn add_opcode(ref self: Compiler, name: felt252, opcode: u8);
// Compiles a program like "OP_1 OP_2 OP_ADD" into a bytecode run by the Engine.
fn compile(self: Compiler, script: ByteArray) -> ByteArray;
fn compile(self: Compiler, script: ByteArray) -> Result<ByteArray, felt252>;
}

pub impl CompilerImpl of CompilerTrait {
Expand Down Expand Up @@ -311,7 +311,7 @@ pub impl CompilerImpl of CompilerTrait {

fn add_opcode(ref self: Compiler, name: felt252, opcode: u8) {
// Insert opcode formatted like OP_XXX
self.opcodes.insert(name, opcode);
self.opcodes.insert(name, NullableTrait::new(opcode));

// Remove OP_ prefix and insert opcode XXX
let nameu256 = name.into();
Expand All @@ -320,10 +320,10 @@ pub impl CompilerImpl of CompilerTrait {
name_mask = name_mask * 256; // Shift left 1 byte
};
name_mask = name_mask / 16_777_216; // Shift right 3 bytes
self.opcodes.insert((nameu256 % name_mask).try_into().unwrap(), opcode);
self.opcodes.insert((nameu256 % name_mask).try_into().unwrap(), NullableTrait::new(opcode));
}

fn compile(mut self: Compiler, script: ByteArray) -> ByteArray {
fn compile(mut self: Compiler, script: ByteArray) -> Result<ByteArray, felt252> {
let mut bytecode = "";
let seperator = ' ';

Expand Down Expand Up @@ -354,6 +354,7 @@ pub impl CompilerImpl of CompilerTrait {
// Compile the script into bytecode
let mut i = 0;
let script_len = split_script.len();
let mut err = '';
while i != script_len {
let script_item = split_script.at(i);
if utils::is_hex(script_item) {
Expand All @@ -363,13 +364,20 @@ pub impl CompilerImpl of CompilerTrait {
} else if utils::is_number(script_item) {
ByteArrayTrait::append(ref bytecode, @utils::number_to_bytecode(script_item));
} else {
// TODO: Check opcode exists
bytecode
.append_byte(self.opcodes.get(utils::byte_array_to_felt252_be(script_item)));
let opcode_nullable = self
.opcodes
.get(utils::byte_array_to_felt252_be(script_item));
if opcode_nullable.is_null() {
err = 'Compiler error: unknown opcode';
break;
}
bytecode.append_byte(opcode_nullable.deref());
}
i += 1;
};

bytecode
if err != '' {
return Result::Err(err);
}
Result::Ok(bytecode)
}
}
1 change: 1 addition & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub mod transaction;
mod tests {
mod test_coinbase;
mod test_transactions;
mod test_compiler;
mod test_p2pk;
mod test_p2pkh;
}
Expand Down
20 changes: 10 additions & 10 deletions src/main.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ fn run_with_flags(input: InputDataWithFlags) -> Result<(), felt252> {
input.Flags
);
let mut compiler = CompilerImpl::new();
let script_pubkey = compiler.compile(input.ScriptPubKey);
let script_pubkey = compiler.compile(input.ScriptPubKey)?;
let compiler = CompilerImpl::new();
let script_sig = compiler.compile(input.ScriptSig);
let script_sig = compiler.compile(input.ScriptSig)?;
let tx = TransactionImpl::new_signed(script_sig);
let flags = scriptflags::parse_flags(input.Flags);
let mut engine = EngineImpl::new(@script_pubkey, tx, 0, flags, 0)?;
Expand All @@ -55,9 +55,9 @@ fn run_with_witness(input: InputDataWithWitness) -> Result<(), felt252> {
input.Witness
);
let mut compiler = CompilerImpl::new();
let script_pubkey = compiler.compile(input.ScriptPubKey);
let script_pubkey = compiler.compile(input.ScriptPubKey)?;
let compiler = CompilerImpl::new();
let script_sig = compiler.compile(input.ScriptSig);
let script_sig = compiler.compile(input.ScriptSig)?;
let witness = witness::parse_witness_input(input.Witness);
let tx = TransactionImpl::new_signed_witness(script_sig, witness);
let flags = scriptflags::parse_flags(input.Flags);
Expand All @@ -73,9 +73,9 @@ fn run(input: InputData) -> Result<(), felt252> {
input.ScriptPubKey
);
let mut compiler = CompilerImpl::new();
let script_pubkey = compiler.compile(input.ScriptPubKey);
let script_pubkey = compiler.compile(input.ScriptPubKey)?;
let compiler = CompilerImpl::new();
let script_sig = compiler.compile(input.ScriptSig);
let script_sig = compiler.compile(input.ScriptSig)?;
let tx = TransactionImpl::new_signed(script_sig);
let mut engine = EngineImpl::new(@script_pubkey, tx, 0, 0, 0)?;
let _ = engine.execute()?;
Expand All @@ -89,9 +89,9 @@ fn run_with_json(input: InputData) -> Result<(), felt252> {
input.ScriptPubKey
);
let mut compiler = CompilerImpl::new();
let script_pubkey = compiler.compile(input.ScriptPubKey);
let script_pubkey = compiler.compile(input.ScriptPubKey)?;
let compiler = CompilerImpl::new();
let script_sig = compiler.compile(input.ScriptSig);
let script_sig = compiler.compile(input.ScriptSig)?;
let tx = TransactionImpl::new_signed(script_sig);
let mut engine = EngineImpl::new(@script_pubkey, tx, 0, 0, 0)?;
let _ = engine.execute()?;
Expand All @@ -106,9 +106,9 @@ fn debug(input: InputData) -> Result<bool, felt252> {
input.ScriptPubKey
);
let mut compiler = CompilerImpl::new();
let script_pubkey = compiler.compile(input.ScriptPubKey);
let script_pubkey = compiler.compile(input.ScriptPubKey)?;
let compiler = CompilerImpl::new();
let script_sig = compiler.compile(input.ScriptSig);
let script_sig = compiler.compile(input.ScriptSig)?;
let tx = TransactionImpl::new_signed(script_sig);
let mut engine = EngineImpl::new(@script_pubkey, tx, 0, 0, 0)?;
let mut res = Result::Ok(true);
Expand Down
14 changes: 7 additions & 7 deletions src/opcodes/tests/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::transaction::{Transaction, TransactionInput, TransactionOutput, OutPo
// Runs a basic bitcoin script as the script_pubkey with empty script_sig
pub fn test_compile_and_run(program: ByteArray) -> Engine {
let mut compiler = CompilerImpl::new();
let bytecode = compiler.compile(program);
let bytecode = compiler.compile(program).unwrap();
// TODO: Nullable
let mut engine = EngineImpl::new(@bytecode, Default::default(), 0, 0, 0).unwrap();
let res = engine.execute();
Expand All @@ -16,7 +16,7 @@ pub fn test_compile_and_run(program: ByteArray) -> Engine {
// Runs a bitcoin script `program` as script_pubkey with corresponding `transaction`
pub fn test_compile_and_run_with_tx(program: ByteArray, transaction: Transaction) -> Engine {
let mut compiler = CompilerImpl::new();
let mut bytecode = compiler.compile(program);
let mut bytecode = compiler.compile(program).unwrap();
let mut engine = EngineImpl::new(@bytecode, transaction, 0, 0, 0).unwrap();
let res = engine.execute();
assert!(res.is_ok(), "Execution of the program failed");
Expand All @@ -28,7 +28,7 @@ pub fn test_compile_and_run_with_tx_flags(
program: ByteArray, transaction: Transaction, flags: u32
) -> Engine {
let mut compiler = CompilerImpl::new();
let mut bytecode = compiler.compile(program);
let mut bytecode = compiler.compile(program).unwrap();
let mut engine = EngineImpl::new(@bytecode, transaction, 0, flags, 0).unwrap();
let res = engine.execute();
assert!(res.is_ok(), "Execution of the program failed");
Expand All @@ -38,7 +38,7 @@ pub fn test_compile_and_run_with_tx_flags(
// Runs a bitcoin script `program` as script_pubkey with empty script_sig expecting an error
pub fn test_compile_and_run_err(program: ByteArray, expected_err: felt252) -> Engine {
let mut compiler = CompilerImpl::new();
let bytecode = compiler.compile(program);
let bytecode = compiler.compile(program).unwrap();
let mut engine = EngineImpl::new(@bytecode, Default::default(), 0, 0, 0).unwrap();
let res = engine.execute();
assert!(res.is_err(), "Execution of the program did not fail as expected");
Expand All @@ -53,7 +53,7 @@ pub fn test_compile_and_run_with_tx_err(
program: ByteArray, transaction: Transaction, expected_err: felt252
) -> Engine {
let mut compiler = CompilerImpl::new();
let mut bytecode = compiler.compile(program);
let mut bytecode = compiler.compile(program).unwrap();
let mut engine = EngineImpl::new(@bytecode, transaction, 0, 0, 0).unwrap();
let res = engine.execute();
assert!(res.is_err(), "Execution of the program did not fail as expected");
Expand All @@ -68,7 +68,7 @@ pub fn test_compile_and_run_with_tx_flags_err(
program: ByteArray, transaction: Transaction, flags: u32, expected_err: felt252
) -> Engine {
let mut compiler = CompilerImpl::new();
let mut bytecode = compiler.compile(program);
let mut bytecode = compiler.compile(program).unwrap();
let mut engine = EngineImpl::new(@bytecode, transaction, 0, flags, 0).unwrap();
let res = engine.execute();
assert!(res.is_err(), "Execution of the program did not fail as expected");
Expand Down Expand Up @@ -101,7 +101,7 @@ pub fn mock_transaction_input_with(
outpoint: OutPoint, script_sig: ByteArray, witness: Array<ByteArray>, sequence: u32
) -> TransactionInput {
let mut compiler = CompilerImpl::new();
let script_sig = compiler.compile(script_sig);
let script_sig = compiler.compile(script_sig).unwrap();
TransactionInput {
previous_outpoint: outpoint,
signature_script: script_sig,
Expand Down
11 changes: 11 additions & 0 deletions src/tests/test_compiler.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use crate::compiler::CompilerImpl;

// TODO: More tests?

#[test]
fn test_compiler_unknown_opcode() {
let mut compiler = CompilerImpl::new();
let res = compiler.compile("OP_FAKE");
assert!(res.is_err());
assert_eq!(res.unwrap_err(), 'Compiler error: unknown opcode', "Error message mismatch");
}

0 comments on commit b42984e

Please sign in to comment.