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

Evaluator #120

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
148 changes: 132 additions & 16 deletions src/encoder.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::evm_context;
use crate::evaluator::Evaluator;
use crate::execution_position::ExecutionPositionManager;
use crate::smt::{self, SMTExpr, SMTFormat, SMTSort, SMTStatement, SMTVariable};
use crate::smt::{SMTExpr, SMTFormat, SMTSort, SMTStatement, SMTVariable};
use crate::ssa_tracker::SSATracker;
use crate::{evm_context, smt};

use num_traits::Num;
use yultsur::dialect::{Builtin, Dialect};
Expand Down Expand Up @@ -29,11 +30,48 @@ pub struct Encoder<InstructionsType> {
ssa_tracker: SSATracker,
output: Vec<SMTStatement>,
interpreter: InstructionsType,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want to change this name now.

evaluator: Evaluator,
loop_unroll: u64,
path_conditions: Vec<SMTExpr>,
execution_position: ExecutionPositionManager,
}

/// Data that is needed if you want to use multiple Encoder instances
/// for one SMT query.
#[derive(Default)]
pub struct EncoderSMTState {
pub query: Vec<SMTStatement>,
pub expression_counter: u64,
pub ssa_tracker: SSATracker,
pub evaluator: Evaluator,
pub execution_position: ExecutionPositionManager,
}

impl<T: Instructions> From<EncoderSMTState> for Encoder<T> {
fn from(state: EncoderSMTState) -> Self {
Encoder::<T> {
output: state.query,
expression_counter: state.expression_counter,
evaluator: state.evaluator,
ssa_tracker: state.ssa_tracker,
execution_position: state.execution_position,
..Default::default()
}
}
}

impl<T> From<Encoder<T>> for EncoderSMTState {
fn from(encoder: Encoder<T>) -> Self {
EncoderSMTState {
query: encoder.output,
expression_counter: encoder.expression_counter,
ssa_tracker: encoder.ssa_tracker,
evaluator: encoder.evaluator,
execution_position: encoder.execution_position,
}
}
}

pub fn encode<T: Instructions>(ast: &Block, loop_unroll: u64) -> String {
let mut encoder = Encoder::<T>::default();
encoder.encode(ast, loop_unroll);
Expand All @@ -57,6 +95,32 @@ pub fn encode_revert_reachable<T: Instructions>(
encode_with_counterexamples(&mut encoder, counterexamples)
}

pub fn encode_solc_panic_reachable_with_signatures<T: Instructions>(
ast: &Block,
signatures: Vec<Option<Vec<u8>>>,
loop_unroll: u64,
counterexamples: &[Expression],
) -> (String, Vec<String>, String, ExecutionPositionManager) {
let mut smt_state = EncoderSMTState::default();
for signature in signatures {
// TODO Check if this specifies the exact calldata or just an initial segment.
smt_state.evaluator.new_transaction(signature.clone());
smt_state.execution_position.new_external_call(signature);
// TODO also provide calldata to encoder?
let mut encoder: Encoder<T> = smt_state.into();
encoder.encode(ast, loop_unroll);
smt_state = encoder.into();
}
let mut encoder: Encoder<T> = smt_state.into();
encoder.encode_solc_panic_reachable();
let revert_position = ExecutionPositionManager::smt_variable()
.smt_var(&mut encoder.ssa_tracker)
.as_smt();

let (enc, cex) = encode_with_counterexamples(&mut encoder, counterexamples);
(enc, cex, revert_position, encoder.execution_position)
}

pub fn encode_solc_panic_reachable<T: Instructions>(
ast: &Block,
loop_unroll: u64,
Expand Down Expand Up @@ -139,8 +203,13 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
assert_eq!(function.parameters.len(), arguments.len());
for (param, arg) in function.parameters.iter().zip(arguments) {
let var = self.ssa_tracker.introduce_variable(param);
self.out(smt::define_const(var, smt::SMTExpr::from(arg.clone())))
self.out(smt::define_const(
var.clone(),
smt::SMTExpr::from(arg.clone()),
));
self.evaluator.define_from_variable(&var, arg);
}

self.encode_variable_declaration(&VariableDeclaration {
variables: function.returns.clone(),
value: None,
Expand Down Expand Up @@ -200,7 +269,14 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
fn encode_if(&mut self, expr: &yul::If) {
let cond = self.encode_expression(&expr.condition);
assert!(cond.len() == 1);
if self
.evaluator
.variable_known_equal(&cond[0], &"0".to_string())
{
return;
}
let prev_ssa = self.ssa_tracker.copy_current_ssa();
let prev_eval = self.evaluator.copy_state();

self.push_path_condition(smt::neq(cond[0].clone(), 0));
self.encode_block(&expr.body);
Expand All @@ -209,6 +285,7 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
let output = self
.ssa_tracker
.join_branches(smt::eq(cond[0].clone(), 0), prev_ssa);
self.evaluator.join_with_old_state(prev_eval);
self.out_vec(output);
}

Expand All @@ -221,6 +298,8 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
let cond = self.encode_expression(&for_loop.condition);
assert!(cond.len() == 1);
let prev_ssa = self.ssa_tracker.copy_current_ssa();
let prev_eval = self.evaluator.copy_state();
// TODO the evaluator does not have path conditions - is that OK?

self.push_path_condition(smt::neq(cond[0].clone(), 0));
self.encode_block(&for_loop.body);
Expand All @@ -230,6 +309,7 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
let output = self
.ssa_tracker
.join_branches(smt::eq(cond[0].clone(), 0), prev_ssa);
self.evaluator.join_with_old_state(prev_eval);
self.out_vec(output);
}
}
Expand All @@ -239,6 +319,7 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
assert!(discriminator.len() == 1);
let pre_switch_ssa = self.ssa_tracker.copy_current_ssa();
let mut post_switch_ssa = self.ssa_tracker.take_current_ssa();
let prev_eval = self.evaluator.copy_state();

for Case {
literal,
Expand All @@ -247,6 +328,15 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
} in &switch.cases
{
let is_default = literal.is_none();
// TODO this will always run the default case.
// We should first go through all case labels and see if we only need to execute a single one.
if !is_default
&& self
.evaluator
.variable_known_unequal(&discriminator[0], &literal.as_ref().unwrap().literal)
{
continue;
}

self.ssa_tracker.set_current_ssa(pre_switch_ssa.clone());

Expand All @@ -259,15 +349,17 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
.map(|case| {
smt::eq(
discriminator[0].clone(),
self.encode_literal_value(case.literal.as_ref().unwrap()),
self.encode_literal_value(case.literal.as_ref().unwrap())
.unwrap(),
)
})
.collect::<Vec<_>>(),
)
} else {
smt::neq(
discriminator[0].clone(),
self.encode_literal_value(literal.as_ref().unwrap()),
self.encode_literal_value(literal.as_ref().unwrap())
.unwrap(),
)
};

Expand All @@ -279,9 +371,13 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
.ssa_tracker
.join_branches(skip_condition, post_switch_ssa);
self.out_vec(output);
// TODO check if this is correct.
self.evaluator.join_with_old_state(prev_eval.clone());
post_switch_ssa = self.ssa_tracker.take_current_ssa();
post_switch_ssa.retain(|key, _| pre_switch_ssa.contains_key(key));
}
// TODO we should actually reset thet state of the evaluator, because
// we do not know in which branch we ended up

self.ssa_tracker.set_current_ssa(post_switch_ssa);
}
Expand All @@ -297,10 +393,12 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
fn encode_literal(&mut self, literal: &Literal) -> SMTVariable {
let sort = SMTSort::BV(256);
let var = self.new_temporary_variable(sort);
self.out(smt::define_const(
var.clone(),
self.encode_literal_value(literal),
));
self.evaluator.define_from_literal(&var, literal);
if let Some(value) = self.encode_literal_value(literal) {
self.out(smt::define_const(var.clone(), value));
} else {
self.out(smt::declare_const(var.clone()))
}
var
}

Expand All @@ -309,7 +407,7 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
}

fn encode_function_call(&mut self, call: &FunctionCall) -> Vec<SMTVariable> {
let arguments = call
let arguments: Vec<SMTVariable> = call
.arguments
.iter()
.rev()
Expand Down Expand Up @@ -344,6 +442,15 @@ impl<InstructionsType: Instructions> Encoder<InstructionsType> {
.map(|_i| self.new_temporary_variable(SMTSort::BV(256)))
.collect();

// TODO call evaluator first or interpreter first?
self.evaluator
.builtin_call(builtin, &arguments, &return_vars);
if builtin.name == "call" {
// if let Some((ast, calldata)) = self.evaluator.is_call_to_knon_contract(arguments) {

// // TODO
// }
}
let result = self.interpreter.encode_builtin_call(
builtin,
arguments,
Expand Down Expand Up @@ -394,17 +501,26 @@ impl<T> Encoder<T> {

for (v, val) in variables.iter().zip(values.into_iter()) {
let var = self.ssa_tracker.allocate_new_ssa_index(v);
self.evaluator.define_from_variable(&var, &val);
self.out(smt::define_const(var, val.into()));
}
}

fn encode_literal_value(&self, literal: &Literal) -> SMTExpr {
let parsed = if literal.literal.starts_with("0x") {
num_bigint::BigUint::from_str_radix(&literal.literal[2..], 16).unwrap()
/// Returns the literal value encoded as an SMT expression.
/// Might fail if the literal is a data object reference or something else that
/// does not have a specific 256 bit value.
fn encode_literal_value(&self, literal: &Literal) -> Option<SMTExpr> {
(if let Some(hex) = literal.literal.strip_prefix("0x") {
Some(num_bigint::BigUint::from_str_radix(hex, 16).unwrap())
} else if let Some(string) = literal.literal.strip_prefix('\"') {
assert!(string.len() >= 2 && string.ends_with('\"'));
// This is usually only used for references to data objects,
// so we do not encode it.
None
} else {
literal.literal.parse::<num_bigint::BigUint>().unwrap()
};
smt::literal_32_bytes(parsed)
Some(literal.literal.parse::<num_bigint::BigUint>().unwrap())
})
.map(smt::literal_32_bytes)
}

fn push_path_condition(&mut self, cond: SMTExpr) {
Expand Down
Loading