def apply_msg(block, tx, msg, code): pblogger.log("MSG APPLY", tx=tx.hex_hash(), sender=msg.sender, to=msg.to, gas=msg.gas, value=msg.value, data=msg.data.encode('hex')) if pblogger.log_pre_state: pblogger.log('MSG PRE STATE', account=msg.to, state=block.account_to_dict(msg.to)) # Transfer value, instaquit if not enough o = block.transfer_value(msg.sender, msg.to, msg.value) if not o: return 1, msg.gas, [] snapshot = block.snapshot() compustate = Compustate(gas=msg.gas) t, ops = time.time(), 0 if code in code_cache: processed_code = code_cache[code] else: processed_code = [opcodes.get(ord(c), ['INVALID', 0, 0, [], 0]) + [ord(c)] for c in code] code_cache[code] = processed_code # Main loop while 1: o = apply_op(block, tx, msg, processed_code, compustate) ops += 1 if o is not None: pblogger.log('MSG APPLIED', result=o, gas_remained=compustate.gas, sender=msg.sender, to=msg.to, ops=ops, time_per_op=(time.time() - t) / ops) if pblogger.log_post_state: pblogger.log('MSG POST STATE', account=msg.to, state=block.account_to_dict(msg.to)) if o == OUT_OF_GAS: block.revert(snapshot) return 0, compustate.gas, [] else: return 1, compustate.gas, o
def gas_estimate(code, depth=0): if isinstance(code.value, int): return 3 elif isinstance(code.value, str) and (code.value.upper() in opcodes or code.value.upper() in pseudo_opcodes): decl = opcodes.get(code.value.upper(), pseudo_opcodes.get(code.value.upper(), None)) o = sum([gas_estimate(c, depth + i) for i, c in enumerate(code.args[::-1])]) + decl[3] # Dynamic gas costs if code.value.upper() == 'CALL' and code.args[2].value != 0: o += 34000 if code.value.upper() == 'SSTORE' and code.args[1].value != 0: o += 15000 if code.value.upper() in ('SUICIDE', 'SELFDESTRUCT'): o += 25000 if code.value.upper() == 'BREAK': o += opcodes['POP'][3] * depth return o elif isinstance(code.value, str) and code.value == 'if': if (len(code.args) == 2): return gas_estimate(code.args[0], depth + 1) + gas_estimate(code.args[1], depth + 1) + 30 elif (len(code.args) == 3): return gas_estimate(code.args[0], depth + 1) + max(gas_estimate(code.args[1], depth + 1), gas_estimate(code.args[2], depth + 1)) + 30 else: raise Exception("If statement must have 2 or 3 child elements") elif isinstance(code.value, str) and code.value == 'with': return gas_estimate(code.args[1], depth + 1) + gas_estimate(code.args[2], depth + 1) + 20 elif isinstance(code.value, str) and code.value == 'repeat': return (gas_estimate(code.args[2], depth + 1) + 50) * code.args[0].value + 30 elif isinstance(code.value, str) and code.value == 'seq': return sum([gas_estimate(c, depth + 1) for c in code.args]) elif isinstance(code.value, str): return 3 else: raise Exception("Gas estimate failed: "+repr(code))
def execute(self): # some common stuff if not self.is_readable(self.cur_addr): raise MemReadLockedError((self.cur_addr, self.cur_addr)) current_word = self.get_cur_word() c = current_word[5] f = current_word[4] op = opcodes.get(c, opcodes.get((c,f), None)) self.jump_to = None before_cycles = self.cycles if self.op_hook is not None: self.op_hook(op.__name__, current_word) op(self) if self.jump_to is None: self.cur_addr += 1 else: self.cur_addr = self.jump_to return self.cycles - before_cycles
def preprocess_vmcode(code): o = [] jumps = {} # Round 1: Locate all JUMP, JUMPI, JUMPDEST, STOP, RETURN, INVALID locs opcodez = copy.deepcopy( [opcodes.get(ord(c), ['INVALID', 0, 0, 1]) + [ord(c)] for c in code]) for i, o in enumerate(opcodez): if o[0] in filter1: jumps[i] = True chunks = {} chunks["code"] = [ord(x) for x in code] c = [] h, reqh, gascost = 0, 0, 0 i = 0 laststart, lastjumpable = 0, 0 while i < len(opcodez): o = opcodez[i] # [op, ins, outs, gas, opcode, pos, push?] o.append(laststart + i) c.append(o) reqh = max(reqh, h + o[1]) h += o[1] - o[2] gascost += o[3] if i in jumps: chunks[laststart] = { "reqh": reqh, "deltah": -h, "gascost": gascost, "opdata": c, "ops": list(enumerate([x[4] for x in c])), "start": laststart, "jumpable": lastjumpable, "end": i + 1 } c = [] laststart = i + 1 lastjumpable = 1 if o[0] == 'JUMPDEST' else 0 h, reqh, gascost = 0, 0, 0 if 0x60 <= o[4] < 0x80: v = 0 for j in range(i + 1, i + o[4] - 0x5e): v = (v << 8) + opcodez[j][4] o.append(v) i += o[4] - 0x5f i += 1 chunks[laststart] = { "reqh": reqh, "deltah": -h, "gascost": gascost, "opdata": c, "ops": list(enumerate([x[4] for x in c])), "start": laststart, "jumpable": lastjumpable, "end": i + 1 } return chunks
def apply_msg(block, tx, msg, code): pblogger.log("MSG APPLY", tx=tx.hex_hash(), sender=msg.sender, to=msg.to, gas=msg.gas, value=msg.value, data=msg.data.encode('hex')) if pblogger.log_pre_state: pblogger.log('MSG PRE STATE', account=msg.to, state=block.account_to_dict(msg.to)) # Transfer value, instaquit if not enough o = block.transfer_value(msg.sender, msg.to, msg.value) if not o: return 1, msg.gas, [] snapshot = block.snapshot() compustate = Compustate(gas=msg.gas) t, ops = time.time(), 0 if code in code_cache: processed_code = code_cache[code] else: processed_code = [ opcodes.get(ord(c), ['INVALID', 0, 0, [], 0]) + [ord(c)] for c in code ] code_cache[code] = processed_code # Main loop while 1: o = apply_op(block, tx, msg, processed_code, compustate) ops += 1 if o is not None: pblogger.log('MSG APPLIED', result=o, gas_remained=compustate.gas, sender=msg.sender, to=msg.to, ops=ops, time_per_op=(time.time() - t) / ops) if pblogger.log_post_state: pblogger.log('MSG POST STATE', account=msg.to, state=block.account_to_dict(msg.to)) if o == OUT_OF_GAS: block.revert(snapshot) return 0, compustate.gas, [] else: return 1, compustate.gas, o
def get_op_data(code, index): opcode = ord(code[index]) if index < len(code) else 0 return opcodes.get(opcode, ['INVALID', 0, 0, [], 0])
def get_op_data(code, index): return opcodes.get(get_opcode(code, index), ['INVALID', 0, 0, []])
def __init__(self, value, args=[], typ=None, annotation=None): self.value = value self.args = args self.typ = typ self.annotation = annotation # Determine this node's valency (1 if it pushes a value on the stack, # 0 otherwise) and checks to make sure the number and valencies of # children are correct # Numbers if isinstance(self.value, int): self.valency = 1 elif isinstance(self.value, str): # Opcodes and pseudo-opcodes (eg. clamp) if self.value.upper() in opcodes or self.value.upper( ) in pseudo_opcodes: record = opcodes.get( self.value.upper(), pseudo_opcodes.get(self.value.upper(), None)) self.valency = record[2] if len(self.args) != record[1]: raise Exception("Number of arguments mismatched: %r %r" % (self.value, self.args)) for arg in self.args: if arg.valency == 0: raise Exception( "Can't have a zerovalent argument to an opcode or a pseudo-opcode! %r" % arg) # If statements elif self.value == 'if': if len(self.args) == 3: if self.args[1].valency != self.args[2].valency: raise Exception( "Valency mismatch between then and else clause: %r %r" % (self.args[1], self.args[2])) if len(self.args) == 2 and self.args[1].valency: raise Exception( "2-clause if statement must have a zerovalent body: %r" % self.args[1]) if not self.args[0].valency: raise Exception( "Can't have a zerovalent argument as a test to an if statement! %r" % self.args[0]) if len(self.args) not in (2, 3): raise Exception("If can only have 2 or 3 arguments") self.valency = self.args[1].valency # With statements: with <var> <initial> <statement> elif self.value == 'with': if len(self.args) != 3: raise Exception("With statement must have 3 arguments") if len(self.args[0].args) or not isinstance( self.args[0].value, str): raise Exception( "First argument to with statement must be a variable") if not self.args[1].valency: raise Exception( "Second argument to with statement (initial value) cannot be zerovalent: %r" % self.args[1]) self.valency = self.args[2].valency # Repeat statements: repeat <index_memloc> <startval> <rounds> <body> elif self.value == 'repeat': if len(self.args[2].args) or not isinstance( self.args[2].value, int) or self.args[2].value <= 0: raise Exception( "Number of times repeated must be a constant nonzero positive integer" ) if not self.args[0].valency: raise Exception( "First argument to repeat (memory location) cannot be zerovalent: %r" % self.args[0]) if not self.args[1].valency: raise Exception( "Second argument to repeat (start value) cannot be zerovalent: %r" % self.args[1]) if self.args[3].valency: raise Exception( "Third argument to repeat (clause to be repeated) must be zerovalent: %r" % self.args[3]) self.valency = 0 # Seq statements: seq <statement> <statement> ... elif self.value == 'seq': self.valency = self.args[-1].valency if self.args else 0 # Variables else: self.valency = 1 else: raise Exception("Invalid value for LLL AST node: %r" % self.value) assert isinstance(self.args, list)