mirror of https://github.com/ConsenSys/mythril
parent
a62ba2470d
commit
f14b21e16d
@ -0,0 +1,74 @@ |
|||||||
|
gascost = { |
||||||
|
'PUSH': 3, |
||||||
|
'DUP': 3, |
||||||
|
'SWAP': 3, |
||||||
|
'STOP': 0, |
||||||
|
'ADD': 3, |
||||||
|
'MUL': 5, |
||||||
|
'SUB': 3, |
||||||
|
'DIV': 5, |
||||||
|
'SDIV': 5, |
||||||
|
'MOD': 5, |
||||||
|
'SMOD': 5, |
||||||
|
'ADDMOD': 8, |
||||||
|
'MULMOD': 8, |
||||||
|
'EXP': 10, |
||||||
|
'SIGNEXTEND': 5, |
||||||
|
'LT': 3, |
||||||
|
'GT': 3, |
||||||
|
'SLT': 3, |
||||||
|
'SGT': 3, |
||||||
|
'EQ': 3, |
||||||
|
'ISZERO': 3, |
||||||
|
'AND': 3, |
||||||
|
'OR': 3, |
||||||
|
'XOR': 3, |
||||||
|
'NOT': 3, |
||||||
|
'BYTE': 3, |
||||||
|
'SHA3': 30, |
||||||
|
'ADDRESS': 2, |
||||||
|
'BALANCE': 400, |
||||||
|
'ORIGIN': 2, |
||||||
|
'CALLER': 2, |
||||||
|
'CALLVALUE': 2, |
||||||
|
'CALLDATALOAD': 3, |
||||||
|
'CALLDATASIZE': 2, |
||||||
|
'CALLDATACOPY': 3, |
||||||
|
'CODESIZE': 2, |
||||||
|
'CODECOPY': 3, |
||||||
|
'GASPRICE': 2, |
||||||
|
'EXTCODESIZE': 700, |
||||||
|
'EXTCODECOPY': 700, |
||||||
|
'BLOCKHASH': 20, |
||||||
|
'COINBASE': 2, |
||||||
|
'TIMESTAMP': 2, |
||||||
|
'NUMBER': 2, |
||||||
|
'DIFFICULTY': 2, |
||||||
|
'GASLIMIT': 2, |
||||||
|
'POP': 2, |
||||||
|
'MLOAD': 3, |
||||||
|
'MSTORE': 3, |
||||||
|
'MSTORE8': 3, |
||||||
|
'SLOAD': 50, |
||||||
|
'SSTORE': 0, |
||||||
|
'JUMP': 8, |
||||||
|
'JUMPI': 10, |
||||||
|
'PC': 2, |
||||||
|
'MSIZE': 2, |
||||||
|
'GAS': 2, |
||||||
|
'JUMPDEST': 1, |
||||||
|
'LOG0': 375, |
||||||
|
'LOG1': 750, |
||||||
|
'LOG2': 1125, |
||||||
|
'LOG3': 1500, |
||||||
|
'LOG4': 1875, |
||||||
|
'CREATE': 32000, |
||||||
|
'CALL': 40, |
||||||
|
'CALLCODE': 40, |
||||||
|
'RETURN': 0, |
||||||
|
'DELEGATECALL': 40, |
||||||
|
'CALLBLACKBOX': 40, |
||||||
|
'STATICCALL': 40, |
||||||
|
'REVERT': 0, |
||||||
|
'SUICIDE': 5000, |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
import re |
||||||
|
from z3 import * |
||||||
|
import logging |
||||||
|
|
||||||
|
TT256 = 2 ** 256 |
||||||
|
TT256M1 = 2 ** 256 - 1 |
||||||
|
TT255 = 2 ** 255 |
||||||
|
|
||||||
|
|
||||||
|
def safe_decode(hex_encoded_string): |
||||||
|
|
||||||
|
if (hex_encoded_string.startswith("0x")): |
||||||
|
return bytes.fromhex(hex_encoded_string[2:]) |
||||||
|
else: |
||||||
|
return bytes.fromhex(hex_encoded_string) |
||||||
|
|
||||||
|
|
||||||
|
def to_signed(i): |
||||||
|
return i if i < TT255 else i - TT256 |
||||||
|
|
||||||
|
|
||||||
|
def get_instruction_index(instruction_list, address): |
||||||
|
|
||||||
|
index = 0 |
||||||
|
|
||||||
|
for instr in instruction_list: |
||||||
|
if instr['address'] == address: |
||||||
|
return index |
||||||
|
|
||||||
|
index += 1 |
||||||
|
|
||||||
|
return None |
||||||
|
|
||||||
|
|
||||||
|
def get_trace_line(instr, state): |
||||||
|
|
||||||
|
stack = str(state.stack[::-1]) |
||||||
|
|
||||||
|
# stack = re.sub("(\d+)", lambda m: hex(int(m.group(1))), stack) |
||||||
|
stack = re.sub("\n", "", stack) |
||||||
|
|
||||||
|
return str(instr['address']) + " " + instr['opcode'] + "\tSTACK: " + stack |
||||||
|
|
||||||
|
|
||||||
|
def pop_bitvec(state): |
||||||
|
# pop one element from stack, converting boolean expressions and |
||||||
|
# concrete Python variables to BitVecVal |
||||||
|
|
||||||
|
item = state.stack.pop() |
||||||
|
|
||||||
|
if type(item) == BoolRef: |
||||||
|
return If(item, BitVecVal(1, 256), BitVecVal(0, 256)) |
||||||
|
elif type(item) == bool: |
||||||
|
if item: |
||||||
|
return BitVecVal(1, 256) |
||||||
|
else: |
||||||
|
return BitVecVal(0, 256) |
||||||
|
elif type(item) == int: |
||||||
|
return BitVecVal(item, 256) |
||||||
|
else: |
||||||
|
return simplify(item) |
||||||
|
|
||||||
|
|
||||||
|
def get_concrete_int(item): |
||||||
|
|
||||||
|
if (type(item) == int): |
||||||
|
return item |
||||||
|
|
||||||
|
if (type(item) == BitVecNumRef): |
||||||
|
return item.as_long() |
||||||
|
|
||||||
|
return simplify(item).as_long() |
||||||
|
|
||||||
|
|
||||||
|
def concrete_int_from_bytes(_bytes, start_index): |
||||||
|
|
||||||
|
# logging.debug("-- concrete_int_from_bytes: " + str(_bytes[start_index:start_index+32])) |
||||||
|
b = _bytes[start_index:start_index+32] |
||||||
|
|
||||||
|
val = int.from_bytes(b, byteorder='big') |
||||||
|
|
||||||
|
return val |
||||||
|
|
||||||
|
|
||||||
|
def concrete_int_to_bytes(val): |
||||||
|
|
||||||
|
# logging.debug("concrete_int_to_bytes " + str(val)) |
||||||
|
|
||||||
|
if (type(val) == int): |
||||||
|
return val.to_bytes(32, byteorder='big') |
||||||
|
|
||||||
|
return (simplify(val).as_long()).to_bytes(32, byteorder='big') |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,309 @@ |
|||||||
|
import logging, copy |
||||||
|
import mythril.laser.ethereum.helper as helper |
||||||
|
|
||||||
|
|
||||||
|
class TaintRecord: |
||||||
|
""" |
||||||
|
TaintRecord contains tainting information for a specific (state, node) |
||||||
|
the information specifies the taint status before executing the operation belonging to the state |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
""" Builds a taint record """ |
||||||
|
self.stack = [] |
||||||
|
self.memory = {} |
||||||
|
self.storage = {} |
||||||
|
self.states = [] |
||||||
|
|
||||||
|
def stack_tainted(self, index): |
||||||
|
""" Returns taint value of stack element at index""" |
||||||
|
if index < len(self.stack): |
||||||
|
return self.stack[index] |
||||||
|
return None |
||||||
|
|
||||||
|
def memory_tainted(self, index): |
||||||
|
""" Returns taint value of memory element at index""" |
||||||
|
if index in self.memory.keys(): |
||||||
|
return self.memory[index] |
||||||
|
return False |
||||||
|
|
||||||
|
def storage_tainted(self, index): |
||||||
|
""" Returns taint value of storage element at index""" |
||||||
|
if index in self.storage.keys(): |
||||||
|
return self.storage[index] |
||||||
|
return False |
||||||
|
|
||||||
|
def add_state(self, state): |
||||||
|
""" Adds state with this taint record """ |
||||||
|
self.states.append(state) |
||||||
|
|
||||||
|
def clone(self): |
||||||
|
""" Clones this record""" |
||||||
|
clone = TaintRecord() |
||||||
|
clone.stack = copy.deepcopy(self.stack) |
||||||
|
clone.memory = copy.deepcopy(self.memory) |
||||||
|
clone.storage = copy.deepcopy(self.storage) |
||||||
|
return clone |
||||||
|
|
||||||
|
|
||||||
|
class TaintResult: |
||||||
|
""" Taint analysis result obtained after having ran the taint runner""" |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
self.records = [] |
||||||
|
|
||||||
|
def check(self, state, stack_index): |
||||||
|
""" |
||||||
|
Checks if stack variable is tainted, before executing the instruction |
||||||
|
:param state: state to check variable in |
||||||
|
:param stack_index: index of stack variable |
||||||
|
:return: tainted |
||||||
|
""" |
||||||
|
record = self._try_get_record(state) |
||||||
|
if record is None: |
||||||
|
return None |
||||||
|
return record.stack_tainted(stack_index) |
||||||
|
|
||||||
|
def add_records(self, records): |
||||||
|
""" Adds records to this taint result """ |
||||||
|
self.records += records |
||||||
|
|
||||||
|
def _try_get_record(self, state): |
||||||
|
""" Finds record belonging to the state """ |
||||||
|
for record in self.records: |
||||||
|
if state in record.states: |
||||||
|
return record |
||||||
|
return None |
||||||
|
|
||||||
|
|
||||||
|
class TaintRunner: |
||||||
|
""" |
||||||
|
Taint runner, is able to run taint analysis on symbolic execution result |
||||||
|
""" |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def execute(statespace, node, state, initial_stack=[]): |
||||||
|
""" |
||||||
|
Runs taint analysis on the statespace |
||||||
|
:param statespace: symbolic statespace to run taint analysis on |
||||||
|
:param node: taint introduction node |
||||||
|
:param state: taint introduction state |
||||||
|
:param stack_indexes: stack indexes to introduce taint |
||||||
|
:return: TaintResult object containing analysis results |
||||||
|
""" |
||||||
|
result = TaintResult() |
||||||
|
|
||||||
|
# Build initial current_node |
||||||
|
init_record = TaintRecord() |
||||||
|
init_record.stack = initial_stack |
||||||
|
|
||||||
|
state_index = node.states.index(state) |
||||||
|
|
||||||
|
# List of (Node, TaintRecord, index) |
||||||
|
current_nodes = [(node, init_record, state_index)] |
||||||
|
|
||||||
|
for node, record, index in current_nodes: |
||||||
|
records = TaintRunner.execute_node(node, record, index) |
||||||
|
result.add_records(records) |
||||||
|
|
||||||
|
children = [statespace.nodes[edge.node_to] for edge in statespace.edges if edge.node_from == node.uid] |
||||||
|
for child in children: |
||||||
|
current_nodes.append((child, records[-1], 0)) |
||||||
|
return result |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def execute_node(node, last_record, state_index=0): |
||||||
|
""" |
||||||
|
Runs taint analysis on a given node |
||||||
|
:param node: node to analyse |
||||||
|
:param last_record: last taint record to work from |
||||||
|
:param state_index: state index to start from |
||||||
|
:return: List of taint records linked to the states in this node |
||||||
|
""" |
||||||
|
records = [last_record] |
||||||
|
for index in range(state_index, len(node.states)): |
||||||
|
current_state = node.states[index] |
||||||
|
records.append(TaintRunner.execute_state(records[-1], current_state)) |
||||||
|
return records[1:] |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def execute_state(record, state): |
||||||
|
assert len(state.mstate.stack) == len(record.stack) |
||||||
|
""" Runs taint analysis on a state """ |
||||||
|
record.add_state(state) |
||||||
|
new_record = record.clone() |
||||||
|
|
||||||
|
# Apply Change |
||||||
|
op = state.get_current_instruction()['opcode'] |
||||||
|
if op in TaintRunner.stack_taint_table.keys(): |
||||||
|
mutator = TaintRunner.stack_taint_table[op] |
||||||
|
TaintRunner.mutate_stack(new_record, mutator) |
||||||
|
elif op.startswith("PUSH"): |
||||||
|
TaintRunner.mutate_push(op, new_record) |
||||||
|
elif op.startswith("DUP"): |
||||||
|
TaintRunner.mutate_dup(op, new_record) |
||||||
|
elif op.startswith("SWAP"): |
||||||
|
TaintRunner.mutate_swap(op, new_record) |
||||||
|
elif op is "MLOAD": |
||||||
|
TaintRunner.mutate_mload(new_record, state.mstate.stack[-1]) |
||||||
|
elif op.startswith("MSTORE"): |
||||||
|
TaintRunner.mutate_mstore(new_record, state.mstate.stack[-1]) |
||||||
|
elif op is "SLOAD": |
||||||
|
TaintRunner.mutate_sload(new_record, state.mstate.stack[-1]) |
||||||
|
elif op is "SSTORE": |
||||||
|
TaintRunner.mutate_sstore(new_record, state.mstate.stack[-1]) |
||||||
|
elif op.startswith("LOG"): |
||||||
|
TaintRunner.mutate_log(new_record, op) |
||||||
|
elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'): |
||||||
|
TaintRunner.mutate_call(new_record, op) |
||||||
|
else: |
||||||
|
logging.debug("Unknown operation encountered: {}".format(op)) |
||||||
|
|
||||||
|
return new_record |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_stack(record, mutator): |
||||||
|
pop, push = mutator |
||||||
|
|
||||||
|
values = [] |
||||||
|
for i in range(pop): |
||||||
|
values.append(record.stack.pop()) |
||||||
|
|
||||||
|
taint = any(values) |
||||||
|
|
||||||
|
for i in range(push): |
||||||
|
record.stack.append(taint) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_push(op, record): |
||||||
|
TaintRunner.mutate_stack(record, (0, 1)) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_dup(op, record): |
||||||
|
depth = int(op[3:]) |
||||||
|
index = len(record.stack) - depth |
||||||
|
record.stack.append(record.stack[index]) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_swap(op, record): |
||||||
|
depth = int(op[4:]) |
||||||
|
l = len(record.stack) - 1 |
||||||
|
i = l - depth |
||||||
|
record.stack[l], record.stack[i] = record.stack[i], record.stack[l] |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_mload(record, op0): |
||||||
|
_ = record.stack.pop() |
||||||
|
try: |
||||||
|
index = helper.get_concrete_int(op0) |
||||||
|
except AttributeError: |
||||||
|
logging.debug("Can't MLOAD taint track symbolically") |
||||||
|
record.stack.append(False) |
||||||
|
return |
||||||
|
|
||||||
|
record.stack.append(record.memory_tainted(index)) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_mstore(record, op0): |
||||||
|
_, value_taint = record.stack.pop(), record.stack.pop() |
||||||
|
try: |
||||||
|
index = helper.get_concrete_int(op0) |
||||||
|
except AttributeError: |
||||||
|
logging.debug("Can't mstore taint track symbolically") |
||||||
|
return |
||||||
|
|
||||||
|
record.memory[index] = value_taint |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_sload(record, op0): |
||||||
|
_ = record.stack.pop() |
||||||
|
try: |
||||||
|
index = helper.get_concrete_int(op0) |
||||||
|
except AttributeError: |
||||||
|
logging.debug("Can't MLOAD taint track symbolically") |
||||||
|
record.stack.append(False) |
||||||
|
return |
||||||
|
|
||||||
|
record.stack.append(record.storage_tainted(index)) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_sstore(record, op0): |
||||||
|
_, value_taint = record.stack.pop(), record.stack.pop() |
||||||
|
try: |
||||||
|
index = helper.get_concrete_int(op0) |
||||||
|
except AttributeError: |
||||||
|
logging.debug("Can't mstore taint track symbolically") |
||||||
|
return |
||||||
|
|
||||||
|
record.storage[index] = value_taint |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_log(record, op): |
||||||
|
depth = int(op[3:]) |
||||||
|
for _ in range(depth + 2): |
||||||
|
record.stack.pop() |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def mutate_call(record, op): |
||||||
|
pops = 6 |
||||||
|
if op in ('CALL', 'CALLCODE'): |
||||||
|
pops += 1 |
||||||
|
for _ in range(pops): |
||||||
|
record.stack.pop() |
||||||
|
|
||||||
|
record.stack.append(False) |
||||||
|
|
||||||
|
stack_taint_table = { |
||||||
|
# instruction: (taint source, taint target) |
||||||
|
'POP': (1, 0), |
||||||
|
'ADD': (2, 1), |
||||||
|
'MUL': (2, 1), |
||||||
|
'SUB': (2, 1), |
||||||
|
'AND': (2, 1), |
||||||
|
'OR': (2, 1), |
||||||
|
'XOR': (2, 1), |
||||||
|
'NOT': (1, 1), |
||||||
|
'BYTE': (2, 1), |
||||||
|
'DIV': (2, 1), |
||||||
|
'MOD': (2, 1), |
||||||
|
'SDIV': (2, 1), |
||||||
|
'SMOD': (2, 1), |
||||||
|
'ADDMOD': (3, 1), |
||||||
|
'MULMOD': (3, 1), |
||||||
|
'EXP': (2, 1), |
||||||
|
'SIGNEXTEND': (2, 1), |
||||||
|
'LT': (2, 1), |
||||||
|
'GT': (2, 1), |
||||||
|
'SLT': (2, 1), |
||||||
|
'SGT': (2, 1), |
||||||
|
'EQ': (2, 1), |
||||||
|
'ISZERO': (1, 1), |
||||||
|
'CALLVALUE': (0, 1), |
||||||
|
'CALLDATALOAD': (1, 1), |
||||||
|
'CALLDATACOPY': (3, 0), #todo |
||||||
|
'CALLDATASIZE': (0, 1), |
||||||
|
'ADDRESS': (0, 1), |
||||||
|
'BALANCE': (1, 1), |
||||||
|
'ORIGIN': (0, 1), |
||||||
|
'CALLER': (0, 1), |
||||||
|
'CODESIZE': (0, 1), |
||||||
|
'SHA3': (2, 1), |
||||||
|
'GASPRICE': (0, 1), |
||||||
|
'CODECOPY': (3, 0), |
||||||
|
'EXTCODESIZE': (1, 1), |
||||||
|
'EXTCODECOPY': (4, 0), |
||||||
|
'RETURNDATASIZE': (0, 1), |
||||||
|
'BLOCKHASH': (1, 1), |
||||||
|
'COINBASE': (0, 1), |
||||||
|
'TIMESTAMP': (0, 1), |
||||||
|
'NUMBER': (0, 1), |
||||||
|
'DIFFICULTY': (0, 1), |
||||||
|
'GASLIMIT': (0, 1), |
||||||
|
'JUMP': (1, 0), |
||||||
|
'JUMPI': (2, 0), |
||||||
|
'PC': (0, 1), |
||||||
|
'MSIZE': (0, 1), |
||||||
|
'GAS': (0, 1), |
||||||
|
'CREATE': (3, 1), |
||||||
|
'RETURN': (2, 0) |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
from mythril.laser.ethereum.taint_analysis import * |
||||||
|
|
||||||
|
def test_mutate_not_tainted(): |
||||||
|
# Arrange |
||||||
|
record = TaintRecord() |
||||||
|
|
||||||
|
record.stack = [True, False, False] |
||||||
|
# Act |
||||||
|
TaintRunner.mutate_stack(record, (2,1)) |
||||||
|
|
||||||
|
# Assert |
||||||
|
assert record.stack_tainted(0) |
||||||
|
assert record.stack_tainted(1) is False |
||||||
|
assert record.stack == [True, False] |
||||||
|
|
||||||
|
|
||||||
|
def test_mutate_tainted(): |
||||||
|
# Arrange |
||||||
|
record = TaintRecord() |
||||||
|
|
||||||
|
record.stack = [True, False, True] |
||||||
|
|
||||||
|
# Act |
||||||
|
TaintRunner.mutate_stack(record, (2, 1)) |
||||||
|
|
||||||
|
# Assert |
||||||
|
assert record.stack_tainted(0) |
||||||
|
assert record.stack_tainted(1) |
||||||
|
assert record.stack == [True, True] |
@ -0,0 +1,36 @@ |
|||||||
|
from mythril.laser.ethereum.taint_analysis import * |
||||||
|
|
||||||
|
|
||||||
|
def test_record_tainted_check(): |
||||||
|
# arrange |
||||||
|
record = TaintRecord() |
||||||
|
record.stack = [True, False, True] |
||||||
|
|
||||||
|
# act |
||||||
|
tainted = record.stack_tainted(2) |
||||||
|
|
||||||
|
# assert |
||||||
|
assert tainted is True |
||||||
|
|
||||||
|
|
||||||
|
def test_record_untainted_check(): |
||||||
|
# arrange |
||||||
|
record = TaintRecord() |
||||||
|
record.stack = [True, False, False] |
||||||
|
|
||||||
|
# act |
||||||
|
tainted = record.stack_tainted(2) |
||||||
|
|
||||||
|
# assert |
||||||
|
assert tainted is False |
||||||
|
|
||||||
|
|
||||||
|
def test_record_untouched_check(): |
||||||
|
# arrange |
||||||
|
record = TaintRecord() |
||||||
|
|
||||||
|
# act |
||||||
|
tainted = record.stack_tainted(3) |
||||||
|
|
||||||
|
# assert |
||||||
|
assert tainted is None |
@ -0,0 +1,36 @@ |
|||||||
|
from mythril.laser.ethereum.taint_analysis import * |
||||||
|
from mythril.laser.ethereum.svm import GlobalState |
||||||
|
|
||||||
|
|
||||||
|
def test_result_state(): |
||||||
|
# arrange |
||||||
|
taint_result = TaintResult() |
||||||
|
record = TaintRecord() |
||||||
|
state = GlobalState(2, None) |
||||||
|
state.mstate.stack = [1,2,3] |
||||||
|
record.add_state(state) |
||||||
|
record.stack = [False, False, False] |
||||||
|
# act |
||||||
|
taint_result.add_records([record]) |
||||||
|
tainted = taint_result.check(state, 2) |
||||||
|
|
||||||
|
# assert |
||||||
|
assert tainted is False |
||||||
|
assert record in taint_result.records |
||||||
|
|
||||||
|
|
||||||
|
def test_result_no_state(): |
||||||
|
# arrange |
||||||
|
taint_result = TaintResult() |
||||||
|
record = TaintRecord() |
||||||
|
state = GlobalState(2, None) |
||||||
|
state.mstate.stack = [1,2,3] |
||||||
|
|
||||||
|
|
||||||
|
# act |
||||||
|
taint_result.add_records([record]) |
||||||
|
tainted = taint_result.check(state, 2) |
||||||
|
|
||||||
|
# assert |
||||||
|
assert tainted is None |
||||||
|
assert record in taint_result.records |
@ -0,0 +1,94 @@ |
|||||||
|
import mock |
||||||
|
import pytest |
||||||
|
from pytest_mock import mocker |
||||||
|
from mythril.laser.ethereum.taint_analysis import * |
||||||
|
from mythril.laser.ethereum.svm import GlobalState, Node, Edge, LaserEVM, MachineState |
||||||
|
|
||||||
|
|
||||||
|
def test_execute_state(mocker): |
||||||
|
record = TaintRecord() |
||||||
|
record.stack = [True, False, True] |
||||||
|
|
||||||
|
state = GlobalState(None, None) |
||||||
|
state.mstate.stack = [1, 2, 3] |
||||||
|
mocker.patch.object(state, 'get_current_instruction') |
||||||
|
state.get_current_instruction.return_value = {"opcode": "ADD"} |
||||||
|
|
||||||
|
# Act |
||||||
|
new_record = TaintRunner.execute_state(record, state) |
||||||
|
|
||||||
|
# Assert |
||||||
|
assert new_record.stack == [True, True] |
||||||
|
assert record.stack == [True, False, True] |
||||||
|
|
||||||
|
|
||||||
|
def test_execute_node(mocker): |
||||||
|
record = TaintRecord() |
||||||
|
record.stack = [True, True, False, False] |
||||||
|
|
||||||
|
state_1 = GlobalState(None, None) |
||||||
|
state_1.mstate.stack = [1, 2, 3] |
||||||
|
state_1.mstate.pc = 1 |
||||||
|
mocker.patch.object(state_1, 'get_current_instruction') |
||||||
|
state_1.get_current_instruction.return_value = {"opcode": "SWAP1"} |
||||||
|
|
||||||
|
state_2 = GlobalState(None, 1) |
||||||
|
state_2.mstate.stack = [1, 2, 4, 1] |
||||||
|
mocker.patch.object(state_2, 'get_current_instruction') |
||||||
|
state_2.get_current_instruction.return_value = {"opcode": "ADD"} |
||||||
|
|
||||||
|
node = Node("Test contract") |
||||||
|
node.states = [state_1, state_2] |
||||||
|
|
||||||
|
# Act |
||||||
|
records = TaintRunner.execute_node(node, record) |
||||||
|
|
||||||
|
# Assert |
||||||
|
assert len(records) == 2 |
||||||
|
|
||||||
|
assert records[0].stack == [True, True, False, False] |
||||||
|
assert records[1].stack == [True, True, False] |
||||||
|
|
||||||
|
assert state_2 in records[0].states |
||||||
|
assert state_1 in record.states |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute(mocker): |
||||||
|
state_1 = GlobalState(None, None, MachineState(gas=10000000)) |
||||||
|
state_1.mstate.stack = [1, 2] |
||||||
|
mocker.patch.object(state_1, 'get_current_instruction') |
||||||
|
state_1.get_current_instruction.return_value = {"opcode": "PUSH"} |
||||||
|
|
||||||
|
state_2 = GlobalState(None, None, MachineState(gas=10000000)) |
||||||
|
state_2.mstate.stack = [1, 2, 3] |
||||||
|
mocker.patch.object(state_2, 'get_current_instruction') |
||||||
|
state_2.get_current_instruction.return_value = {"opcode": "ADD"} |
||||||
|
|
||||||
|
node_1 = Node("Test contract") |
||||||
|
node_1.states = [state_1, state_2] |
||||||
|
|
||||||
|
state_3 = GlobalState(None, None, MachineState(gas=10000000)) |
||||||
|
state_3.mstate.stack = [1, 2] |
||||||
|
mocker.patch.object(state_3, 'get_current_instruction') |
||||||
|
state_3.get_current_instruction.return_value = {"opcode": "ADD"} |
||||||
|
|
||||||
|
node_2 = Node("Test contract") |
||||||
|
node_2.states = [state_3] |
||||||
|
|
||||||
|
edge = Edge(node_1.uid, node_2.uid) |
||||||
|
|
||||||
|
statespace = LaserEVM(None) |
||||||
|
statespace.edges = [edge] |
||||||
|
statespace.nodes[node_1.uid] = node_1 |
||||||
|
statespace.nodes[node_2.uid] = node_2 |
||||||
|
|
||||||
|
# Act |
||||||
|
result = TaintRunner.execute(statespace, node_1, state_1, [True, True]) |
||||||
|
|
||||||
|
# Assert |
||||||
|
print(result) |
||||||
|
assert len(result.records) == 3 |
||||||
|
assert result.records[2].states == [] |
||||||
|
assert state_3 in result.records[1].states |
@ -0,0 +1,152 @@ |
|||||||
|
contract Rubixi { |
||||||
|
|
||||||
|
//Declare variables for storage critical to contract |
||||||
|
uint private balance = 0; |
||||||
|
uint private collectedFees = 0; |
||||||
|
uint private feePercent = 10; |
||||||
|
uint private pyramidMultiplier = 300; |
||||||
|
uint private payoutOrder = 0; |
||||||
|
|
||||||
|
address private creator; |
||||||
|
|
||||||
|
//Sets creator |
||||||
|
function DynamicPyramid() { |
||||||
|
creator = msg.sender; |
||||||
|
} |
||||||
|
|
||||||
|
modifier onlyowner { |
||||||
|
if (msg.sender == creator) _; |
||||||
|
} |
||||||
|
|
||||||
|
struct Participant { |
||||||
|
address etherAddress; |
||||||
|
uint payout; |
||||||
|
} |
||||||
|
|
||||||
|
Participant[] private participants; |
||||||
|
|
||||||
|
//Fallback function |
||||||
|
function() { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
//init function run on fallback |
||||||
|
function init() private { |
||||||
|
//Ensures only tx with value of 1 ether or greater are processed and added to pyramid |
||||||
|
if (msg.value < 1 ether) { |
||||||
|
collectedFees += msg.value; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
uint _fee = feePercent; |
||||||
|
//50% fee rebate on any ether value of 50 or greater |
||||||
|
if (msg.value >= 50 ether) _fee /= 2; |
||||||
|
|
||||||
|
addPayout(_fee); |
||||||
|
} |
||||||
|
|
||||||
|
//Function called for valid tx to the contract |
||||||
|
function addPayout(uint _fee) private { |
||||||
|
//Adds new address to participant array |
||||||
|
participants.push(Participant(msg.sender, (msg.value * pyramidMultiplier) / 100)); |
||||||
|
|
||||||
|
//These statements ensure a quicker payout system to later pyramid entrants, so the pyramid has a longer lifespan |
||||||
|
if (participants.length == 10) pyramidMultiplier = 200; |
||||||
|
else if (participants.length == 25) pyramidMultiplier = 150; |
||||||
|
|
||||||
|
// collect fees and update contract balance |
||||||
|
balance += (msg.value * (100 - _fee)) / 100; |
||||||
|
collectedFees += (msg.value * _fee) / 100; |
||||||
|
|
||||||
|
//Pays earlier participiants if balance sufficient |
||||||
|
while (balance > participants[payoutOrder].payout) { |
||||||
|
uint payoutToSend = participants[payoutOrder].payout; |
||||||
|
participants[payoutOrder].etherAddress.send(payoutToSend); |
||||||
|
|
||||||
|
balance -= participants[payoutOrder].payout; |
||||||
|
payoutOrder += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//Fee functions for creator |
||||||
|
function collectAllFees() onlyowner { |
||||||
|
if (collectedFees == 0) throw; |
||||||
|
|
||||||
|
creator.send(collectedFees); |
||||||
|
collectedFees = 0; |
||||||
|
} |
||||||
|
|
||||||
|
function collectFeesInEther(uint _amt) onlyowner { |
||||||
|
_amt *= 1 ether; |
||||||
|
if (_amt > collectedFees) collectAllFees(); |
||||||
|
|
||||||
|
if (collectedFees == 0) throw; |
||||||
|
|
||||||
|
creator.send(_amt); |
||||||
|
collectedFees -= _amt; |
||||||
|
} |
||||||
|
|
||||||
|
function collectPercentOfFees(uint _pcent) onlyowner { |
||||||
|
if (collectedFees == 0 || _pcent > 100) throw; |
||||||
|
|
||||||
|
uint feesToCollect = collectedFees / 100 * _pcent; |
||||||
|
creator.send(feesToCollect); |
||||||
|
collectedFees -= feesToCollect; |
||||||
|
} |
||||||
|
|
||||||
|
//Functions for changing variables related to the contract |
||||||
|
function changeOwner(address _owner) onlyowner { |
||||||
|
creator = _owner; |
||||||
|
} |
||||||
|
|
||||||
|
function changeMultiplier(uint _mult) onlyowner { |
||||||
|
if (_mult > 300 || _mult < 120) throw; |
||||||
|
|
||||||
|
pyramidMultiplier = _mult; |
||||||
|
} |
||||||
|
|
||||||
|
function changeFeePercentage(uint _fee) onlyowner { |
||||||
|
if (_fee > 10) throw; |
||||||
|
|
||||||
|
feePercent = _fee; |
||||||
|
} |
||||||
|
|
||||||
|
//Functions to provide information to end-user using JSON interface or other interfaces |
||||||
|
function currentMultiplier() constant returns(uint multiplier, string info) { |
||||||
|
multiplier = pyramidMultiplier; |
||||||
|
info = 'This multiplier applies to you as soon as transaction is received, may be lowered to hasten payouts or increased if payouts are fast enough. Due to no float or decimals, multiplier is x100 for a fractional multiplier e.g. 250 is actually a 2.5x multiplier. Capped at 3x max and 1.2x min.'; |
||||||
|
} |
||||||
|
|
||||||
|
function currentFeePercentage() constant returns(uint fee, string info) { |
||||||
|
fee = feePercent; |
||||||
|
info = 'Shown in % form. Fee is halved(50%) for amounts equal or greater than 50 ethers. (Fee may change, but is capped to a maximum of 10%)'; |
||||||
|
} |
||||||
|
|
||||||
|
function currentPyramidBalanceApproximately() constant returns(uint pyramidBalance, string info) { |
||||||
|
pyramidBalance = balance / 1 ether; |
||||||
|
info = 'All balance values are measured in Ethers, note that due to no decimal placing, these values show up as integers only, within the contract itself you will get the exact decimal value you are supposed to'; |
||||||
|
} |
||||||
|
|
||||||
|
function nextPayoutWhenPyramidBalanceTotalsApproximately() constant returns(uint balancePayout) { |
||||||
|
balancePayout = participants[payoutOrder].payout / 1 ether; |
||||||
|
} |
||||||
|
|
||||||
|
function feesSeperateFromBalanceApproximately() constant returns(uint fees) { |
||||||
|
fees = collectedFees / 1 ether; |
||||||
|
} |
||||||
|
|
||||||
|
function totalParticipants() constant returns(uint count) { |
||||||
|
count = participants.length; |
||||||
|
} |
||||||
|
|
||||||
|
function numberOfParticipantsWaitingForPayout() constant returns(uint count) { |
||||||
|
count = participants.length - payoutOrder; |
||||||
|
} |
||||||
|
|
||||||
|
function participantDetails(uint orderInPyramid) constant returns(address Address, uint Payout) { |
||||||
|
if (orderInPyramid <= participants.length) { |
||||||
|
Address = participants[orderInPyramid].etherAddress; |
||||||
|
Payout = participants[orderInPyramid].payout / 1 ether; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue