def as_paths(trace, path=None): assert type(trace) == list path = path or tuple() # self.find_offsets() trace = replace_f(trace, make_fands) for line in trace: if opcode(line) == "if": # assumes 'ifs' end trace cond, if_true, if_false = line[1], line[2], line[3] return as_paths(if_true, path + (cond,)) + as_paths( if_false, path + (is_zero(cond),) ) if opcode(line) == "LOOP": path += (("LOOP", line[2]),) return as_paths(line[1], path) path += (line,) # pprint_logic() return (list(path),)
def to_while(trace, jd, path=None): path = path or [] while True: if trace == []: raise line = trace[0] trace = trace[1:] if m := match(line, ("if", ":cond", ":if_true", ":if_false")): cond, if_true, if_false = m.cond, m.if_true, m.if_false if is_revert(if_true): path.append(("require", is_zero(cond))) trace = if_false continue if is_revert(if_false): path.append(("require", cond)) trace = if_true continue jds_true = find_f_list(if_true, get_jds) jds_false = find_f_list(if_false, get_jds) assert (jd in jds_true) != (jd in jds_false), (jds_true, jds_false) def add_path(line): if m := match(line, ("goto", Any, ":svs")): path2 = path for _, v_idx, v_val in m.svs: path2 = replace(path2, ("var", v_idx), v_val) return path2 + [line] else: return [line]
def cleanup_ors(path): assert type(path) == list ret = [] idx = 0 while idx < len(path): if type(path[idx]) == list: path = path[:idx] + path[idx] + path[idx + 1 :] line = path[idx] if opcode(line) != "or": ret.append(line) elif len(line) == 2: # one-sided or # clean up the inside, skip the next line in the main path condition = line[1][0] line = ("or", cleanup_ors(line[1])) ret.append(line) idx += 1 elif len(line) == 3: # two-sided or if len(line[1]) == 1: assert comp_bool(simplify_bool(line[1][0]), is_zero(line[2][0])) line = ("or", cleanup_ors(line[2])) ret.append(line) else: assert comp_bool(is_zero(line[1][0]), simplify_bool(line[2][0])) line = ("or", cleanup_ors(line[1]), cleanup_ors(line[2][1:])) ret.append(line) else: # three-sided ors? madness! assert False idx += 1 return ret
def pprint_logic(exp, indent=2): INDENT_LEN = 4 if opcode(exp) == "while": if len(exp) == 5: cond, path, jd, vars = exp[1], exp[2], exp[3], exp[4] else: cond, path = exp[1], exp[2] vars = [] for v in vars: yield " " * indent + list( pretty_line(("setvar", v[1], v[2]), add_color=True))[0] yield " " * indent + COLOR_GREEN + "while " + ENDC + prettify( cond, add_color=True, parentheses=False, rem_bool=True ) + COLOR_GREEN + ":" + ENDC # +COLOR_GREEN+': # '+str(jd)+ENDC if type(path) != list: path = path.trace for l in pprint_logic(path, indent + INDENT_LEN): yield l elif opcode(exp) == "require": _, cond = exp yield " " * indent + "require " + prettify( exp[1], add_color=True, parentheses=False, rem_bool=True) + "" elif m := match( exp, ("if", ":cond", ":if_true")): # one-sided ifs, only after folding cond, if_true = m.cond, m.if_true if (len(if_true) == 1 and (first := if_true[0]) and ((first == ("revert", 0)) or opcode(first) == "invalid")): yield " " * indent + "require " + prettify(is_zero(exp[1]), add_color=True, parentheses=False, rem_bool=True)
path.append(("require", cond)) trace = if_true continue jds_true = find_f_list(if_true, get_jds) jds_false = find_f_list(if_false, get_jds) assert (jd in jds_true) != (jd in jds_false), (jds_true, jds_false) def add_path(line): if m := match(line, ("goto", Any, ":svs")): path2 = path for _, v_idx, v_val in m.svs: path2 = replace(path2, ("var", v_idx), v_val) return path2 + [line] else: return [line] if jd in jds_true: if_true = rewrite_trace(if_true, add_path) return path, if_true, if_false, cond else: if_false = rewrite_trace(if_false, add_path) return path, if_false, if_true, is_zero(cond) else: path.append(line) assert False, f"no if after label?{jd}"
def handle_jumps(self, trace, line, condition): i, op = line[0], line[1] stack = self.stack if "--explain" in sys.argv and op in ( "jump", "jumpi", "selfdestruct", "stop", "return", "invalid", "assert_fail", "revert", ): trace.append(C.asm(f" {stack}")) trace.append("") trace.append(f"[{line[0]}] {C.asm(op)}") if op in ( "jump", "jumpi", "selfdestruct", "stop", "return", "invalid", "assert_fail", "revert", ): logger.debug("[%s] %s", i, op) if op == "jump": target = stack.pop() n = Node( self, start=target, safe=False, stack=tuple(self.stack.stack), condition=condition, ) trace.append(("jump", n)) return trace elif op == "jumpi": target = stack.pop() if_condition = simplify_bool(stack.pop()) tuple_stack = tuple(self.stack.stack) n_true = Node( self, start=target, safe=False, stack=tuple_stack, condition=if_condition, ) n_false = Node( self, start=self.loader.next_line(i), safe=True, stack=tuple_stack, condition=is_zero(if_condition), ) if self.just_fdests: if ( (m := match(if_condition, ("eq", ":fx_hash", ":is_cd"))) and str(("cd", 0)) in str(m.is_cd) and isinstance(m.fx_hash, int) ): n_true.trace = [("funccall", m.fx_hash, target, tuple_stack)] if ( (m := match(if_condition, ("eq", ":is_cd", ":fx_hash"))) and str(("cd", 0)) in str(m.is_cd) and isinstance(m.fx_hash, int) ): n_true.trace = [("funccall", m.fx_hash, target, tuple_stack)]
for l in pprint_logic(if_true, indent + INDENT_LEN): yield l elif m := match(exp, ("if", ":cond", ":if_true", ":if_false")): cond, if_true, if_false = m.cond, m.if_true, m.if_false if (len(if_false) == 1 and (first := if_false[0]) and (first == ("revert", 0) or opcode(first) == "invalid")): yield " " * indent + "require " + prettify( exp[1], add_color=True, parentheses=False, rem_bool=True) for l in pprint_logic(exp[2], indent): yield l elif (len(if_true) == 1 and (first := if_true[0]) and ((first == ("revert", 0)) or opcode(first) == "invalid")): yield " " * indent + "require " + prettify(is_zero(exp[1]), add_color=True, parentheses=False, rem_bool=True) for l in pprint_logic(exp[3], indent): yield l else: yield " " * indent + "if " + prettify( exp[1], add_color=True, parentheses=False, rem_bool=True) + ":" for l in pprint_logic(if_true, indent + INDENT_LEN): yield l """ while len(if_false) == 1 and opcode(if_false) == 'if' and len(if_false) == 4: