Exemple #1
0
def dec(typ, arg):
    base, sub, arrlist = typ
    sz = get_size(typ)
    # Dynamic-sized strings are encoded as <len(str)> + <str>
    if base in ('string', 'bytes') and not sub:
        L = big_endian_to_int(arg[:32])
        assert len(arg[32:]) == ceil32(
            L
        ), "Wrong data size for string/bytes object: expected %d actual %d" % (
            ceil32(L), len(arg[32:]))
        return arg[32:][:L]
    # Dynamic-sized arrays
    elif sz is None:
        L = big_endian_to_int(arg[:32])
        subtyp = base, sub, arrlist[:-1]
        subsize = get_size(subtyp)
        # If children are dynamic, use the head/tail mechanism. Fortunately,
        # here the code is simpler since we do not have to worry about
        # mixed dynamic and static children, as we do in the top-level multi-arg
        # case
        if subsize is None:
            assert len(arg) >= 32 + 32 * L, "Not enough data for head"
            start_positions = [
                big_endian_to_int(arg[32 + 32 * i:64 + 32 * i])
                for i in range(L)
            ] + [len(arg)]
            outs = [
                arg[start_positions[i]:start_positions[i + 1]]
                for i in range(L)
            ]
            return [dec(subtyp, out) for out in outs]
        # If children are static, then grab the data slice for each one and
        # sequentially decode them manually
        else:
            return [
                dec(subtyp, arg[32 + subsize * i:32 + subsize * (i + 1)])
                for i in range(L)
            ]
    # Static-sized arrays: decode piece-by-piece
    elif len(arrlist):
        L = arrlist[-1][0]
        subtyp = base, sub, arrlist[:-1]
        subsize = get_size(subtyp)
        return [
            dec(subtyp, arg[subsize * i:subsize * (i + 1)]) for i in range(L)
        ]
    else:
        return decode_single(typ, arg)
Exemple #2
0
def calculate_sha3_gas(length: int):
    """

    :param length:
    :return:
    """
    gas_val = 30 + opcodes.GSHA3WORD * (ceil32(length) // 32)
    return gas_val, gas_val
Exemple #3
0
def data_copy(compustate, size):
    if size:
        copyfee = opcodes.GCOPY * utils.ceil32(size) // 32
        if compustate.gas < copyfee:
            compustate.gas = 0
            return False
        compustate.gas -= copyfee
    return True
Exemple #4
0
def data_copy(compustate, size):
    if size:
        copyfee = opcodes.GCOPY * utils.ceil32(size) / 32
        if compustate.gas < copyfee:
            compustate.gas = 0
            return False
        compustate.gas -= copyfee
    return True
 def calculate_memory_gas(self, start: int, size: int):
     # https://github.com/ethereum/pyethereum/blob/develop/ethereum/vm.py#L148
     oldsize = self.memory_size // 32
     old_totalfee = (oldsize * opcodes.GMEMORY +
                     oldsize**2 // opcodes.GQUADRATICMEMDENOM)
     newsize = utils.ceil32(start + size) // 32
     new_totalfee = (newsize * opcodes.GMEMORY +
                     newsize**2 // opcodes.GQUADRATICMEMDENOM)
     return new_totalfee - old_totalfee
Exemple #6
0
def proc_ripemd160(ext, msg):
    # print('ripemd160 proc', msg.gas)
    OP_GAS = opcodes.GRIPEMD160BASE + \
        (utils.ceil32(msg.data.size) // 32) * opcodes.GRIPEMD160WORD
    gas_cost = OP_GAS
    if msg.gas < gas_cost:
        return 0, 0, []
    d = msg.data.extract_all()
    o = [0] * 12 + [safe_ord(x) for x in bitcoin.ripemd.RIPEMD160(d).digest()]
    return 1, msg.gas - gas_cost, o
Exemple #7
0
def proc_sha256(ext, msg):
    # print('sha256 proc', msg.gas)
    OP_GAS = opcodes.GSHA256BASE + \
        (utils.ceil32(msg.data.size) // 32) * opcodes.GSHA256WORD
    gas_cost = OP_GAS
    if msg.gas < gas_cost:
        return 0, 0, []
    d = msg.data.extract_all()
    o = [safe_ord(x) for x in bitcoin.bin_sha256(d)]
    return 1, msg.gas - gas_cost, o
Exemple #8
0
def proc_identity(ext, msg):
    #print('identity proc', msg.gas)
    OP_GAS = opcodes.GIDENTITYBASE + \
        opcodes.GIDENTITYWORD * (utils.ceil32(msg.data.size) // 32)
    gas_cost = OP_GAS
    if msg.gas < gas_cost:
        return 0, 0, []
    o = [0] * msg.data.size
    msg.data.extract_copy(o, 0, 0, len(o))
    return 1, msg.gas - gas_cost, o
def proc_ripemd160(ext, msg):
    # print('ripemd160 proc', msg.gas)
    OP_GAS = opcodes.GRIPEMD160BASE + \
        (utils.ceil32(msg.data.size) // 32) * opcodes.GRIPEMD160WORD
    gas_cost = OP_GAS
    if msg.gas < gas_cost:
        return 0, 0, []
    d = msg.data.extract_all()
    o = [0] * 12 + [safe_ord(x) for x in bitcoin.ripemd.RIPEMD160(d).digest()]
    return 1, msg.gas - gas_cost, o
Exemple #10
0
def mem_extend(mem, compustate, op, start, sz):
    if sz and utils.ceil32(start + sz) > len(mem):
        oldsize = len(mem) // 32
        old_totalfee = oldsize * opcodes.GMEMORY + \
            oldsize**2 // opcodes.GQUADRATICMEMDENOM
        newsize = utils.ceil32(start + sz) // 32
        # if newsize > 524288:
        #     raise Exception("Memory above 16 MB per call not supported by this VM")
        new_totalfee = newsize * opcodes.GMEMORY + \
            newsize**2 // opcodes.GQUADRATICMEMDENOM
        if old_totalfee < new_totalfee:
            memfee = new_totalfee - old_totalfee
            if compustate.gas < memfee:
                compustate.gas = 0
                return False
            compustate.gas -= memfee
            m_extend = (newsize - oldsize) * 32
            mem.extend([0] * m_extend)
    return True
def proc_sha256(ext, msg):
    # print('sha256 proc', msg.gas)
    OP_GAS = opcodes.GSHA256BASE + \
        (utils.ceil32(msg.data.size) // 32) * opcodes.GSHA256WORD
    gas_cost = OP_GAS
    if msg.gas < gas_cost:
        return 0, 0, []
    d = msg.data.extract_all()
    o = [safe_ord(x) for x in bitcoin.bin_sha256(d)]
    return 1, msg.gas - gas_cost, o
def proc_identity(ext, msg):
    #print('identity proc', msg.gas)
    OP_GAS = opcodes.GIDENTITYBASE + \
        opcodes.GIDENTITYWORD * (utils.ceil32(msg.data.size) // 32)
    gas_cost = OP_GAS
    if msg.gas < gas_cost:
        return 0, 0, []
    o = [0] * msg.data.size
    msg.data.extract_copy(o, 0, 0, len(o))
    return 1, msg.gas - gas_cost, o
def mem_extend(mem, compustate, op, start, sz):
    if sz and utils.ceil32(start + sz) > len(mem):
        oldsize = len(mem) // 32
        old_totalfee = oldsize * opcodes.GMEMORY + \
            oldsize**2 // opcodes.GQUADRATICMEMDENOM
        newsize = utils.ceil32(start + sz) // 32
        # if newsize > 524288:
        #     raise Exception("Memory above 16 MB per call not supported by this VM")
        new_totalfee = newsize * opcodes.GMEMORY + \
            newsize**2 // opcodes.GQUADRATICMEMDENOM
        if old_totalfee < new_totalfee:
            memfee = new_totalfee - old_totalfee
            if compustate.gas < memfee:
                compustate.gas = 0
                return False
            compustate.gas -= memfee
            m_extend = (newsize - oldsize) * 32
            mem.extend([0] * m_extend)
    return True
Exemple #14
0
def calculate_native_gas(size: int, contract: str):
    gas_value = None
    word_num = ceil32(size) // 32
    if contract == "ecrecover":
        gas_value = opcodes.GECRECOVER
    elif contract == "sha256":
        gas_value = opcodes.GSHA256BASE + word_num * opcodes.GSHA256WORD
    elif contract == "ripemd160":
        gas_value = opcodes.GRIPEMD160BASE + word_num * opcodes.GRIPEMD160WORD
    elif contract == "identity":
        gas_value = opcodes.GIDENTITYBASE + word_num * opcodes.GIDENTITYWORD
    else:
        raise ValueError("Unknown contract type {}".format(contract))
    return gas_value, gas_value
Exemple #15
0
def test_memory_extension(initial_size, start, extension_size):
    # Arrange
    machine_state = MachineState(gas_limit=8000000)
    machine_state.memory = Memory()
    machine_state.memory.extend(initial_size)

    # Act
    machine_state.mem_extend(start, extension_size)

    # Assert
    assert machine_state.memory_size == len(machine_state.memory)
    assert machine_state.memory_size == max(
        initial_size, (utils.ceil32(start + extension_size) // 32) * 32
    )
    def calculate_extension_size(self, start: int, size: int) -> int:
        """

        :param start:
        :param size:
        :return:
        """
        if self.memory_size > start + size:
            return 0

        # The extension size is calculated based on the word length
        new_size = utils.ceil32(start + size) // 32
        old_size = self.memory_size // 32

        return (new_size - old_size) * 32
Exemple #17
0
def mem_extend(mem, compustate, op, start, sz):
    if sz and start + sz > len(mem):
        oldsize = len(mem) // 32
        old_totalfee = oldsize * opcodes.GMEMORY + \
            oldsize**2 // opcodes.GQUADRATICMEMDENOM
        newsize = utils.ceil32(start + sz) // 32
        new_totalfee = newsize * opcodes.GMEMORY + \
            newsize**2 // opcodes.GQUADRATICMEMDENOM
        memfee = new_totalfee - old_totalfee
        if compustate.gas < memfee:
            compustate.gas = 0
            return False
        compustate.gas -= memfee
        m_extend = (newsize - oldsize) * 32
        mem.extend(bytearray(m_extend))
    return True
Exemple #18
0
def enc(typ, arg):
    base, sub, arrlist = typ
    sz = get_size(typ)
    # Encode dynamic-sized strings as <len(str)> + <str>
    if base in ('string', 'bytes') and not sub:
        assert isinstance(arg, (str, bytes, utils.unicode)), \
            "Expecting a string"
        return enc(lentyp, len(arg)) + \
            utils.to_string(arg) + \
            b'\x00' * (utils.ceil32(len(arg)) - len(arg))
    # Encode dynamic-sized lists via the head/tail mechanism described in
    # https://github.com/ethereum/wiki/wiki/Proposal-for-new-ABI-value-encoding
    elif sz is None:
        assert isinstance(arg, list), \
            "Expecting a list argument"
        subtyp = base, sub, arrlist[:-1]
        subsize = get_size(subtyp)
        myhead, mytail = b'', b''
        if arrlist[-1] == []:
            myhead += enc(lentyp, len(arg))
        else:
            assert len(arg) == arrlist[-1][0], \
                "Wrong array size: found %d, expecting %d" % \
                (len(arg), arrlist[-1][0])
        for i in range(len(arg)):
            if subsize is None:
                myhead += enc(lentyp, 32 * len(arg) + len(mytail))
                mytail += enc(subtyp, arg[i])
            else:
                myhead += enc(subtyp, arg[i])
        return myhead + mytail
    # Encode static-sized lists via sequential packing
    else:
        if arrlist == []:
            return utils.to_string(encode_single(typ, arg))
        else:
            subtyp = base, sub, arrlist[:-1]
            o = b''
            for x in arg:
                o += enc(subtyp, x)
            return o
Exemple #19
0
def enc(typ, arg):
    base, sub, arrlist = typ
    sz = get_size(typ)
    # Encode dynamic-sized strings as <len(str)> + <str>
    if base in ('string', 'bytes') and not sub:
        assert isinstance(arg, (str, bytes, utils.unicode)), \
            "Expecting a string"
        return enc(lentyp, len(arg)) + \
            utils.to_string(arg) + \
            b'\x00' * (utils.ceil32(len(arg)) - len(arg))
    # Encode dynamic-sized lists via the head/tail mechanism described in
    # https://github.com/ethereum/wiki/wiki/Proposal-for-new-ABI-value-encoding
    elif sz is None:
        assert isinstance(arg, list), \
            "Expecting a list argument"
        subtyp = base, sub, arrlist[:-1]
        subsize = get_size(subtyp)
        myhead, mytail = b'', b''
        if arrlist[-1] == []:
            myhead += enc(lentyp, len(arg))
        else:
            assert len(arg) == arrlist[-1][0], \
                "Wrong array size: found %d, expecting %d" % \
                (len(arg), arrlist[-1][0])
        for i in range(len(arg)):
            if subsize is None:
                myhead += enc(lentyp, 32 * len(arg) + len(mytail))
                mytail += enc(subtyp, arg[i])
            else:
                myhead += enc(subtyp, arg[i])
        return myhead + mytail
    # Encode static-sized lists via sequential packing
    else:
        if arrlist == []:
            return utils.to_string(encode_single(typ, arg))
        else:
            subtyp = base, sub, arrlist[:-1]
            o = b''
            for x in arg:
                o += enc(subtyp, x)
            return o
Exemple #20
0
def dec(typ, arg):
    base, sub, arrlist = typ
    sz = get_size(typ)
    # Dynamic-sized strings are encoded as <len(str)> + <str>
    if base in ('string', 'bytes') and not sub:
        L = big_endian_to_int(arg[:32])
        assert len(arg[32:]) == ceil32(L), "Wrong data size for string/bytes object"
        return arg[32:][:L]
    # Dynamic-sized arrays
    elif sz is None:
        L = big_endian_to_int(arg[:32])
        subtyp = base, sub, arrlist[:-1]
        subsize = get_size(subtyp)
        # If children are dynamic, use the head/tail mechanism. Fortunately,
        # here the code is simpler since we do not have to worry about
        # mixed dynamic and static children, as we do in the top-level multi-arg
        # case
        if subsize is None:
            assert len(arg) >= 32 + 32 * L, "Not enough data for head"
            start_positions = [big_endian_to_int(arg[32 + 32 * i: 64 + 32 * i])
                               for i in range(L)] + [len(arg)]
            outs = [arg[start_positions[i]: start_positions[i + 1]]
                    for i in range(L)]
            return [dec(subtyp, out) for out in outs]
        # If children are static, then grab the data slice for each one and
        # sequentially decode them manually
        else:
            return [dec(subtyp, arg[32 + subsize * i: 32 + subsize * (i + 1)])
                    for i in range(L)]
    # Static-sized arrays: decode piece-by-piece
    elif len(arrlist):
        L = arrlist[-1][0]
        subtyp = base, sub, arrlist[:-1]
        subsize = get_size(subtyp)
        return [dec(subtyp, arg[subsize * i:subsize * (i + 1)])
                for i in range(L)]
    else:
        return decode_single(typ, arg)
Exemple #21
0
def data_copy(compustate, size):
    return eat_gas(compustate, opcodes.GCOPY * utils.ceil32(size) // 32)
Exemple #22
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code

    codelen = len(processed_code)

    s = time.time()
    op = None
    steps = 0
    _prevop = None  # for trace only

    while 1:
        # print('op: ', op, time.time() - s)
        # s = time.time()
        # stack size limit error
        if compustate.pc >= codelen:
            return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])

        op, in_args, out_args, fee, opcode, pushval = \
            processed_code[compustate.pc]

        # out of gas error
        if fee > compustate.gas:
            return vm_exception('OUT OF GAS')

        # empty stack error
        if in_args > len(compustate.stack):
            return vm_exception('INSUFFICIENT STACK',
                                op=op, needed=to_string(in_args),
                                available=to_string(len(compustate.stack)))

        if len(compustate.stack) - in_args + out_args > 1024:
            return vm_exception('STACK SIZE LIMIT EXCEEDED',
                                op=op,
                                pre_height=to_string(len(compustate.stack)))

        # Apply operation
        compustate.gas -= fee
        compustate.pc += 1

        if trace_vm:
            """
            This diverges from normal logging, as we use the logging namespace
            only to decide which features get logged in 'eth.vm.op'
            i.e. tracing can not be activated by activating a sub
            like 'eth.vm.op.stack'
            """
            trace_data = {}
            trace_data['stack'] = list(map(to_string, list(compustate.stack)))
            if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL',
                           'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY',
                           'EXTCODECOPY'):
                if len(compustate.memory) < 1024:
                    trace_data['memory'] = \
                        b''.join([encode_hex(ascii_chr(x)) for x
                                  in compustate.memory])
                else:
                    trace_data['sha3memory'] = \
                        encode_hex(utils.sha3(''.join([ascii_chr(x) for
                                              x in compustate.memory])))
            if _prevop in ('SSTORE', 'SLOAD') or steps == 0:
                trace_data['storage'] = ext.log_storage(msg.to)
            trace_data['gas'] = to_string(compustate.gas + fee)
            trace_data['inst'] = opcode
            trace_data['pc'] = to_string(compustate.pc - 1)
            if steps == 0:
                trace_data['depth'] = msg.depth
                trace_data['address'] = msg.to
            trace_data['op'] = op
            trace_data['steps'] = steps
            if op[:4] == 'PUSH':
                trace_data['pushvalue'] = pushval
            log_vm_op.trace('vm', **trace_data)
            steps += 1
            _prevop = op

        # Invalid operation
        if op == 'INVALID':
            return vm_exception('INVALID OP', opcode=opcode)

        # Valid operations
        if opcode < 0x10:
            if op == 'STOP':
                return peaceful_exit('STOP', compustate.gas, [])
            elif op == 'ADD':
                stk.append((stk.pop() + stk.pop()) & TT256M1)
            elif op == 'SUB':
                stk.append((stk.pop() - stk.pop()) & TT256M1)
            elif op == 'MUL':
                stk.append((stk.pop() * stk.pop()) & TT256M1)
            elif op == 'DIV':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 // s1)
            elif op == 'MOD':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 % s1)
            elif op == 'SDIV':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                              (-1 if s0 * s1 < 0 else 1)) & TT256M1)
            elif op == 'SMOD':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                              (-1 if s0 < 0 else 1)) & TT256M1)
            elif op == 'ADDMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 + s1) % s2 if s2 else 0)
            elif op == 'MULMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 * s1) % s2 if s2 else 0)
            elif op == 'EXP':
                base, exponent = stk.pop(), stk.pop()
                # fee for exponent is dependent on its bytes
                # calc n bytes to represent exponent
                nbytes = len(utils.encode_int(exponent))
                expfee = nbytes * opcodes.GEXPONENTBYTE
                if compustate.gas < expfee:
                    compustate.gas = 0
                    return vm_exception('OOG EXPONENT')
                compustate.gas -= expfee
                stk.append(pow(base, exponent, TT256))
            elif op == 'SIGNEXTEND':
                s0, s1 = stk.pop(), stk.pop()
                if s0 <= 31:
                    testbit = s0 * 8 + 7
                    if s1 & (1 << testbit):
                        stk.append(s1 | (TT256 - (1 << testbit)))
                    else:
                        stk.append(s1 & ((1 << testbit) - 1))
                else:
                    stk.append(s1)
        elif opcode < 0x20:
            if op == 'LT':
                stk.append(1 if stk.pop() < stk.pop() else 0)
            elif op == 'GT':
                stk.append(1 if stk.pop() > stk.pop() else 0)
            elif op == 'SLT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 < s1 else 0)
            elif op == 'SGT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 > s1 else 0)
            elif op == 'EQ':
                stk.append(1 if stk.pop() == stk.pop() else 0)
            elif op == 'ISZERO':
                stk.append(0 if stk.pop() else 1)
            elif op == 'AND':
                stk.append(stk.pop() & stk.pop())
            elif op == 'OR':
                stk.append(stk.pop() | stk.pop())
            elif op == 'XOR':
                stk.append(stk.pop() ^ stk.pop())
            elif op == 'NOT':
                stk.append(TT256M1 - stk.pop())
            elif op == 'BYTE':
                s0, s1 = stk.pop(), stk.pop()
                if s0 >= 32:
                    stk.append(0)
                else:
                    stk.append((s1 // 256 ** (31 - s0)) % 256)
        elif opcode < 0x40:
            if op == 'SHA3':
                s0, s1 = stk.pop(), stk.pop()
                compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32)
                if compustate.gas < 0:
                    return vm_exception('OOG PAYING FOR SHA3')
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + s1]))
                stk.append(utils.big_endian_to_int(utils.sha3(data)))
            elif op == 'ADDRESS':
                stk.append(utils.coerce_to_int(msg.to))
            elif op == 'BALANCE':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(ext.get_balance(addr))
            elif op == 'ORIGIN':
                stk.append(utils.coerce_to_int(ext.tx_origin))
            elif op == 'CALLER':
                stk.append(utils.coerce_to_int(msg.sender))
            elif op == 'CALLVALUE':
                stk.append(msg.value)
            elif op == 'CALLDATALOAD':
                stk.append(msg.data.extract32(stk.pop()))
            elif op == 'CALLDATASIZE':
                stk.append(msg.data.size)
            elif op == 'CALLDATACOPY':
                mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                msg.data.extract_copy(mem, mstart, dstart, size)
            elif op == 'CODESIZE':
                stk.append(len(processed_code))
            elif op == 'CODECOPY':
                start, s1, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s1 + i < len(processed_code):
                        mem[start + i] = processed_code[s1 + i][4]
                    else:
                        mem[start + i] = 0
            elif op == 'GASPRICE':
                stk.append(ext.tx_gasprice)
            elif op == 'EXTCODESIZE':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(len(ext.get_code(addr) or b''))
            elif op == 'EXTCODECOPY':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                start, s2, size = stk.pop(), stk.pop(), stk.pop()
                extcode = ext.get_code(addr) or b''
                assert utils.is_string(extcode)
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s2 + i < len(extcode):
                        mem[start + i] = utils.safe_ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
        elif opcode < 0x50:
            if op == 'BLOCKHASH':
                stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop())))
            elif op == 'COINBASE':
                stk.append(utils.big_endian_to_int(ext.block_coinbase))
            elif op == 'TIMESTAMP':
                stk.append(ext.block_timestamp)
            elif op == 'NUMBER':
                stk.append(ext.block_number)
            elif op == 'DIFFICULTY':
                stk.append(ext.block_difficulty)
            elif op == 'GASLIMIT':
                stk.append(ext.block_gas_limit)
        elif opcode < 0x60:
            if op == 'POP':
                stk.pop()
            elif op == 'MLOAD':
                s0 = stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + 32]))
                stk.append(utils.big_endian_to_int(data))
            elif op == 'MSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                v = s1
                for i in range(31, -1, -1):
                    mem[s0 + i] = v % 256
                    v //= 256
            elif op == 'MSTORE8':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 1):
                    return vm_exception('OOG EXTENDING MEMORY')
                mem[s0] = s1 % 256
            elif op == 'SLOAD':
                stk.append(ext.get_storage_data(msg.to, stk.pop()))
            elif op == 'SSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if ext.get_storage_data(msg.to, s0):
                    gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                    refund = 0 if s1 else opcodes.GSTORAGEREFUND
                else:
                    gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                    refund = 0
                if compustate.gas < gascost:
                    return vm_exception('OUT OF GAS')
                compustate.gas -= gascost
                ext.add_refund(refund)  # adds neg gascost as a refund if below zero
                ext.set_storage_data(msg.to, s0, s1)
            elif op == 'JUMP':
                compustate.pc = stk.pop()
                opnew = processed_code[compustate.pc][0] if \
                    compustate.pc < len(processed_code) else 'STOP'
                if opnew != 'JUMPDEST':
                    return vm_exception('BAD JUMPDEST')
            elif op == 'JUMPI':
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    opnew = processed_code[compustate.pc][0] if \
                        compustate.pc < len(processed_code) else 'STOP'
                    if opnew != 'JUMPDEST':
                        return vm_exception('BAD JUMPDEST')
            elif op == 'PC':
                stk.append(compustate.pc - 1)
            elif op == 'MSIZE':
                stk.append(len(mem))
            elif op == 'GAS':
                stk.append(compustate.gas)  # AFTER subtracting cost 1
        elif op[:4] == 'PUSH':
            pushnum = int(op[4:])
            compustate.pc += pushnum
            stk.append(pushval)
        elif op[:3] == 'DUP':
            depth = int(op[3:])
            stk.append(stk[-depth])
        elif op[:4] == 'SWAP':
            depth = int(op[4:])
            temp = stk[-depth - 1]
            stk[-depth - 1] = stk[-1]
            stk[-1] = temp

        elif op[:3] == 'LOG':
            """
            0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
            a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                    MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
            b. Logs are kept track of during tx execution exactly the same way as suicides
               (except as an ordered list, not a set).
               Each log is in the form [address, [topic1, ... ], data] where:
               * address is what the ADDRESS opcode would output
               * data is mem[MEMSTART: MEMSTART + MEMSZ]
               * topics are as provided by the opcode
            c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
            """
            depth = int(op[3:])
            mstart, msz = stk.pop(), stk.pop()
            topics = [stk.pop() for x in range(depth)]
            compustate.gas -= msz * opcodes.GLOGBYTE
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            data = b''.join(map(ascii_chr, mem[mstart: mstart + msz]))
            ext.log(msg.to, topics, data)
            log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data)))
            # print('LOG', msg.to, topics, list(map(ord, data)))

        elif op == 'CREATE':
            value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                cd = CallData(mem, mstart, msz)
                create_msg = Message(msg.to, b'', value, compustate.gas, cd, msg.depth + 1)
                o, gas, addr = ext.create(create_msg)
                if o:
                    stk.append(utils.coerce_to_int(addr))
                    compustate.gas = gas
                else:
                    stk.append(0)
                    compustate.gas = 0
            else:
                stk.append(0)
        elif op == 'CALL':
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            to = utils.encode_int(to)
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to, to, value, submsg_gas, cd,
                                   msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'CALLCODE' or op == 'DELEGATECALL':
            if op == 'CALLCODE':
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            else:
                gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                value = 0
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                cd = CallData(mem, meminstart, meminsz)
                if ext.post_homestead_hardfork() and op == 'DELEGATECALL':
                    call_msg = Message(msg.sender, msg.to, msg.value, submsg_gas, cd,
                                       msg.depth + 1, code_address=to, transfers_value=False)
                elif op == 'DELEGATECALL':
                    return vm_exception('OPCODE INACTIVE')
                else:
                    call_msg = Message(msg.to, msg.to, value, submsg_gas, cd,
                                       msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'RETURN':
            s0, s1 = stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, s0, s1):
                return vm_exception('OOG EXTENDING MEMORY')
            return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1])
        elif op == 'SUICIDE':
            to = utils.encode_int(stk.pop())
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            xfer = ext.get_balance(msg.to)
            ext.set_balance(to, ext.get_balance(to) + xfer)
            ext.set_balance(msg.to, 0)
            ext.add_suicide(msg.to)
            # print('suiciding %s %s %d' % (msg.to, to, xfer))
            return 1, compustate.gas, []
Exemple #23
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code

    s = time.time()
    op = None
    steps = 0
    _prevop = None  # for trace only

    while 1:
      # print('op: ', op, time.time() - s)
      # s = time.time()
      # stack size limit error
      if compustate.pc not in processed_code:
          return vm_exception('INVALID START POINT')

      _data = processed_code[compustate.pc]
      gas, min_stack, max_stack, compustate.pc = _data[:4]
      ops = _data[4:]

      # out of gas error
      if gas > compustate.gas:
        return vm_exception('OUT OF GAS')

      # insufficient stack error
      if not (min_stack <= len(compustate.stack) <= max_stack):
        return vm_exception('INCOMPATIBLE STACK LENGTH', min_stack=min_stack,
                            have=len(compustate.stack), max_stack=max_stack)

      # Apply operation
      compustate.gas -= gas

      for op in ops:

        if trace_vm:
            """
            This diverges from normal logging, as we use the logging namespace
            only to decide which features get logged in 'eth.vm.op'
            i.e. tracing can not be activated by activating a sub
            like 'eth.vm.op.stack'
            """
            trace_data = {}
            trace_data['stack'] = list(map(to_string, list(compustate.stack)))
            if _prevop in (op_MLOAD, op_MSTORE, op_MSTORE8, op_SHA3, op_CALL,
                           op_CALLCODE, op_CREATE, op_CALLDATACOPY, op_CODECOPY,
                           op_EXTCODECOPY):
                if len(compustate.memory) < 1024:
                    trace_data['memory'] = \
                        b''.join([encode_hex(ascii_chr(x)) for x
                                  in compustate.memory])
                else:
                    trace_data['sha3memory'] = \
                        encode_hex(utils.sha3(''.join([ascii_chr(x) for
                                              x in compustate.memory])))
            if _prevop in (op_SSTORE, op_SLOAD) or steps == 0:
                trace_data['storage'] = ext.log_storage(msg.to)
            # trace_data['gas'] = to_string(compustate.gas + fee)
            trace_data['inst'] = op
            trace_data['pc'] = to_string(compustate.pc - 1)
            if steps == 0:
                trace_data['depth'] = msg.depth
                trace_data['address'] = msg.to
            trace_data['op'] = op
            trace_data['steps'] = steps
            # if op[:4] == 'PUSH':
            #     trace_data['pushvalue'] = pushval
            log_vm_op.trace('vm', **trace_data)
            steps += 1
            _prevop = op

        # Invalid operation
        if op == INVALID:
            return vm_exception('INVALID OP', opcode=op)

        # Valid operations
        if op < 0x10:
            if op == op_STOP:
                return peaceful_exit('STOP', compustate.gas, [])
            elif op == op_ADD:
                stk.append((stk.pop() + stk.pop()) & TT256M1)
            elif op == op_SUB:
                stk.append((stk.pop() - stk.pop()) & TT256M1)
            elif op == op_MUL:
                stk.append((stk.pop() * stk.pop()) & TT256M1)
            elif op == op_DIV:
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 // s1)
            elif op == op_MOD:
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 % s1)
            elif op == op_SDIV:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                              (-1 if s0 * s1 < 0 else 1)) & TT256M1)
            elif op == op_SMOD:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                              (-1 if s0 < 0 else 1)) & TT256M1)
            elif op == op_ADDMOD:
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 + s1) % s2 if s2 else 0)
            elif op == op_MULMOD:
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 * s1) % s2 if s2 else 0)
            elif op == op_EXP:
                base, exponent = stk.pop(), stk.pop()
                # fee for exponent is dependent on its bytes
                # calc n bytes to represent exponent
                nbytes = len(utils.encode_int(exponent))
                expfee = nbytes * opcodes.GEXPONENTBYTE
                if compustate.gas < expfee:
                    compustate.gas = 0
                    return vm_exception('OOG EXPONENT')
                compustate.gas -= expfee
                stk.append(pow(base, exponent, TT256))
            elif op == op_SIGNEXTEND:
                s0, s1 = stk.pop(), stk.pop()
                if s0 <= 31:
                    testbit = s0 * 8 + 7
                    if s1 & (1 << testbit):
                        stk.append(s1 | (TT256 - (1 << testbit)))
                    else:
                        stk.append(s1 & ((1 << testbit) - 1))
                else:
                    stk.append(s1)
        elif op < 0x20:
            if op == op_LT:
                stk.append(1 if stk.pop() < stk.pop() else 0)
            elif op == op_GT:
                stk.append(1 if stk.pop() > stk.pop() else 0)
            elif op == op_SLT:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 < s1 else 0)
            elif op == op_SGT:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 > s1 else 0)
            elif op == op_EQ:
                stk.append(1 if stk.pop() == stk.pop() else 0)
            elif op == op_ISZERO:
                stk.append(0 if stk.pop() else 1)
            elif op == op_AND:
                stk.append(stk.pop() & stk.pop())
            elif op == op_OR:
                stk.append(stk.pop() | stk.pop())
            elif op == op_XOR:
                stk.append(stk.pop() ^ stk.pop())
            elif op == op_NOT:
                stk.append(TT256M1 - stk.pop())
            elif op == op_BYTE:
                s0, s1 = stk.pop(), stk.pop()
                if s0 >= 32:
                    stk.append(0)
                else:
                    stk.append((s1 // 256 ** (31 - s0)) % 256)
        elif op < 0x40:
            if op == op_SHA3:
                s0, s1 = stk.pop(), stk.pop()
                compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32)
                if compustate.gas < 0:
                    return vm_exception('OOG PAYING FOR SHA3')
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + s1]))
                stk.append(utils.big_endian_to_int(utils.sha3(data)))
            elif op == op_ADDRESS:
                stk.append(utils.coerce_to_int(msg.to))
            elif op == op_BALANCE:
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(ext.get_balance(addr))
            elif op == op_ORIGIN:
                stk.append(utils.coerce_to_int(ext.tx_origin))
            elif op == op_CALLER:
                stk.append(utils.coerce_to_int(msg.sender))
            elif op == op_CALLVALUE:
                stk.append(msg.value)
            elif op == op_CALLDATALOAD:
                stk.append(msg.data.extract32(stk.pop()))
            elif op == op_CALLDATASIZE:
                stk.append(msg.data.size)
            elif op == op_CALLDATACOPY:
                mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                msg.data.extract_copy(mem, mstart, dstart, size)
            elif op == op_CODESIZE:
                stk.append(len(code))
            elif op == op_CODECOPY:
                start, s1, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s1 + i < len(code):
                        mem[start + i] = utils.safe_ord(code[s1 + i])
                    else:
                        mem[start + i] = 0
            elif op == op_GASPRICE:
                stk.append(ext.tx_gasprice)
            elif op == op_EXTCODESIZE:
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(len(ext.get_code(addr) or b''))
            elif op == op_EXTCODECOPY:
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                start, s2, size = stk.pop(), stk.pop(), stk.pop()
                extcode = ext.get_code(addr) or b''
                assert utils.is_string(extcode)
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s2 + i < len(extcode):
                        mem[start + i] = utils.safe_ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
        elif op < 0x50:
            if op == op_BLOCKHASH:
                stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop())))
            elif op == op_COINBASE:
                stk.append(utils.big_endian_to_int(ext.block_coinbase))
            elif op == op_TIMESTAMP:
                stk.append(ext.block_timestamp)
            elif op == op_NUMBER:
                stk.append(ext.block_number)
            elif op == op_DIFFICULTY:
                stk.append(ext.block_difficulty)
            elif op == op_GASLIMIT:
                stk.append(ext.block_gas_limit)
        elif op < 0x60:
            if op == op_POP:
                stk.pop()
            elif op == op_MLOAD:
                s0 = stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = 0
                for c in mem[s0: s0 + 32]:
                    data = (data << 8) + c
                stk.append(data)
            elif op == op_MSTORE:
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                v = s1
                for i in range(31, -1, -1):
                    mem[s0 + i] = v % 256
                    v //= 256
            elif op == op_MSTORE8:
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 1):
                    return vm_exception('OOG EXTENDING MEMORY')
                mem[s0] = s1 % 256
            elif op == op_SLOAD:
                stk.append(ext.get_storage_data(msg.to, stk.pop()))
            elif op == op_SSTORE:
                s0, s1 = stk.pop(), stk.pop()
                if ext.get_storage_data(msg.to, s0):
                    gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                    refund = 0 if s1 else opcodes.GSTORAGEREFUND
                else:
                    gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                    refund = 0
                if compustate.gas < gascost:
                    return vm_exception('OUT OF GAS')
                compustate.gas -= gascost
                ext.add_refund(refund)  # adds neg gascost as a refund if below zero
                ext.set_storage_data(msg.to, s0, s1)
            elif op == op_JUMP:
                compustate.pc = stk.pop()
                opnew = processed_code[compustate.pc][4] if \
                    compustate.pc in processed_code else op_STOP
                if opnew != op_JUMPDEST:
                    return vm_exception('BAD JUMPDEST')
            elif op == op_JUMPI:
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    opnew = processed_code[compustate.pc][4] if \
                        compustate.pc in processed_code else op_STOP
                    if opnew != op_JUMPDEST:
                        return vm_exception('BAD JUMPDEST')
            elif op == op_PC:
                stk.append(compustate.pc - 1)
            elif op == op_MSIZE:
                stk.append(len(mem))
            elif op == op_GAS:
                stk.append(compustate.gas)  # AFTER subtracting cost 1
        elif op_PUSH1 <= (op & 255) <= op_PUSH32:
            # Hide push value in high-order bytes of op
            stk.append(op >> 8)
        elif op_DUP1 <= op <= op_DUP16:
            depth = op - op_DUP1 + 1
            stk.append(stk[-depth])
        elif op_SWAP1 <= op <= op_SWAP16:
            depth = op - op_SWAP1 + 1
            temp = stk[-depth - 1]
            stk[-depth - 1] = stk[-1]
            stk[-1] = temp

        elif op_LOG0 <= op <= op_LOG4:
            """
            0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
            a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                    MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
            b. Logs are kept track of during tx execution exactly the same way as suicides
               (except as an ordered list, not a set).
               Each log is in the form [address, [topic1, ... ], data] where:
               * address is what the ADDRESS opcode would output
               * data is mem[MEMSTART: MEMSTART + MEMSZ]
               * topics are as provided by the opcode
            c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
            """
            depth = op - op_LOG0
            mstart, msz = stk.pop(), stk.pop()
            topics = [stk.pop() for x in range(depth)]
            compustate.gas -= msz * opcodes.GLOGBYTE
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            data = b''.join(map(ascii_chr, mem[mstart: mstart + msz]))
            ext.log(msg.to, topics, data)
            log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data)))
            # print('LOG', msg.to, topics, list(map(ord, data)))

        elif op == op_CREATE:
            value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                cd = CallData(mem, mstart, msz)
                create_msg = Message(msg.to, b'', value, compustate.gas, cd, msg.depth + 1)
                o, gas, addr = ext.create(create_msg)
                if o:
                    stk.append(utils.coerce_to_int(addr))
                    compustate.gas = gas
                else:
                    stk.append(0)
                    compustate.gas = 0
            else:
                stk.append(0)
        elif op == op_CALL:
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            to = utils.encode_int(to)
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to, to, value, submsg_gas, cd,
                                   msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == op_CALLCODE:
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to, msg.to, value, submsg_gas, cd,
                                   msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == op_RETURN:
            s0, s1 = stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, s0, s1):
                return vm_exception('OOG EXTENDING MEMORY')
            return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1])
        elif op == op_SUICIDE:
            to = utils.encode_int(stk.pop())
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            xfer = ext.get_balance(msg.to)
            ext.set_balance(to, ext.get_balance(to) + xfer)
            ext.set_balance(msg.to, 0)
            ext.add_suicide(msg.to)
            # print('suiciding %s %s %d' % (msg.to, to, xfer))
            return 1, compustate.gas, []
Exemple #24
0
def encode_single(typ, arg):
    base, sub, _ = typ
    # Unsigned integers: uint<sz>
    if base == 'uint':
        sub = int(sub)
        i = decint(arg)

        if not 0 <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i), 32)
    # bool: int<sz>
    elif base == 'bool':
        assert isinstance(arg, bool)
        return zpad(encode_int(int(arg)), 32)
    # Signed integers: int<sz>
    elif base == 'int':
        sub = int(sub)
        i = decint(arg)
        if not -2**(sub - 1) <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i % 2**sub), 32)
    # Unsigned reals: ureal<high>x<low>
    elif base == 'ureal':
        high, low = [int(x) for x in sub.split('x')]
        if not 0 <= arg < 2**high:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(arg * 2**low), 32)
    # Signed reals: real<high>x<low>
    elif base == 'real':
        high, low = [int(x) for x in sub.split('x')]
        if not -2**(high - 1) <= arg < 2**(high - 1):
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int((arg % 2**high) * 2**low), 32)
    # Strings
    elif base == 'string' or base == 'bytes':
        if not is_string(arg):
            raise EncodingError("Expecting string: %r" % arg)
        # Fixed length: string<sz>
        if len(sub):
            assert int(sub) <= 32
            assert len(arg) <= int(sub)
            return arg + b'\x00' * (32 - len(arg))
        # Variable length: string
        else:
            return zpad(encode_int(len(arg)), 32) + \
                arg + \
                b'\x00' * (utils.ceil32(len(arg)) - len(arg))
    # Hashes: hash<sz>
    elif base == 'hash':
        if not (int(sub) and int(sub) <= 32):
            raise EncodingError("too long: %r" % arg)
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == len(sub):
            return zpad(arg, 32)
        elif len(arg) == len(sub) * 2:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse hash: %r" % arg)
    # Addresses: address (== hash160)
    elif base == 'address':
        assert sub == ''
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == 20:
            return zpad(arg, 32)
        elif len(arg) == 40:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse address: %r" % arg)
    raise EncodingError("Unhandled type: %r %r" % (base, sub))
Exemple #25
0
def encode_single(typ, arg):
    base, sub, _ = typ
    # Unsigned integers: uint<sz>
    if base == 'uint':
        sub = int(sub)
        i = decint(arg)

        if not 0 <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i), 32)
    # bool: int<sz>
    elif base == 'bool':
        assert isinstance(arg, bool)
        return zpad(encode_int(int(arg)), 32)
    # Signed integers: int<sz>
    elif base == 'int':
        sub = int(sub)
        i = decint(arg)
        if not -2**(sub - 1) <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i % 2**sub), 32)
    # Unsigned reals: ureal<high>x<low>
    elif base == 'ureal':
        high, low = [int(x) for x in sub.split('x')]
        if not 0 <= arg < 2**high:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(arg * 2**low), 32)
    # Signed reals: real<high>x<low>
    elif base == 'real':
        high, low = [int(x) for x in sub.split('x')]
        if not -2**(high - 1) <= arg < 2**(high - 1):
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int((arg % 2**high) * 2**low), 32)
    # Strings
    elif base == 'string' or base == 'bytes':
        if not is_string(arg):
            raise EncodingError("Expecting string: %r" % arg)
        # Fixed length: string<sz>
        if len(sub):
            assert int(sub) <= 32
            assert len(arg) <= int(sub)
            return arg + b'\x00' * (32 - len(arg))
        # Variable length: string
        else:
            return zpad(encode_int(len(arg)), 32) + \
                arg + \
                b'\x00' * (utils.ceil32(len(arg)) - len(arg))
    # Hashes: hash<sz>
    elif base == 'hash':
        if not (int(sub) and int(sub) <= 32):
            raise EncodingError("too long: %r" % arg)
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == len(sub):
            return zpad(arg, 32)
        elif len(arg) == len(sub) * 2:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse hash: %r" % arg)
    # Addresses: address (== hash160)
    elif base == 'address':
        assert sub == ''
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == 20:
            return zpad(arg, 32)
        elif len(arg) == 40:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse address: %r" % arg)
    raise EncodingError("Unhandled type: %r %r" % (base, sub))
Exemple #26
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code

    codelen = len(processed_code)

    s = time.time()
    op = None

    while 1:
        # print 'op: ', op, time.time() - s
        # s = time.time()
        # stack size limit error
        if compustate.pc >= codelen:
            return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])

        op, in_args, out_args, fee, opcode, pushval = \
            processed_code[compustate.pc]

        # out of gas error
        if fee > compustate.gas:
            return vm_exception('OUT OF GAS')

        # empty stack error
        if in_args > len(compustate.stack):
            return vm_exception('INSUFFICIENT STACK',
                                op=op,
                                needed=to_string(in_args),
                                available=to_string(len(compustate.stack)))

        if len(compustate.stack) - in_args + out_args > 1024:
            return vm_exception('STACK SIZE LIMIT EXCEEDED',
                                op=op,
                                pre_height=to_string(len(compustate.stack)))

        # Apply operation
        compustate.gas -= fee
        compustate.pc += 1

        if trace_vm:
            """
            This diverges from normal logging, as we use the logging namespace
            only to decide which features get logged in 'eth.vm.op'
            i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack'
            """
            trace_data = {}
            if log_vm_op_stack.is_active():
                trace_data['stack'] = list(
                    map(to_string, list(compustate.stack)))
            if log_vm_op_memory.is_active():
                trace_data['memory'] = \
                    b''.join([encode_hex(ascii_chr(x)) for x in compustate.memory])
            if log_vm_op_storage.is_active():
                trace_data['storage'] = ext.log_storage(msg.to)
            trace_data['gas'] = to_string(compustate.gas + fee)
            trace_data['pc'] = to_string(compustate.pc - 1)
            trace_data['op'] = op
            if op[:4] == 'PUSH':
                trace_data['pushvalue'] = pushval

            log_vm_op.trace('vm', **trace_data)

        # Invalid operation
        if op == 'INVALID':
            return vm_exception('INVALID OP', opcode=opcode)

        # Valid operations
        if opcode < 0x10:
            if op == 'STOP':
                return peaceful_exit('STOP', compustate.gas, [])
            elif op == 'ADD':
                stk.append((stk.pop() + stk.pop()) & TT256M1)
            elif op == 'SUB':
                stk.append((stk.pop() - stk.pop()) & TT256M1)
            elif op == 'MUL':
                stk.append((stk.pop() * stk.pop()) & TT256M1)
            elif op == 'DIV':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 // s1)
            elif op == 'MOD':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 % s1)
            elif op == 'SDIV':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                              (-1 if s0 * s1 < 0 else 1))
                           & TT256M1)
            elif op == 'SMOD':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                              (-1 if s0 < 0 else 1)) & TT256M1)
            elif op == 'ADDMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 + s1) % s2 if s2 else 0)
            elif op == 'MULMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 * s1) % s2 if s2 else 0)
            elif op == 'EXP':
                base, exponent = stk.pop(), stk.pop()
                # fee for exponent is dependent on its bytes
                # calc n bytes to represent exponent
                nbytes = len(utils.encode_int(exponent))
                expfee = nbytes * opcodes.GEXPONENTBYTE
                if compustate.gas < expfee:
                    compustate.gas = 0
                    return vm_exception('OOG EXPONENT')
                compustate.gas -= expfee
                stk.append(pow(base, exponent, TT256))
            elif op == 'SIGNEXTEND':
                s0, s1 = stk.pop(), stk.pop()
                if s0 <= 31:
                    testbit = s0 * 8 + 7
                    if s1 & (1 << testbit):
                        stk.append(s1 | (TT256 - (1 << testbit)))
                    else:
                        stk.append(s1 & ((1 << testbit) - 1))
                else:
                    stk.append(s1)
        elif opcode < 0x20:
            if op == 'LT':
                stk.append(1 if stk.pop() < stk.pop() else 0)
            elif op == 'GT':
                stk.append(1 if stk.pop() > stk.pop() else 0)
            elif op == 'SLT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 < s1 else 0)
            elif op == 'SGT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 > s1 else 0)
            elif op == 'EQ':
                stk.append(1 if stk.pop() == stk.pop() else 0)
            elif op == 'ISZERO':
                stk.append(0 if stk.pop() else 1)
            elif op == 'AND':
                stk.append(stk.pop() & stk.pop())
            elif op == 'OR':
                stk.append(stk.pop() | stk.pop())
            elif op == 'XOR':
                stk.append(stk.pop() ^ stk.pop())
            elif op == 'NOT':
                stk.append(TT256M1 - stk.pop())
            elif op == 'BYTE':
                s0, s1 = stk.pop(), stk.pop()
                if s0 >= 32:
                    stk.append(0)
                else:
                    stk.append((s1 // 256**(31 - s0)) % 256)
        elif opcode < 0x40:
            if op == 'SHA3':
                s0, s1 = stk.pop(), stk.pop()
                compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32)
                if compustate.gas < 0:
                    return vm_exception('OOG PAYING FOR SHA3')
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0:s0 + s1]))
                stk.append(utils.big_endian_to_int(utils.sha3(data)))
            elif op == 'ADDRESS':
                stk.append(utils.coerce_to_int(msg.to))
            elif op == 'BALANCE':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(ext.get_balance(addr))
            elif op == 'ORIGIN':
                stk.append(utils.coerce_to_int(ext.tx_origin))
            elif op == 'CALLER':
                stk.append(utils.coerce_to_int(msg.sender))
            elif op == 'CALLVALUE':
                stk.append(msg.value)
            elif op == 'CALLDATALOAD':
                stk.append(msg.data.extract32(stk.pop()))
            elif op == 'CALLDATASIZE':
                stk.append(msg.data.size)
            elif op == 'CALLDATACOPY':
                mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                msg.data.extract_copy(mem, mstart, dstart, size)
            elif op == 'CODESIZE':
                stk.append(len(processed_code))
            elif op == 'CODECOPY':
                start, s1, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s1 + i < len(processed_code):
                        mem[start + i] = processed_code[s1 + i][4]
                    else:
                        mem[start + i] = 0
            elif op == 'GASPRICE':
                stk.append(ext.tx_gasprice)
            elif op == 'EXTCODESIZE':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(len(ext.get_code(addr) or b''))
            elif op == 'EXTCODECOPY':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                start, s2, size = stk.pop(), stk.pop(), stk.pop()
                extcode = ext.get_code(addr) or b''
                assert isinstance(extcode, str)
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s2 + i < len(extcode):
                        mem[start + i] = ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
        elif opcode < 0x50:
            if op == 'BLOCKHASH':
                stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop())))
            elif op == 'COINBASE':
                stk.append(utils.big_endian_to_int(ext.block_coinbase))
            elif op == 'TIMESTAMP':
                stk.append(ext.block_timestamp)
            elif op == 'NUMBER':
                stk.append(ext.block_number)
            elif op == 'DIFFICULTY':
                stk.append(ext.block_difficulty)
            elif op == 'GASLIMIT':
                stk.append(ext.block_gas_limit)
        elif opcode < 0x60:
            if op == 'POP':
                stk.pop()
            elif op == 'MLOAD':
                s0 = stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0:s0 + 32]))
                stk.append(utils.big_endian_to_int(data))
            elif op == 'MSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                v = s1
                for i in range(31, -1, -1):
                    mem[s0 + i] = v % 256
                    v //= 256
            elif op == 'MSTORE8':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 1):
                    return vm_exception('OOG EXTENDING MEMORY')
                mem[s0] = s1 % 256
            elif op == 'SLOAD':
                stk.append(ext.get_storage_data(msg.to, stk.pop()))
            elif op == 'SSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if ext.get_storage_data(msg.to, s0):
                    gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                    refund = 0 if s1 else opcodes.GSTORAGEREFUND
                else:
                    gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                    refund = 0
                if compustate.gas < gascost:
                    return vm_exception('OUT OF GAS')
                compustate.gas -= gascost
                ext.add_refund(
                    refund)  # adds neg gascost as a refund if below zero
                ext.set_storage_data(msg.to, s0, s1)
            elif op == 'JUMP':
                compustate.pc = stk.pop()
                opnew = processed_code[compustate.pc][0] if \
                    compustate.pc < len(processed_code) else 'STOP'
                if opnew != 'JUMPDEST':
                    return vm_exception('BAD JUMPDEST')
            elif op == 'JUMPI':
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    opnew = processed_code[compustate.pc][0] if \
                        compustate.pc < len(processed_code) else 'STOP'
                    if opnew != 'JUMPDEST':
                        return vm_exception('BAD JUMPDEST')
            elif op == 'PC':
                stk.append(compustate.pc - 1)
            elif op == 'MSIZE':
                stk.append(len(mem))
            elif op == 'GAS':
                stk.append(compustate.gas)  # AFTER subtracting cost 1
        elif op[:4] == 'PUSH':
            pushnum = int(op[4:])
            compustate.pc += pushnum
            stk.append(pushval)
        elif op[:3] == 'DUP':
            depth = int(op[3:])
            stk.append(stk[-depth])
        elif op[:4] == 'SWAP':
            depth = int(op[4:])
            temp = stk[-depth - 1]
            stk[-depth - 1] = stk[-1]
            stk[-1] = temp

        elif op[:3] == 'LOG':
            """
            0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
            a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                    MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
            b. Logs are kept track of during tx execution exactly the same way as suicides
               (except as an ordered list, not a set).
               Each log is in the form [address, [topic1, ... ], data] where:
               * address is what the ADDRESS opcode would output
               * data is mem[MEMSTART: MEMSTART + MEMSZ]
               * topics are as provided by the opcode
            c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
            """
            depth = int(op[3:])
            mstart, msz = stk.pop(), stk.pop()
            topics = [stk.pop() for x in range(depth)]
            compustate.gas -= msz * opcodes.GLOGBYTE
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            data = b''.join(map(ascii_chr, mem[mstart:mstart + msz]))
            ext.log(msg.to, topics, data)
            log_log.trace('LOG',
                          to=msg.to,
                          topics=topics,
                          data=list(map(ord, data)))
            # print('LOG', msg.to, topics, list(map(ord, data)))

        elif op == 'CREATE':
            value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                cd = CallData(mem, mstart, msz)
                create_msg = Message(msg.to, b'', value, compustate.gas, cd,
                                     msg.depth + 1)
                o, gas, addr = ext.create(create_msg)
                if o:
                    stk.append(utils.coerce_to_int(addr))
                    compustate.gas = gas
                else:
                    stk.append(0)
                    compustate.gas = 0
            else:
                stk.append(0)
        elif op == 'CALL':
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            to = utils.encode_int(to)
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to,
                                   to,
                                   value,
                                   submsg_gas,
                                   cd,
                                   msg.depth + 1,
                                   code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'CALLCODE':
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to,
                                   msg.to,
                                   value,
                                   submsg_gas,
                                   cd,
                                   msg.depth + 1,
                                   code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'RETURN':
            s0, s1 = stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, s0, s1):
                return vm_exception('OOG EXTENDING MEMORY')
            return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1])
        elif op == 'SUICIDE':
            to = utils.encode_int(stk.pop())
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            xfer = ext.get_balance(msg.to)
            ext.set_balance(msg.to, 0)
            ext.set_balance(to, ext.get_balance(to) + xfer)
            ext.add_suicide(msg.to)
            # print('suiciding %s %s %d' % (msg.to, to, xfer))
            return 1, compustate.gas, []

        # this is slow!
        if verify_stack_after_op:
            for a in stk:
                assert is_numeric(a)
                assert a >= 0 and a < 2**256, (a, op, stk)
Exemple #27
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code
        # print(processed_code.keys(), code)

    codelen = len(code)

    s = time.time()
    steps = 0
    _prevop = None  # for trace only

    while compustate.pc in processed_code:
        ops, minstack, maxstack, totgas, nextpos = processed_code[
            compustate.pc]

        if len(compustate.stack) < minstack:
            return vm_exception('INSUFFICIENT STACK')
        if len(compustate.stack) > maxstack:
            return vm_exception('STACK SIZE LIMIT EXCEEDED')
        if totgas > compustate.gas:
            return vm_exception('OUT OF GAS %d %d' % (totgas, compustate.gas))
        jumped = False

        compustate.gas -= totgas
        compustate.pc = nextpos

        # Invalid operation; can only come at the end of a chunk
        if ops[-1][0] == 'INVALID':
            return vm_exception('INVALID OP', opcode=ops[-1][1])

        for op, opcode, pushval in ops:

            if trace_vm:
                """
                This diverges from normal logging, as we use the logging namespace
                only to decide which features get logged in 'eth.vm.op'
                i.e. tracing can not be activated by activating a sub
                like 'eth.vm.op.stack'
                """
                trace_data = {}
                trace_data['stack'] = list(
                    map(to_string, list(compustate.stack)))
                if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL',
                               'CALLCODE', 'CREATE', 'CALLDATACOPY',
                               'CODECOPY', 'EXTCODECOPY'):
                    if len(compustate.memory) < 1024:
                        trace_data['memory'] = \
                            ''.join([encode_hex(ascii_chr(x)) for x
                                     in compustate.memory])
                    else:
                        trace_data['sha3memory'] = \
                            encode_hex(utils.sha3(b''.join([ascii_chr(x) for
                                                            x in compustate.memory])))
                if _prevop in ('SSTORE', ) or steps == 0:
                    trace_data['storage'] = ext.log_storage(msg.to)
                trace_data['gas'] = to_string(compustate.gas + totgas)
                trace_data['inst'] = opcode
                trace_data['pc'] = to_string(compustate.pc - 1)
                if steps == 0:
                    trace_data['depth'] = msg.depth
                    trace_data['address'] = msg.to
                trace_data['steps'] = steps
                trace_data['depth'] = msg.depth
                if op[:4] == 'PUSH':
                    trace_data['pushvalue'] = pushval
                log_vm_op.trace('vm', op=op, **trace_data)
                steps += 1
                _prevop = op

            # Valid operations
            # Pushes first because they are very frequent
            if 0x60 <= opcode <= 0x7f:
                # compustate.pc += opcode - 0x5f # Move 1 byte forward for
                # 0x60, up to 32 bytes for 0x7f
                stk.append(pushval)
            elif opcode < 0x10:
                if op == 'STOP':
                    return peaceful_exit('STOP', compustate.gas, [])
                elif op == 'ADD':
                    stk.append((stk.pop() + stk.pop()) & TT256M1)
                elif op == 'SUB':
                    stk.append((stk.pop() - stk.pop()) & TT256M1)
                elif op == 'MUL':
                    stk.append((stk.pop() * stk.pop()) & TT256M1)
                elif op == 'DIV':
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 // s1)
                elif op == 'MOD':
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 % s1)
                elif op == 'SDIV':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                                  (-1 if s0 * s1 < 0 else 1))
                               & TT256M1)
                elif op == 'SMOD':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                                  (-1 if s0 < 0 else 1))
                               & TT256M1)
                elif op == 'ADDMOD':
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 + s1) % s2 if s2 else 0)
                elif op == 'MULMOD':
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 * s1) % s2 if s2 else 0)
                elif op == 'EXP':
                    base, exponent = stk.pop(), stk.pop()
                    # fee for exponent is dependent on its bytes
                    # calc n bytes to represent exponent
                    nbytes = len(utils.encode_int(exponent))
                    expfee = nbytes * opcodes.GEXPONENTBYTE
                    if ext.post_clearing_hardfork():
                        expfee += opcodes.EXP_SUPPLEMENTAL_GAS * nbytes
                    if compustate.gas < expfee:
                        compustate.gas = 0
                        return vm_exception('OOG EXPONENT')
                    compustate.gas -= expfee
                    stk.append(pow(base, exponent, TT256))
                elif op == 'SIGNEXTEND':
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 <= 31:
                        testbit = s0 * 8 + 7
                        if s1 & (1 << testbit):
                            stk.append(s1 | (TT256 - (1 << testbit)))
                        else:
                            stk.append(s1 & ((1 << testbit) - 1))
                    else:
                        stk.append(s1)
            elif opcode < 0x20:
                if op == 'LT':
                    stk.append(1 if stk.pop() < stk.pop() else 0)
                elif op == 'GT':
                    stk.append(1 if stk.pop() > stk.pop() else 0)
                elif op == 'SLT':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 < s1 else 0)
                elif op == 'SGT':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 > s1 else 0)
                elif op == 'EQ':
                    stk.append(1 if stk.pop() == stk.pop() else 0)
                elif op == 'ISZERO':
                    stk.append(0 if stk.pop() else 1)
                elif op == 'AND':
                    stk.append(stk.pop() & stk.pop())
                elif op == 'OR':
                    stk.append(stk.pop() | stk.pop())
                elif op == 'XOR':
                    stk.append(stk.pop() ^ stk.pop())
                elif op == 'NOT':
                    stk.append(TT256M1 - stk.pop())
                elif op == 'BYTE':
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 >= 32:
                        stk.append(0)
                    else:
                        stk.append((s1 // 256**(31 - s0)) % 256)
            elif opcode < 0x40:
                if op == 'SHA3':
                    s0, s1 = stk.pop(), stk.pop()
                    compustate.gas -= opcodes.GSHA3WORD * \
                        (utils.ceil32(s1) // 32)
                    if compustate.gas < 0:
                        return vm_exception('OOG PAYING FOR SHA3')
                    if not mem_extend(mem, compustate, op, s0, s1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    data = bytearray_to_bytestr(mem[s0:s0 + s1])
                    stk.append(utils.big_endian_to_int(utils.sha3(data)))
                elif op == 'ADDRESS':
                    stk.append(utils.coerce_to_int(msg.to))
                elif op == 'BALANCE':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.BALANCE_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(ext.get_balance(addr))
                elif op == 'ORIGIN':
                    stk.append(utils.coerce_to_int(ext.tx_origin))
                elif op == 'CALLER':
                    stk.append(utils.coerce_to_int(msg.sender))
                elif op == 'CALLVALUE':
                    stk.append(msg.value)
                elif op == 'CALLDATALOAD':
                    stk.append(msg.data.extract32(stk.pop()))
                elif op == 'CALLDATASIZE':
                    stk.append(msg.data.size)
                elif op == 'CALLDATACOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    msg.data.extract_copy(mem, mstart, dstart, size)
                elif op == 'CODESIZE':
                    stk.append(len(code))
                elif op == 'CODECOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if dstart + i < len(code):
                            mem[mstart + i] = utils.safe_ord(code[dstart + i])
                        else:
                            mem[mstart + i] = 0
                elif op == 'RETURNDATACOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    if dstart + size > len(compustate.last_returned):
                        return vm_exception('RETURNDATACOPY out of range')
                    mem[mstart:mstart + size] = compustate.last_returned
                elif op == 'RETURNDATASIZE':
                    stk.append(len(compustate.last_returned))
                elif op == 'GASPRICE':
                    stk.append(ext.tx_gasprice)
                elif op == 'EXTCODESIZE':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(len(ext.get_code(addr) or b''))
                elif op == 'EXTCODECOPY':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    start, s2, size = stk.pop(), stk.pop(), stk.pop()
                    extcode = ext.get_code(addr) or b''
                    assert utils.is_string(extcode)
                    if not mem_extend(mem, compustate, op, start, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if s2 + i < len(extcode):
                            mem[start + i] = utils.safe_ord(extcode[s2 + i])
                        else:
                            mem[start + i] = 0
            elif opcode < 0x50:
                if op == 'BLOCKHASH':
                    if ext.post_metropolis_hardfork() and False:
                        bh_addr = ext.blockhash_store
                        stk.append(ext.get_storage_data(bh_addr, stk.pop()))
                    else:
                        stk.append(
                            utils.big_endian_to_int(ext.block_hash(stk.pop())))
                elif op == 'COINBASE':
                    stk.append(utils.big_endian_to_int(ext.block_coinbase))
                elif op == 'TIMESTAMP':
                    stk.append(ext.block_timestamp)
                elif op == 'NUMBER':
                    stk.append(ext.block_number)
                elif op == 'DIFFICULTY':
                    stk.append(ext.block_difficulty)
                elif op == 'GASLIMIT':
                    stk.append(ext.block_gas_limit)
            elif opcode < 0x60:
                if op == 'POP':
                    stk.pop()
                elif op == 'MLOAD':
                    s0 = stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    stk.append(utils.bytes_to_int(mem[s0:s0 + 32]))
                elif op == 'MSTORE':
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0:s0 + 32] = utils.encode_int32(s1)
                elif op == 'MSTORE8':
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0] = s1 % 256
                elif op == 'SLOAD':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.SLOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    stk.append(ext.get_storage_data(msg.to, stk.pop()))
                elif op == 'SSTORE':
                    s0, s1 = stk.pop(), stk.pop()
                    if msg.static:
                        return vm_exception(
                            'Cannot SSTORE inside a static context')
                    if ext.get_storage_data(msg.to, s0):
                        gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                        refund = 0 if s1 else opcodes.GSTORAGEREFUND
                    else:
                        gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                        refund = 0
                    if compustate.gas < gascost:
                        return vm_exception('OUT OF GAS')
                    compustate.gas -= gascost
                    # adds neg gascost as a refund if below zero
                    ext.add_refund(refund)
                    ext.set_storage_data(msg.to, s0, s1)
                elif op == 'JUMP':
                    compustate.pc = stk.pop()
                    opnew = code[
                        compustate.pc] if compustate.pc < codelen else 0
                    jumped = True
                    if opnew != JUMPDEST:
                        return vm_exception('BAD JUMPDEST')
                elif op == 'JUMPI':
                    s0, s1 = stk.pop(), stk.pop()
                    if s1:
                        compustate.pc = s0
                        opnew = code[
                            compustate.pc] if compustate.pc < codelen else 0
                        jumped = True
                        if opnew != JUMPDEST:
                            return vm_exception('BAD JUMPDEST')
                elif op == 'PC':
                    stk.append(compustate.pc - 1)
                elif op == 'MSIZE':
                    stk.append(len(mem))
                elif op == 'GAS':
                    stk.append(compustate.gas)  # AFTER subtracting cost 1
            elif op[:3] == 'DUP':
                # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for
                # 0x8f
                stk.append(stk[0x7f - opcode])
            elif op[:4] == 'SWAP':
                # 0x8e - opcode is a negative number, -2 for 0x90 ... -17 for
                # 0x9f
                temp = stk[0x8e - opcode]
                stk[0x8e - opcode] = stk[-1]
                stk[-1] = temp

            elif op[:3] == 'LOG':
                """
                0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
                a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                        MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
                b. Logs are kept track of during tx execution exactly the same way as suicides
                   (except as an ordered list, not a set).
                   Each log is in the form [address, [topic1, ... ], data] where:
                   * address is what the ADDRESS opcode would output
                   * data is mem[MEMSTART: MEMSTART + MEMSZ]
                   * topics are as provided by the opcode
                c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
                """
                depth = int(op[3:])
                mstart, msz = stk.pop(), stk.pop()
                topics = [stk.pop() for x in range(depth)]
                compustate.gas -= msz * opcodes.GLOGBYTE
                if msg.static:
                    return vm_exception('Cannot LOG inside a static context')
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = bytearray_to_bytestr(mem[mstart:mstart + msz])
                ext.log(msg.to, topics, data)
                log_log.trace('LOG',
                              to=msg.to,
                              topics=topics,
                              data=list(map(utils.safe_ord, data)))
                # print('LOG', msg.to, topics, list(map(ord, data)))

            elif op == 'CREATE':
                value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                if msg.static:
                    return vm_exception(
                        'Cannot CREATE inside a static context')
                if ext.get_balance(msg.to) >= value and msg.depth < MAX_DEPTH:
                    cd = CallData(mem, mstart, msz)
                    ingas = compustate.gas
                    if ext.post_anti_dos_hardfork():
                        ingas = all_but_1n(ingas,
                                           opcodes.CALL_CHILD_LIMIT_DENOM)
                    create_msg = Message(msg.to, b'', value, ingas, cd,
                                         msg.depth + 1)
                    o, gas, addr = ext.create(create_msg)
                    if o:
                        stk.append(utils.coerce_to_int(addr))
                    else:
                        stk.append(0)
                    compustate.gas = compustate.gas - ingas + gas
                else:
                    stk.append(0)
            elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'):
                # Pull arguments from the stack
                if op in ('CALL', 'CALLCODE'):
                    gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                        stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                else:
                    gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                        stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                    value = 0
                # Static context prohibition
                if msg.static and value > 0:
                    return vm_exception(
                        'Cannot make a non-zero-value call inside a static context'
                    )
                # Expand memory
                if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                        not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                    return vm_exception('OOG EXTENDING MEMORY')
                to = utils.int_to_addr(to)
                # Extra gas costs based on hard fork-dependent factors
                extra_gas = (not ext.account_exists(to)) * (op == 'CALL') * (value > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT + \
                    (value > 0) * opcodes.GCALLVALUETRANSFER + \
                    ext.post_anti_dos_hardfork() * opcodes.CALL_SUPPLEMENTAL_GAS
                # Compute child gas limit
                if ext.post_anti_dos_hardfork():
                    if compustate.gas < extra_gas:
                        return vm_exception('OUT OF GAS', needed=extra_gas)
                    gas = min(
                        gas,
                        all_but_1n(compustate.gas - extra_gas,
                                   opcodes.CALL_CHILD_LIMIT_DENOM))
                else:
                    if compustate.gas < gas + extra_gas:
                        return vm_exception('OUT OF GAS',
                                            needed=gas + extra_gas)
                submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
                # Verify that there is sufficient balance and depth
                if ext.get_balance(msg.to) < value or msg.depth >= MAX_DEPTH:
                    compustate.gas -= (gas + extra_gas - submsg_gas)
                    stk.append(0)
                else:
                    # Subtract gas from parent
                    compustate.gas -= (gas + extra_gas)
                    assert compustate.gas >= 0
                    cd = CallData(mem, meminstart, meminsz)
                    # Generate the message
                    if op == 'CALL':
                        call_msg = Message(msg.to,
                                           to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=msg.static)
                    elif ext.post_homestead_hardfork(
                    ) and op == 'DELEGATECALL':
                        call_msg = Message(msg.sender,
                                           msg.to,
                                           msg.value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           transfers_value=False,
                                           static=msg.static)
                    elif op == 'DELEGATECALL':
                        return vm_exception('OPCODE INACTIVE')
                    elif op == 'CALLCODE':
                        call_msg = Message(msg.to,
                                           msg.to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=msg.static)
                    elif op == 'STATICCALL':
                        call_msg = Message(msg.to,
                                           to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=True)
                    else:
                        raise Exception("Lolwut")
                    # Get result
                    result, gas, data = ext.msg(call_msg)
                    if result == 0:
                        stk.append(0)
                    else:
                        stk.append(1)
                    # Set output memory
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
                    compustate.gas += gas
                    compustate.last_returned = bytearray(data)
            elif op == 'RETURN':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1])
            elif op == 'REVERT':
                if not ext.post_metropolis_hardfork():
                    return vm_exception('Opcode not yet enabled')
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return revert(compustate.gas, mem[s0:s0 + s1])
            elif op == 'SUICIDE':
                if msg.static:
                    return vm_exception(
                        'Cannot SUICIDE inside a static context')
                to = utils.encode_int(stk.pop())
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                xfer = ext.get_balance(msg.to)
                if ext.post_anti_dos_hardfork():
                    extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \
                        (not ext.account_exists(
                            to)) * (xfer > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT
                    if not eat_gas(compustate, extra_gas):
                        return vm_exception("OUT OF GAS")
                ext.set_balance(to, ext.get_balance(to) + xfer)
                ext.set_balance(msg.to, 0)
                ext.add_suicide(msg.to)
                log_msg.debug('SUICIDING',
                              addr=utils.checksum_encode(msg.to),
                              to=utils.checksum_encode(to),
                              xferring=xfer)
                return 1, compustate.gas, []

            # assert utils.is_numeric(compustate.gas)
            # this is slow!
            # for a in stk:
            #     assert is_numeric(a), (op, stk)
            #     assert a >= 0 and a < 2**256, (a, op, stk)
        # if not jumped:
        #    assert compustate.pc == nextpos
        #    compustate.pc = nextpos
    if compustate.pc >= codelen:
        return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])
    return vm_exception('INVALID JUMP')
Exemple #28
0
def encode_single(typ, arg):  # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements,too-many-locals
    """ Encode `arg` as `typ`.

    `arg` will be encoded in a best effort manner, were necessary the function
    will try to correctly define the underlying binary representation (ie.
    decoding a hex-encoded address/hash).

    Args:
        typ (Tuple[(str, int, list)]): A 3-tuple defining the `arg` type.

            The first element defines the type name.
            The second element defines the type length in bits.
            The third element defines if it's an array type.

            Together the first and second defines the elementary type, the third
            element must be present but is ignored.

            Valid type names are:
                - uint
                - int
                - bool
                - ufixed
                - fixed
                - string
                - bytes
                - hash
                - address

        arg (object): The object to be encoded, it must be a python object
            compatible with the `typ`.

    Raises:
        ValueError: when an invalid `typ` is supplied.
        ValueOutOfBounds: when `arg` cannot be encoded as `typ` because of the
            binary contraints.

    Note:
        This function don't work with array types, for that use the `enc`
        function.
    """
    base, sub, _ = typ

    if base == 'uint':
        sub = int(sub)

        if not (0 < sub <= 256 and sub % 8 == 0):
            raise ValueError(
                'invalid unsigned integer bit length {}'.format(sub))

        try:
            i = decint(arg, signed=False)
        except EncodingError:
            # arg is larger than 2**256
            raise ValueOutOfBounds(repr(arg))

        if not 0 <= i < 2 ** sub:
            raise ValueOutOfBounds(repr(arg))

        value_encoded = int_to_big_endian(i)
        return zpad(value_encoded, 32)

    if base == 'int':
        sub = int(sub)
        bits = sub - 1

        if not (0 < sub <= 256 and sub % 8 == 0):
            raise ValueError('invalid integer bit length {}'.format(sub))

        try:
            i = decint(arg, signed=True)
        except EncodingError:
            # arg is larger than 2**255
            raise ValueOutOfBounds(repr(arg))

        if not -2 ** bits <= i < 2 ** bits:
            raise ValueOutOfBounds(repr(arg))

        value = i % 2 ** 256  # convert negative to "equivalent" positive
        value_encoded = int_to_big_endian(value)
        return zpad(value_encoded, 32)

    if base == 'bool':
        if arg is True:
            value_encoded = int_to_big_endian(1)
        elif arg is False:
            value_encoded = int_to_big_endian(0)
        else:
            raise ValueError('%r is not bool' % arg)

        return zpad(value_encoded, 32)

    if base == 'ufixed':
        sub = str(sub)  # pylint: disable=redefined-variable-type

        high_str, low_str = sub.split('x')
        high = int(high_str)
        low = int(low_str)

        if not (0 < high + low <= 256 and high % 8 == 0 and low % 8 == 0):
            raise ValueError('invalid unsigned fixed length {}'.format(sub))

        if not 0 <= arg < 2 ** high:
            raise ValueOutOfBounds(repr(arg))

        float_point = arg * 2 ** low
        fixed_point = int(float_point)
        return zpad(int_to_big_endian(fixed_point), 32)

    if base == 'fixed':
        sub = str(sub)  # pylint: disable=redefined-variable-type

        high_str, low_str = sub.split('x')
        high = int(high_str)
        low = int(low_str)
        bits = high - 1

        if not (0 < high + low <= 256 and high % 8 == 0 and low % 8 == 0):
            raise ValueError('invalid unsigned fixed length {}'.format(sub))

        if not -2 ** bits <= arg < 2 ** bits:
            raise ValueOutOfBounds(repr(arg))

        float_point = arg * 2 ** low
        fixed_point = int(float_point)
        value = fixed_point % 2 ** 256
        return zpad(int_to_big_endian(value), 32)

    # Decimals
    if base == 'decimal':
        val_to_encode = int(arg * 10**int(sub))
        return zpad(encode_int(val_to_encode % 2**256), 32)

    if base == 'string':
        if isinstance(arg, utils.unicode):
            arg = arg.encode('utf8')
        else:
            try:
                arg.decode('utf8')
            except UnicodeDecodeError:
                raise ValueError('string must be utf8 encoded')

        if len(sub):  # fixed length
            if not 0 <= len(arg) <= int(sub):
                raise ValueError('invalid string length {}'.format(sub))

            if not 0 <= int(sub) <= 32:
                raise ValueError('invalid string length {}'.format(sub))

            return rzpad(arg, 32)

        if not 0 <= len(arg) < TT256:
            raise Exception('Integer invalid or out of range: %r' % arg)

        length_encoded = zpad(int_to_big_endian(len(arg)), 32)
        value_encoded = rzpad(arg, utils.ceil32(len(arg)))

        return length_encoded + value_encoded

    if base == 'bytes':
        if not is_string(arg):
            if isinstance(arg, str):
                arg = bytes(arg, 'utf8')
            else:
                raise EncodingError('Expecting string: %r' % arg)

        arg = utils.to_string(arg)  # py2: force unicode into str

        if len(sub):  # fixed length
            if not 0 <= len(arg) <= int(sub):
                raise ValueError('string must be utf8 encoded')

            if not 0 <= int(sub) <= 32:
                raise ValueError('string must be utf8 encoded')

            return rzpad(arg, 32)

        if not 0 <= len(arg) < TT256:
            raise Exception('Integer invalid or out of range: %r' % arg)

        length_encoded = zpad(int_to_big_endian(len(arg)), 32)
        value_encoded = rzpad(arg, utils.ceil32(len(arg)))

        return length_encoded + value_encoded

    if base == 'hash':
        if not (int(sub) and int(sub) <= 32):
            raise EncodingError('too long: %r' % arg)

        if is_numeric(arg):
            return zpad(encode_int(arg), 32)

        if len(arg) == int(sub):
            return zpad(arg, 32)

        if len(arg) == int(sub) * 2:
            return zpad(decode_hex(arg), 32)

        raise EncodingError('Could not parse hash: %r' % arg)

    if base == 'address':
        assert sub == ''

        if is_numeric(arg):
            return zpad(encode_int(arg), 32)

        if len(arg) == 20:
            return zpad(arg, 32)

        if len(arg) == 40:
            return zpad(decode_hex(arg), 32)

        if len(arg) == 42 and arg[:2] == '0x':
            return zpad(decode_hex(arg[2:]), 32)

        raise EncodingError('Could not parse address: %r' % arg)
    raise EncodingError('Unhandled type: %r %r' % (base, sub))
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    processed_code = preprocess_code(code)

    s = time.time()
    op = None
    steps = 0
    _prevop = None  # for trace only

    while True:
        # print('op: ', op, time.time() - s)
        # s = time.time()
        # stack size limit error
        if compustate.pc not in processed_code:
            return vm_exception('INVALID START POINT')

        _data = processed_code[compustate.pc]
        gas, min_stack, max_stack, compustate.pc = _data[:4]
        ops = _data[4:]

        # out of gas error
        if gas > compustate.gas:
            return vm_exception('OUT OF GAS')

        # insufficient stack error
        if not (min_stack <= len(compustate.stack) <= max_stack):
            return vm_exception('INCOMPATIBLE STACK LENGTH',
                                min_stack=min_stack,
                                have=len(compustate.stack),
                                max_stack=max_stack)

        # Apply operation
        compustate.gas -= gas

        for op in ops:

            if trace_vm:
                """
                This diverges from normal logging, as we use the logging namespace
                only to decide which features get logged in 'eth.vm.op'
                i.e. tracing can not be activated by activating a sub
                like 'eth.vm.op.stack'
                """
                trace_data = {}
                trace_data['stack'] = list(
                    map(to_string, list(compustate.stack)))
                if _prevop in (op_MLOAD, op_MSTORE, op_MSTORE8, op_SHA3,
                               op_CALL, op_CALLCODE, op_CREATE,
                               op_CALLDATACOPY, op_CODECOPY, op_EXTCODECOPY):
                    if len(compustate.memory) < 1024:
                        trace_data['memory'] = \
                            b''.join([encode_hex(ascii_chr(x)) for x
                                      in compustate.memory])
                    else:
                        trace_data['sha3memory'] = \
                            encode_hex(utils.sha3(''.join([ascii_chr(x) for
                                                           x in compustate.memory])))
                if _prevop in (op_SSTORE, op_SLOAD) or steps == 0:
                    trace_data['storage'] = ext.log_storage(msg.to)
                # trace_data['gas'] = to_string(compustate.gas + fee)
                trace_data['inst'] = op
                trace_data['pc'] = to_string(compustate.pc - 1)
                if steps == 0:
                    trace_data['depth'] = msg.depth
                    trace_data['address'] = msg.to
                trace_data['op'] = op
                trace_data['steps'] = steps
                # if op[:4] == 'PUSH':
                #     trace_data['pushvalue'] = pushval
                log_vm_op.trace('vm', **trace_data)
                steps += 1
                _prevop = op

            # Invalid operation
            if op == INVALID:
                return vm_exception('INVALID OP', opcode=op)

            # Valid operations
            if op < 0x10:
                if op == op_STOP:
                    return peaceful_exit('STOP', compustate.gas, [])
                elif op == op_ADD:
                    stk.append((stk.pop() + stk.pop()) & TT256M1)
                elif op == op_SUB:
                    stk.append((stk.pop() - stk.pop()) & TT256M1)
                elif op == op_MUL:
                    stk.append((stk.pop() * stk.pop()) & TT256M1)
                elif op == op_DIV:
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 // s1)
                elif op == op_MOD:
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 % s1)
                elif op == op_SDIV:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                                  (-1 if s0 * s1 < 0 else 1))
                               & TT256M1)
                elif op == op_SMOD:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                                  (-1 if s0 < 0 else 1))
                               & TT256M1)
                elif op == op_ADDMOD:
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 + s1) % s2 if s2 else 0)
                elif op == op_MULMOD:
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 * s1) % s2 if s2 else 0)
                elif op == op_EXP:
                    base, exponent = stk.pop(), stk.pop()
                    # fee for exponent is dependent on its bytes
                    # calc n bytes to represent exponent
                    nbytes = len(utils.encode_int(exponent))
                    expfee = nbytes * opcodes.GEXPONENTBYTE
                    if compustate.gas < expfee:
                        compustate.gas = 0
                        return vm_exception('OOG EXPONENT')
                    compustate.gas -= expfee
                    stk.append(pow(base, exponent, TT256))
                elif op == op_SIGNEXTEND:
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 <= 31:
                        testbit = s0 * 8 + 7
                        if s1 & (1 << testbit):
                            stk.append(s1 | (TT256 - (1 << testbit)))
                        else:
                            stk.append(s1 & ((1 << testbit) - 1))
                    else:
                        stk.append(s1)
            elif op < 0x20:
                if op == op_LT:
                    stk.append(1 if stk.pop() < stk.pop() else 0)
                elif op == op_GT:
                    stk.append(1 if stk.pop() > stk.pop() else 0)
                elif op == op_SLT:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 < s1 else 0)
                elif op == op_SGT:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 > s1 else 0)
                elif op == op_EQ:
                    stk.append(1 if stk.pop() == stk.pop() else 0)
                elif op == op_ISZERO:
                    stk.append(0 if stk.pop() else 1)
                elif op == op_AND:
                    stk.append(stk.pop() & stk.pop())
                elif op == op_OR:
                    stk.append(stk.pop() | stk.pop())
                elif op == op_XOR:
                    stk.append(stk.pop() ^ stk.pop())
                elif op == op_NOT:
                    stk.append(TT256M1 - stk.pop())
                elif op == op_BYTE:
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 >= 32:
                        stk.append(0)
                    else:
                        stk.append((s1 // 256**(31 - s0)) % 256)
            elif op < 0x40:
                if op == op_SHA3:
                    s0, s1 = stk.pop(), stk.pop()
                    compustate.gas -= opcodes.GSHA3WORD * \
                        (utils.ceil32(s1) // 32)
                    if compustate.gas < 0:
                        return vm_exception('OOG PAYING FOR SHA3')
                    if not mem_extend(mem, compustate, op, s0, s1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    data = b''.join(map(ascii_chr, mem[s0:s0 + s1]))
                    stk.append(utils.big_endian_to_int(utils.sha3(data)))
                elif op == op_ADDRESS:
                    stk.append(utils.coerce_to_int(msg.to))
                elif op == op_BALANCE:
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(ext.get_balance(addr))
                elif op == op_ORIGIN:
                    stk.append(utils.coerce_to_int(ext.tx_origin))
                elif op == op_CALLER:
                    stk.append(utils.coerce_to_int(msg.sender))
                elif op == op_CALLVALUE:
                    stk.append(msg.value)
                elif op == op_CALLDATALOAD:
                    stk.append(msg.data.extract32(stk.pop()))
                elif op == op_CALLDATASIZE:
                    stk.append(msg.data.size)
                elif op == op_CALLDATACOPY:
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    msg.data.extract_copy(mem, mstart, dstart, size)
                elif op == op_CODESIZE:
                    stk.append(len(code))
                elif op == op_CODECOPY:
                    start, s1, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, start, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if s1 + i < len(code):
                            mem[start + i] = utils.safe_ord(code[s1 + i])
                        else:
                            mem[start + i] = 0
                elif op == op_GASPRICE:
                    stk.append(ext.tx_gasprice)
                elif op == op_EXTCODESIZE:
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(len(ext.get_code(addr) or b''))
                elif op == op_EXTCODECOPY:
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    start, s2, size = stk.pop(), stk.pop(), stk.pop()
                    extcode = ext.get_code(addr) or b''
                    assert utils.is_string(extcode)
                    if not mem_extend(mem, compustate, op, start, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if s2 + i < len(extcode):
                            mem[start + i] = utils.safe_ord(extcode[s2 + i])
                        else:
                            mem[start + i] = 0
            elif op < 0x50:
                if op == op_BLOCKHASH:
                    stk.append(
                        utils.big_endian_to_int(ext.block_hash(stk.pop())))
                elif op == op_COINBASE:
                    stk.append(utils.big_endian_to_int(ext.block_coinbase))
                elif op == op_TIMESTAMP:
                    stk.append(ext.block_timestamp)
                elif op == op_NUMBER:
                    stk.append(ext.block_number)
                elif op == op_DIFFICULTY:
                    stk.append(ext.block_difficulty)
                elif op == op_GASLIMIT:
                    stk.append(ext.block_gas_limit)
            elif op < 0x60:
                if op == op_POP:
                    stk.pop()
                elif op == op_MLOAD:
                    s0 = stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    data = 0
                    for c in mem[s0:s0 + 32]:
                        data = (data << 8) + c
                    stk.append(data)
                elif op == op_MSTORE:
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    v = s1
                    for i in range(31, -1, -1):
                        mem[s0 + i] = v % 256
                        v //= 256
                elif op == op_MSTORE8:
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0] = s1 % 256
                elif op == op_SLOAD:
                    stk.append(ext.get_storage_data(msg.to, stk.pop()))
                elif op == op_SSTORE:
                    s0, s1 = stk.pop(), stk.pop()
                    if ext.get_storage_data(msg.to, s0):
                        gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                        refund = 0 if s1 else opcodes.GSTORAGEREFUND
                    else:
                        gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                        refund = 0
                    if compustate.gas < gascost:
                        return vm_exception('OUT OF GAS')
                    compustate.gas -= gascost
                    # adds neg gascost as a refund if below zero
                    ext.add_refund(refund)
                    ext.set_storage_data(msg.to, s0, s1)
                elif op == op_JUMP:
                    compustate.pc = stk.pop()
                    opnew = processed_code[compustate.pc][4] if \
                        compustate.pc in processed_code else op_STOP
                    if opnew != op_JUMPDEST:
                        return vm_exception('BAD JUMPDEST')
                elif op == op_JUMPI:
                    s0, s1 = stk.pop(), stk.pop()
                    if s1:
                        compustate.pc = s0
                        opnew = processed_code[compustate.pc][4] if \
                            compustate.pc in processed_code else op_STOP
                        if opnew != op_JUMPDEST:
                            return vm_exception('BAD JUMPDEST')
                elif op == op_PC:
                    stk.append(compustate.pc - 1)
                elif op == op_MSIZE:
                    stk.append(len(mem))
                elif op == op_GAS:
                    stk.append(compustate.gas)  # AFTER subtracting cost 1
            elif op_PUSH1 <= (op & 255) <= op_PUSH32:
                # Hide push value in high-order bytes of op
                stk.append(op >> 8)
            elif op_DUP1 <= op <= op_DUP16:
                depth = op - op_DUP1 + 1
                stk.append(stk[-depth])
            elif op_SWAP1 <= op <= op_SWAP16:
                depth = op - op_SWAP1 + 1
                temp = stk[-depth - 1]
                stk[-depth - 1] = stk[-1]
                stk[-1] = temp

            elif op_LOG0 <= op <= op_LOG4:
                """
                0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
                a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                        MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
                b. Logs are kept track of during tx execution exactly the same way as suicides
                   (except as an ordered list, not a set).
                   Each log is in the form [address, [topic1, ... ], data] where:
                   * address is what the ADDRESS opcode would output
                   * data is mem[MEMSTART: MEMSTART + MEMSZ]
                   * topics are as provided by the opcode
                c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
                """
                depth = op - op_LOG0
                mstart, msz = stk.pop(), stk.pop()
                topics = [stk.pop() for x in range(depth)]
                compustate.gas -= msz * opcodes.GLOGBYTE
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[mstart:mstart + msz]))
                ext.log(msg.to, topics, data)
                log_log.trace('LOG',
                              to=msg.to,
                              topics=topics,
                              data=list(map(utils.safe_ord, data)))
                # print('LOG', msg.to, topics, list(map(ord, data)))

            elif op == op_CREATE:
                value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                    cd = CallData(mem, mstart, msz)
                    create_msg = Message(msg.to, b'', value, compustate.gas,
                                         cd, msg.depth + 1)
                    o, gas, addr = ext.create(create_msg)
                    if o:
                        stk.append(utils.coerce_to_int(addr))
                        compustate.gas = gas
                    else:
                        stk.append(0)
                        compustate.gas = 0
                else:
                    stk.append(0)
            elif op == op_CALL:
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                        not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                    return vm_exception('OOG EXTENDING MEMORY')
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                    (value > 0) * opcodes.GCALLVALUETRANSFER
                submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
                if compustate.gas < gas + extra_gas:
                    return vm_exception('OUT OF GAS', needed=gas + extra_gas)
                if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                    compustate.gas -= (gas + extra_gas)
                    cd = CallData(mem, meminstart, meminsz)
                    call_msg = Message(msg.to,
                                       to,
                                       value,
                                       submsg_gas,
                                       cd,
                                       msg.depth + 1,
                                       code_address=to)
                    result, gas, data = ext.msg(call_msg)
                    if result == 0:
                        stk.append(0)
                    else:
                        stk.append(1)
                        compustate.gas += gas
                        for i in range(min(len(data), memoutsz)):
                            mem[memoutstart + i] = data[i]
                else:
                    compustate.gas -= (gas + extra_gas - submsg_gas)
                    stk.append(0)
            elif op == op_CALLCODE:
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                        not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                    return vm_exception('OOG EXTENDING MEMORY')
                extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER
                submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
                if compustate.gas < gas + extra_gas:
                    return vm_exception('OUT OF GAS', needed=gas + extra_gas)
                if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                    compustate.gas -= (gas + extra_gas)
                    to = utils.encode_int(to)
                    to = ((b'\x00' * (32 - len(to))) + to)[12:]
                    cd = CallData(mem, meminstart, meminsz)
                    call_msg = Message(msg.to,
                                       msg.to,
                                       value,
                                       submsg_gas,
                                       cd,
                                       msg.depth + 1,
                                       code_address=to)
                    result, gas, data = ext.msg(call_msg)
                    if result == 0:
                        stk.append(0)
                    else:
                        stk.append(1)
                        compustate.gas += gas
                        for i in range(min(len(data), memoutsz)):
                            mem[memoutstart + i] = data[i]
                else:
                    compustate.gas -= (gas + extra_gas - submsg_gas)
                    stk.append(0)
            elif op == op_RETURN:
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1])
            elif op == op_SUICIDE:
                to = utils.encode_int(stk.pop())
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                xfer = ext.get_balance(msg.to)
                ext.set_balance(to, ext.get_balance(to) + xfer)
                ext.set_balance(msg.to, 0)
                ext.add_suicide(msg.to)
                # print('suiciding %s %s %d' % (msg.to, to, xfer))
                return 1, compustate.gas, []