Esempio n. 1
0
def proc_ecrecover(ext, msg):
    # print('ecrecover proc', msg.gas)
    OP_GAS = opcodes.GECRECOVER
    gas_cost = OP_GAS
    if msg.gas < gas_cost:
        return 0, 0, []

    message_hash_bytes = [0] * 32
    msg.data.extract_copy(message_hash_bytes, 0, 0, 32)
    message_hash = b''.join(map(ascii_chr, message_hash_bytes))

    # TODO: This conversion isn't really necessary.
    # TODO: Invesitage if the check below is really needed.
    v = msg.data.extract32(32)
    r = msg.data.extract32(64)
    s = msg.data.extract32(96)

    if r >= secp256k1n or s >= secp256k1n or v < 27 or v > 28:
        return 1, msg.gas - opcodes.GECRECOVER, []
    try:
        pub = utils.ecrecover_to_pub(message_hash, v, r, s)
    except Exception as e:
        return 1, msg.gas - gas_cost, []
    o = [0] * 12 + [safe_ord(x) for x in utils.sha3(pub)[-20:]]
    return 1, msg.gas - gas_cost, o
Esempio n. 2
0
def consume_length_prefix(rlp, start):
    """Read a length prefix from an RLP string.

    :param rlp: the rlp string to read from
    :param start: the position at which to start reading
    :returns: a tuple ``(type, length, end)``, where ``type`` is either ``str``
              or ``list`` depending on the type of the following payload,
              ``length`` is the length of the payload in bytes, and ``end`` is
              the position of the first payload byte in the rlp string
    """
    b0 = safe_ord(rlp[start])
    if b0 < 128:  # single byte
        return (str, 1, start)
    elif b0 < 128 + 56:  # short string
        return (str, b0 - 128, start + 1)
    elif b0 < 192:  # long string
        ll = b0 - 128 - 56 + 1
        l = big_endian_to_int(rlp[start + 1 : start + 1 + ll])
        return (str, l, start + 1 + ll)
    elif b0 < 192 + 56:  # short list
        return (list, b0 - 192, start + 1)
    else:  # long list
        ll = b0 - 192 - 56 + 1
        l = big_endian_to_int(rlp[start + 1 : start + 1 + ll])
        return (list, l, start + 1 + ll)
Esempio n. 3
0
def proc_sha256(ext, msg):
    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 hashlib.sha256(d).digest()]
    return 1, msg.gas - gas_cost, o
Esempio n. 4
0
def optimized_decode_single(x, pos):
    z = safe_ord(x[pos])
    if z < 128:
        return x[pos : pos + 1], 1
    elif z < 184:
        return x[pos + 1 : pos + z - 127], z - 127
    else:
        ll = big_endian_to_int(x[pos + 1 : pos + z - 182])
        return x[pos + z - 182 : pos + z - 182 + ll], z - 182 + ll
Esempio n. 5
0
def proc_ripemd160(ext, msg):
    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 hashlib.new("ripemd160", d).digest()]
    return 1, msg.gas - gas_cost, o
Esempio n. 6
0
def preprocess_code(code):
    o = 0
    i = 0
    pushcache = {}
    code = code + b"\x00" * 32
    while i < len(code) - 32:
        codebyte = safe_ord(code[i])
        if codebyte == 0x5B:
            o |= 1 << i
        if 0x60 <= codebyte <= 0x7F:
            pushcache[i] = utils.big_endian_to_int(code[i + 1 : i + codebyte - 0x5E])
            i += codebyte - 0x5E
        else:
            i += 1
    return o, pushcache
Esempio n. 7
0
def proc_ecmul(ext, msg):
    if not ext.post_metropolis_hardfork():
        return 1, msg.gas, []
    import py_ecc.optimized_bn128 as bn128
    FQ = bn128.FQ
    print('ecmul proc', msg.gas)
    if msg.gas < opcodes.GECMUL:
        return 0, 0, []
    x = msg.data.extract32(0)
    y = msg.data.extract32(32)
    m = msg.data.extract32(64)
    p = validate_point(x, y)
    if p is False:
        return 0, 0, []
    o = bn128.normalize(bn128.multiply(p, m))
    return (1, msg.gas - opcodes.GECMUL,
            [safe_ord(c) for c in (encode_int32(o[0].n) + encode_int32(o[1].n))])
Esempio n. 8
0
def proc_ecmul(ext, msg):
    import py_ecc.optimized_bn128 as bn128

    FQ = bn128.FQ
    if msg.gas < opcodes.GECMUL:
        return 0, 0, []
    x = msg.data.extract32(0)
    y = msg.data.extract32(32)
    m = msg.data.extract32(64)
    p = validate_point(x, y)
    if p is False:
        return 0, 0, []
    o = bn128.normalize(bn128.multiply(p, m))
    return (
        1,
        msg.gas - opcodes.GECMUL,
        [safe_ord(c) for c in (encode_int32(o[0].n) + encode_int32(o[1].n))],
    )
Esempio n. 9
0
def apply_xshard_desposit(state, deposit, gas_used_start):
    state.logs = []
    state.suicides = []
    state.refunds = 0
    state.full_shard_key = deposit.to_address.full_shard_key

    state.delta_token_balance(
        deposit.from_address.recipient, deposit.transfer_token_id, deposit.value
    )

    message_data = vm.CallData(
        [safe_ord(x) for x in deposit.message_data], 0, len(deposit.message_data)
    )

    message = vm.Message(
        deposit.from_address.recipient,
        deposit.to_address.recipient,
        deposit.value,
        deposit.gas_remained,
        message_data,
        code_address=deposit.to_address.recipient,
        from_full_shard_key=deposit.from_address.full_shard_key,
        to_full_shard_key=deposit.to_address.full_shard_key,
        tx_hash=deposit.tx_hash,
        transfer_token_id=deposit.transfer_token_id,
        gas_token_id=deposit.gas_token_id,
    )

    # MESSAGE
    ext = VMExt(
        state, sender=deposit.from_address.recipient, gas_price=deposit.gas_price
    )

    return apply_transaction_message(
        state,
        message,
        ext,
        should_create_contract=deposit.create_contract,
        gas_used_start=gas_used_start,
        is_cross_shard=True,
        contract_address=deposit.to_address.recipient
        if deposit.create_contract
        else b"",
    )
Esempio n. 10
0
def proc_ecadd(ext, msg):
    import py_ecc.optimized_bn128 as bn128

    FQ = bn128.FQ
    if msg.gas < opcodes.GECADD:
        return 0, 0, []
    x1 = msg.data.extract32(0)
    y1 = msg.data.extract32(32)
    x2 = msg.data.extract32(64)
    y2 = msg.data.extract32(96)
    p1 = validate_point(x1, y1)
    p2 = validate_point(x2, y2)
    if p1 is False or p2 is False:
        return 0, 0, []
    o = bn128.normalize(bn128.add(p1, p2))
    return (
        1,
        msg.gas - opcodes.GECADD,
        [safe_ord(x) for x in (encode_int32(o[0].n) + encode_int32(o[1].n))],
    )
Esempio n. 11
0
def proc_ecadd(ext, msg):
    if not ext.post_metropolis_hardfork():
        return 1, msg.gas, []
    import py_ecc.optimized_bn128 as bn128
    FQ = bn128.FQ
    print('ecadd proc:', msg.gas)
    if msg.gas < opcodes.GECADD:
        return 0, 0, []
    x1 = msg.data.extract32(0)
    y1 = msg.data.extract32(32)
    x2 = msg.data.extract32(64)
    y2 = msg.data.extract32(96)
    p1 = validate_point(x1, y1)
    p2 = validate_point(x2, y2)
    if p1 is False or p2 is False:
        return 0, 0, []
    o = bn128.normalize(bn128.add(p1, p2))
    return 1, msg.gas - \
        opcodes.GECADD, [safe_ord(x) for x in (
            encode_int32(o[0].n) + encode_int32(o[1].n))]
Esempio n. 12
0
def proc_modexp(ext, msg):
    if not ext.post_metropolis_hardfork():
        return 1, msg.gas, []
    print('modexp proc', msg.gas)
    baselen = msg.data.extract32(0)
    explen = msg.data.extract32(32)
    modlen = msg.data.extract32(64)
    first_exp_bytes = msg.data.extract32(
        96 + baselen) >> (8 * max(32 - explen, 0))
    bitlength = -1
    while first_exp_bytes:
        bitlength += 1
        first_exp_bytes >>= 1
    adjusted_explen = max(bitlength, 0) + 8 * max(explen - 32, 0)
    gas_cost = (mult_complexity(max(modlen, baselen)) *
                max(adjusted_explen, 1)) // opcodes.GMODEXPQUADDIVISOR
    print(baselen, explen, modlen, 'expected gas cost', gas_cost)
    if msg.gas < gas_cost:
        return 0, 0, []
    if baselen == 0:
        return 1, msg.gas - gas_cost, [0] * modlen
    if modlen == 0:
        return 1, msg.gas - gas_cost, []
    base = bytearray(baselen)
    msg.data.extract_copy(base, 0, 96, baselen)
    exp = bytearray(explen)
    msg.data.extract_copy(exp, 0, 96 + baselen, explen)
    mod = bytearray(modlen)
    msg.data.extract_copy(mod, 0, 96 + baselen + explen, modlen)
    if utils.big_endian_to_int(mod) == 0:
        return 1, msg.gas - gas_cost, [0] * modlen
    o = pow(
        utils.big_endian_to_int(base),
        utils.big_endian_to_int(exp),
        utils.big_endian_to_int(mod))
    return 1, msg.gas - \
        gas_cost, [
            safe_ord(x) for x in utils.zpad(
                utils.int_to_big_endian(o), modlen)]
Esempio n. 13
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")

    # Initialize stack, memory, program counter, etc
    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    # Compute
    jumpdest_mask, pushcache = preprocess_code(code)
    codelen = len(code)

    # For tracing purposes
    op = None
    _prevop = None
    steps = 0
    while compustate.pc < codelen:

        opcode = safe_ord(code[compustate.pc])

        # Invalid operation
        if opcode not in opcodes.opcodes:
            return vm_exception("INVALID OP", opcode=opcode)

        op, in_args, out_args, fee = opcodes.opcodes[opcode]

        # Apply operation
        if trace_vm:
            compustate.reset_prev()
        compustate.gas -= fee
        compustate.pc += 1

        # Tracing
        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) < 4096:
                    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 + 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["steps"] = steps
            trace_data["depth"] = msg.depth
            if op[:4] == "PUSH":
                trace_data["pushvalue"] = pushcache[compustate.pc - 1]
            log_vm_op.trace("vm", op=op, **trace_data)
            steps += 1
            _prevop = op

        # out of gas error
        if compustate.gas < 0:
            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)),
            )

        # overfull stack error
        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)),
            )

        # Valid operations
        # Pushes first because they are very frequent
        if 0x60 <= opcode <= 0x7F:
            stk.append(pushcache[compustate.pc - 1])
            # Move 1 byte forward for 0x60, up to 32 bytes for 0x7f
            compustate.pc += opcode - 0x5F
        # Arithmetic
        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 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)
        # Comparisons
        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 op == "SHL":
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s0 >= 256 else (s1 << s0) & TT256M1)
            elif op == "SHR":
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s0 >= 256 else s1 >> s0)
            elif op == "SAR":
                s0, s1 = stk.pop(), utils.to_signed(stk.pop())
                if s0 >= 256:
                    ret = 0 if s1 >= 0 else TT256M1
                else:
                    ret = (s1 >> s0) & TT256M1
                stk.append(ret)
        # SHA3 and environment info
        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":
                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(codelen)
            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 < codelen:
                        mem[mstart + i] = 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[
                    dstart : dstart + size
                ]
            elif op == "RETURNDATASIZE":
                stk.append(len(compustate.last_returned))
            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] = safe_ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
            elif op == "EXTCODEHASH":
                addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160)
                if not ext.account_exists(addr):
                    stk.append(0)
                else:
                    extcode = ext.get_code(addr) or b""
                    assert utils.is_string(extcode)
                    stk.append(utils.big_endian_to_int(utils.sha3(extcode)))
        # Block info
        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)
        # VM state manipulations
        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":
                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()
                if compustate.pc >= codelen or not (
                    (1 << compustate.pc) & jumpdest_mask
                ):
                    return vm_exception("BAD JUMPDEST")
            elif op == "JUMPI":
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    if compustate.pc >= codelen or not (
                        (1 << compustate.pc) & jumpdest_mask
                    ):
                        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
        # DUPn (eg. DUP1: a b c -> a b c c, DUP3: a b c -> a b c a)
        elif op[:3] == "DUP":
            # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for 0x8f
            stk.append(stk[0x7F - opcode])
        # SWAPn (eg. SWAP1: a b c d -> a b d c, SWAP3: a b c d -> d b c a)
        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
        # Logs (aka "events")
        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))
            )
        # Create a new contract
        elif op in ("CREATE", "CREATE2"):
            salt = None
            if op == "CREATE":
                value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
            else:  # CREATE2
                value, mstart, msz, salt = stk.pop(), stk.pop(), stk.pop(), stk.pop()
                salt = salt.to_bytes(32, byteorder="big")
                compustate.gas -= opcodes.GSHA3WORD * ceil(msz / 32)

            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
                ingas = all_but_1n(ingas, opcodes.CALL_CHILD_LIMIT_DENOM)
                create_msg = Message(
                    msg.to,
                    b"",
                    value,
                    ingas,
                    cd,
                    msg.depth + 1,
                    # Used for calculating contract address
                    to_full_shard_key=msg.to_full_shard_key,
                    transfer_token_id=msg.transfer_token_id,
                    gas_token_id=msg.gas_token_id,
                )
                o, gas, data = ext.create(create_msg, salt)
                if o:
                    stk.append(utils.coerce_to_int(data))
                    compustate.last_returned = bytearray(b"")
                else:
                    stk.append(0)
                    compustate.last_returned = bytearray(data)
                compustate.gas = compustate.gas - ingas + gas
            else:
                stk.append(0)
                compustate.last_returned = bytearray(b"")
        # Calls
        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 and op == "CALL":
                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 various factors
            extra_gas = 0
            # Creating a new account
            if op == "CALL" and not ext.account_exists(to) and (value > 0):
                extra_gas += opcodes.GCALLNEWACCOUNT
            # Value transfer
            if value > 0:
                extra_gas += opcodes.GCALLVALUETRANSFER
            # Compute child gas limit
            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),
            )
            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)
                compustate.last_returned = bytearray(b"")
            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,
                        # Used for calculating contract address
                        to_full_shard_key=msg.to_full_shard_key,
                        code_address=to,
                        static=msg.static,
                        transfer_token_id=msg.transfer_token_id,
                        gas_token_id=msg.gas_token_id,
                    )
                elif op == "DELEGATECALL":
                    call_msg = Message(
                        msg.sender,
                        msg.to,
                        msg.value,
                        submsg_gas,
                        cd,
                        msg.depth + 1,
                        # Used for calculating contract address
                        to_full_shard_key=msg.to_full_shard_key,
                        code_address=to,
                        transfers_value=False,
                        static=msg.static,
                        transfer_token_id=msg.transfer_token_id,
                        gas_token_id=msg.gas_token_id,
                    )
                elif op == "STATICCALL":
                    call_msg = Message(
                        msg.to,
                        to,
                        value,
                        submsg_gas,
                        cd,
                        msg.depth + 1,
                        # Used for calculating contract address
                        to_full_shard_key=msg.to_full_shard_key,
                        code_address=to,
                        static=True,
                        transfer_token_id=msg.transfer_token_id,
                        gas_token_id=msg.gas_token_id,
                    )
                elif op == "CALLCODE":
                    call_msg = Message(
                        msg.to,
                        msg.to,
                        value,
                        submsg_gas,
                        cd,
                        msg.depth + 1,
                        # Used for calculating contract address
                        to_full_shard_key=msg.to_full_shard_key,
                        code_address=to,
                        static=msg.static,
                        transfer_token_id=msg.transfer_token_id,
                        gas_token_id=msg.gas_token_id,
                    )
                else:
                    raise Exception("Lolwut")
                # Get result
                if call_msg.to == PROC_CURRENT_MNT_ID:
                    msg.token_id_queried = True
                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)
        # Return opcode
        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])
        # Revert opcode (Metropolis)
        elif op == "REVERT":
            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])
        # SUICIDE opcode (also called SELFDESTRUCT)
        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)
            extra_gas = (
                (not ext.account_exists(to)) * (xfer > 0) * 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 peaceful_exit("SUICIDED", compustate.gas, [])

    if trace_vm:
        compustate.reset_prev()
    return peaceful_exit("CODE OUT OF RANGE", compustate.gas, [])
Esempio n. 14
0
def apply_transaction(state, tx: transactions.Transaction, tx_wrapper_hash):
    """tx_wrapper_hash is the hash for quarkchain.core.Transaction
    TODO: remove quarkchain.core.Transaction wrapper and use evm.Transaction directly
    """
    state.logs = []
    state.suicides = []
    state.refunds = 0
    validate_transaction(state, tx)

    state.full_shard_key = tx.to_full_shard_key

    intrinsic_gas = tx.intrinsic_gas_used
    log_tx.debug("TX NEW", txdict=tx.to_dict())

    # start transacting #################
    state.increment_nonce(tx.sender)

    # part of fees should go to root chain miners
    local_fee_rate = (1 - state.qkc_config.reward_tax_rate
                      if state.qkc_config else Fraction(1))

    # buy startgas
    gasprice, refund_rate = tx.gasprice, 100
    # convert gas if using non-genesis native token
    if gasprice != 0 and tx.gas_token_id != state.genesis_token:
        refund_rate, converted_genesis_token_gas_price = pay_native_token_as_gas(
            state, tx.gas_token_id, tx.startgas, tx.gasprice)
        # guaranteed by validation
        check(converted_genesis_token_gas_price > 0)
        gasprice = converted_genesis_token_gas_price
        contract_addr = SystemContract.GENERAL_NATIVE_TOKEN.addr()
        # guaranteed by validation
        check(
            state.deduct_value(
                contract_addr,
                state.genesis_token,
                tx.startgas * converted_genesis_token_gas_price,
            ))
        state.delta_token_balance(contract_addr, tx.gas_token_id,
                                  tx.startgas * tx.gasprice)

    check(
        state.deduct_value(tx.sender, tx.gas_token_id,
                           tx.startgas * tx.gasprice))

    message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data))
    message = vm.Message(
        tx.sender,
        tx.to,
        tx.value,
        tx.startgas - intrinsic_gas,
        message_data,
        code_address=tx.to,
        from_full_shard_key=tx.from_full_shard_key
        if not tx.is_testing else None,
        to_full_shard_key=tx.to_full_shard_key if not tx.is_testing else None,
        tx_hash=tx_wrapper_hash,
        transfer_token_id=tx.transfer_token_id,
        # always genesis token for gas token
        gas_token_id=state.genesis_token,
    )

    # MESSAGE
    ext = VMExt(state, tx.sender, gasprice)

    contract_address = b""
    if not tx.is_cross_shard:
        return apply_transaction_message(state,
                                         message,
                                         ext,
                                         tx.to == b"",
                                         intrinsic_gas,
                                         refund_rate=refund_rate)

    # handle xshard
    local_gas_used = intrinsic_gas
    remote_gas_reserved = 0
    if transfer_failure_by_posw_balance_check(ext, message):
        success = 0
        # Currently, burn all gas
        local_gas_used = tx.startgas
    elif tx.to == b"":
        check(state.deduct_value(tx.sender, tx.transfer_token_id, tx.value))
        remote_gas_reserved = tx.startgas - intrinsic_gas
        ext.add_cross_shard_transaction_deposit(
            quarkchain.core.CrossShardTransactionDeposit(
                tx_hash=tx_wrapper_hash,
                from_address=quarkchain.core.Address(tx.sender,
                                                     tx.from_full_shard_key),
                to_address=quarkchain.core.Address(
                    mk_contract_address(tx.sender, state.get_nonce(tx.sender),
                                        tx.from_full_shard_key),
                    tx.to_full_shard_key,
                ),
                value=tx.value,
                # convert to genesis token and use converted gas price
                gas_token_id=state.genesis_token,
                gas_price=gasprice,
                transfer_token_id=tx.transfer_token_id,
                message_data=tx.data,
                create_contract=True,
                gas_remained=remote_gas_reserved,
                refund_rate=refund_rate,
            ))
        success = 1
    else:
        check(state.deduct_value(tx.sender, tx.transfer_token_id, tx.value))
        if (state.qkc_config.ENABLE_EVM_TIMESTAMP is None
                or state.timestamp >= state.qkc_config.ENABLE_EVM_TIMESTAMP):
            remote_gas_reserved = tx.startgas - intrinsic_gas
        ext.add_cross_shard_transaction_deposit(
            quarkchain.core.CrossShardTransactionDeposit(
                tx_hash=tx_wrapper_hash,
                from_address=quarkchain.core.Address(tx.sender,
                                                     tx.from_full_shard_key),
                to_address=quarkchain.core.Address(tx.to,
                                                   tx.to_full_shard_key),
                value=tx.value,
                # convert to genesis token and use converted gas price
                gas_token_id=state.genesis_token,
                gas_price=gasprice,
                transfer_token_id=tx.transfer_token_id,
                message_data=tx.data,
                create_contract=False,
                gas_remained=remote_gas_reserved,
                refund_rate=refund_rate,
            ))
        success = 1
    gas_remained = tx.startgas - local_gas_used - remote_gas_reserved

    _refund(state, message, ext.tx_gasprice * gas_remained, refund_rate)

    # if x-shard, reserve part of the gas for the target shard miner for fee
    fee = (ext.tx_gasprice * (local_gas_used -
                              (opcodes.GTXXSHARDCOST if success else 0)) *
           local_fee_rate.numerator // local_fee_rate.denominator)
    state.delta_token_balance(state.block_coinbase, state.genesis_token, fee)
    add_dict(state.block_fee_tokens, {state.genesis_token: fee})

    output = []

    state.gas_used += local_gas_used
    if (state.qkc_config.ENABLE_EVM_TIMESTAMP is None
            or state.timestamp >= state.qkc_config.ENABLE_EVM_TIMESTAMP):
        state.gas_used -= opcodes.GTXXSHARDCOST if success else 0

    # Construct a receipt
    r = mk_receipt(state, success, state.logs, contract_address,
                   state.full_shard_key)
    state.logs = []
    state.add_receipt(r)
    return success, output
Esempio n. 15
0
def apply_transaction(state, tx: transactions.Transaction, tx_wrapper_hash):
    """tx_wrapper_hash is the hash for quarkchain.core.Transaction
    TODO: remove quarkchain.core.Transaction wrapper and use evm.Transaction directly
    """
    state.logs = []
    state.suicides = []
    state.refunds = 0
    validate_transaction(state, tx)

    state.full_shard_key = tx.to_full_shard_key

    intrinsic_gas = tx.intrinsic_gas_used
    log_tx.debug("TX NEW", txdict=tx.to_dict())

    # start transacting #################
    state.increment_nonce(tx.sender)

    # part of fees should go to root chain miners
    local_fee_rate = (1 - state.qkc_config.reward_tax_rate
                      if state.qkc_config else Fraction(1))

    # buy startgas
    assert (state.get_balance(tx.sender, token_id=tx.gas_token_id) >=
            tx.startgas * tx.gasprice)
    state.delta_token_balance(tx.sender, tx.gas_token_id,
                              -tx.startgas * tx.gasprice)

    message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data))
    message = vm.Message(
        tx.sender,
        tx.to,
        tx.value,
        tx.startgas - intrinsic_gas,
        message_data,
        code_address=tx.to,
        from_full_shard_key=tx.from_full_shard_key
        if not tx.is_testing else None,
        to_full_shard_key=tx.to_full_shard_key if not tx.is_testing else None,
        tx_hash=tx_wrapper_hash,
        transfer_token_id=tx.transfer_token_id,
        gas_token_id=tx.gas_token_id,
    )

    # MESSAGE
    ext = VMExt(state, tx.sender, tx.gasprice)

    contract_address = b""
    if tx.is_cross_shard:
        local_gas_used = intrinsic_gas
        remote_gas_reserved = 0
        if transfer_failure_by_posw_balance_check(ext, message):
            success = 0
            # Currently, burn all gas
            local_gas_used = tx.startgas
        elif tx.to == b"":
            state.delta_token_balance(tx.sender, tx.transfer_token_id,
                                      -tx.value)
            remote_gas_reserved = tx.startgas - intrinsic_gas
            ext.add_cross_shard_transaction_deposit(
                quarkchain.core.CrossShardTransactionDeposit(
                    tx_hash=tx_wrapper_hash,
                    from_address=quarkchain.core.Address(
                        tx.sender, tx.from_full_shard_key),
                    to_address=quarkchain.core.Address(
                        mk_contract_address(
                            tx.sender,
                            state.get_nonce(tx.sender),
                            tx.from_full_shard_key,
                        ),
                        tx.to_full_shard_key,
                    ),
                    value=tx.value,
                    gas_price=tx.gasprice,
                    gas_token_id=tx.gas_token_id,
                    transfer_token_id=tx.transfer_token_id,
                    message_data=tx.data,
                    create_contract=True,
                    gas_remained=remote_gas_reserved,
                ))
            success = 1
        else:
            state.delta_token_balance(tx.sender, tx.transfer_token_id,
                                      -tx.value)
            if (state.qkc_config.ENABLE_EVM_TIMESTAMP is None or
                    state.timestamp >= state.qkc_config.ENABLE_EVM_TIMESTAMP):
                remote_gas_reserved = tx.startgas - intrinsic_gas
            ext.add_cross_shard_transaction_deposit(
                quarkchain.core.CrossShardTransactionDeposit(
                    tx_hash=tx_wrapper_hash,
                    from_address=quarkchain.core.Address(
                        tx.sender, tx.from_full_shard_key),
                    to_address=quarkchain.core.Address(tx.to,
                                                       tx.to_full_shard_key),
                    value=tx.value,
                    gas_price=tx.gasprice,
                    gas_token_id=tx.gas_token_id,
                    transfer_token_id=tx.transfer_token_id,
                    message_data=tx.data,
                    create_contract=False,
                    gas_remained=remote_gas_reserved,
                ))
            success = 1
        gas_remained = tx.startgas - local_gas_used - remote_gas_reserved

        # Refund
        state.delta_token_balance(message.sender, message.gas_token_id,
                                  ext.tx_gasprice * gas_remained)

        # if x-shard, reserve part of the gas for the target shard miner for fee
        fee = (tx.gasprice * (local_gas_used -
                              (opcodes.GTXXSHARDCOST if success else 0)) *
               local_fee_rate.numerator // local_fee_rate.denominator)
        state.delta_token_balance(state.block_coinbase, tx.gas_token_id, fee)
        add_dict(state.block_fee_tokens, {message.gas_token_id: fee})

        output = []

        state.gas_used += local_gas_used
        if (state.qkc_config.ENABLE_EVM_TIMESTAMP is None
                or state.timestamp >= state.qkc_config.ENABLE_EVM_TIMESTAMP):
            state.gas_used -= opcodes.GTXXSHARDCOST if success else 0

        # Construct a receipt
        r = mk_receipt(state, success, state.logs, contract_address,
                       state.full_shard_key)
        state.logs = []
        state.add_receipt(r)
        return success, output

    return apply_transaction_message(state, message, ext, tx.to == b"",
                                     intrinsic_gas)
Esempio n. 16
0
def apply_transaction(state, tx: transactions.Transaction, tx_wrapper_hash):
    """tx_wrapper_hash is the hash for quarkchain.core.Transaction
    TODO: remove quarkchain.core.Transaction wrapper and use evm.Transaction directly
    """
    state.logs = []
    state.suicides = []
    state.refunds = 0
    validate_transaction(state, tx)

    state.full_shard_key = tx.to_full_shard_key

    intrinsic_gas = tx.intrinsic_gas_used
    log_tx.debug("TX NEW", txdict=tx.to_dict())

    # start transacting #################
    if tx.sender != null_address:
        state.increment_nonce(tx.sender)

    # part of fees should go to root chain miners
    local_fee_rate = (1 - state.qkc_config.reward_tax_rate
                      if state.qkc_config else Fraction(1))

    # buy startgas
    assert state.get_balance(tx.sender) >= tx.startgas * tx.gasprice
    state.delta_balance(tx.sender, -tx.startgas * tx.gasprice)

    message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data))
    message = vm.Message(
        tx.sender,
        tx.to,
        tx.value,
        tx.startgas - intrinsic_gas,
        message_data,
        code_address=tx.to,
        is_cross_shard=tx.is_cross_shard,
        from_full_shard_key=tx.from_full_shard_key,
        to_full_shard_key=tx.to_full_shard_key,
        tx_hash=tx_wrapper_hash,
    )

    # MESSAGE
    ext = VMExt(state, tx)

    contract_address = b""
    if tx.to != b"":
        result, gas_remained, data = apply_msg(ext, message)
    else:  # CREATE
        result, gas_remained, data = create_contract(ext, message)
        contract_address = (data if data else b""
                            )  # data could be [] when vm failed execution

    assert gas_remained >= 0

    log_tx.debug("TX APPLIED",
                 result=result,
                 gas_remained=gas_remained,
                 data=data)

    gas_used = tx.startgas - gas_remained

    # pay CORRECT tx fee (after tax) to coinbase so that each step of state is accurate
    # Transaction failed
    if not result:
        log_tx.debug(
            "TX FAILED",
            reason="out of gas",
            startgas=tx.startgas,
            gas_remained=gas_remained,
        )
        state.delta_balance(tx.sender, tx.gasprice * gas_remained)
        fee = (tx.gasprice * gas_used * local_fee_rate.numerator //
               local_fee_rate.denominator)
        state.delta_balance(state.block_coinbase, fee)
        state.block_fee += tx.gasprice * gas_used
        output = b""
        success = 0
    # Transaction success
    else:
        log_tx.debug("TX SUCCESS", data=data)
        state.refunds += len(set(state.suicides)) * opcodes.GSUICIDEREFUND
        if state.refunds > 0:
            log_tx.debug("Refunding",
                         gas_refunded=min(state.refunds, gas_used // 2))
            gas_remained += min(state.refunds, gas_used // 2)
            gas_used -= min(state.refunds, gas_used // 2)
            state.refunds = 0
        # sell remaining gas
        state.delta_balance(tx.sender, tx.gasprice * gas_remained)
        # if x-shard, reserve part of the gas for the target shard miner
        fee = (tx.gasprice *
               (gas_used -
                (opcodes.GTXXSHARDCOST if tx.is_cross_shard else 0)) *
               local_fee_rate.numerator // local_fee_rate.denominator)
        state.delta_balance(state.block_coinbase, fee)
        state.block_fee += fee
        if tx.to:
            output = bytearray_to_bytestr(data)
        else:
            output = data
        success = 1

        # TODO: check if the destination address is correct, and consume xshard gas of the state
        # the xshard gas and fee is consumed by destination shard block

    state.gas_used += gas_used

    # Clear suicides
    suicides = state.suicides
    state.suicides = []
    for s in suicides:
        state.set_balance(s, 0)
        state.del_account(s)

    # Pre-Metropolis: commit state after every tx
    if not state.is_METROPOLIS() and not SKIP_MEDSTATES:
        state.commit()

    # Construct a receipt
    r = mk_receipt(state, success, state.logs, contract_address,
                   state.full_shard_key)
    state.logs = []
    state.add_receipt(r)
    state.set_param("bloom", state.bloom | r.bloom)
    state.set_param("txindex", state.txindex + 1)

    return success, output
Esempio n. 17
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')

    # Initialize stack, memory, program counter, etc
    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    # Compute
    jumpdest_mask, pushcache = preprocess_code(code)
    codelen = len(code)

    # For tracing purposes
    op = None
    _prevop = None
    steps = 0
    while compustate.pc < codelen:

        opcode = safe_ord(code[compustate.pc])

        # Invalid operation
        if opcode not in opcodes.opcodes:
            return vm_exception('INVALID OP', opcode=opcode)

        if opcode in opcodes.opcodesMetropolis and not ext.post_metropolis_hardfork(
        ):
            return vm_exception('INVALID OP (not yet enabled)', opcode=opcode)

        op, in_args, out_args, fee = opcodes.opcodes[opcode]

        # Apply operation
        if trace_vm:
            compustate.reset_prev()
        compustate.gas -= fee
        compustate.pc += 1

        # Tracing
        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) < 4096:
                    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 + 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['steps'] = steps
            trace_data['depth'] = msg.depth
            if op[:4] == 'PUSH':
                trace_data['pushvalue'] = pushcache[compustate.pc - 1]
            log_vm_op.trace('vm', op=op, **trace_data)
            steps += 1
            _prevop = op

        # out of gas error
        if compustate.gas < 0:
            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)))

        # overfull stack error
        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)))

        # Valid operations
        # Pushes first because they are very frequent
        if 0x60 <= opcode <= 0x7f:
            stk.append(pushcache[compustate.pc - 1])
            # Move 1 byte forward for 0x60, up to 32 bytes for 0x7f
            compustate.pc += opcode - 0x5f
        # Arithmetic
        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_spurious_dragon_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)
        # Comparisons
        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)
        # SHA3 and environment info
        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(codelen)
            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 < codelen:
                        mem[mstart + i] = 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[dstart:dstart + size]
            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] = safe_ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
        # Block info
        elif opcode < 0x50:
            if op == 'BLOCKHASH':
                if ext.post_constantinople_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)
        # VM state manipulations
        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()
                if compustate.pc >= codelen or not (
                    (1 << compustate.pc) & jumpdest_mask):
                    return vm_exception('BAD JUMPDEST')
            elif op == 'JUMPI':
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    if compustate.pc >= codelen or not (
                        (1 << compustate.pc) & jumpdest_mask):
                        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
        # DUPn (eg. DUP1: a b c -> a b c c, DUP3: a b c -> a b c a)
        elif op[:3] == 'DUP':
            # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for 0x8f
            stk.append(stk[0x7f - opcode])
        # SWAPn (eg. SWAP1: a b c d -> a b d c, SWAP3: a b c d -> d b c a)
        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
        # Logs (aka "events")
        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)))
        # Create a new contract
        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, data = ext.create(create_msg)
                if o:
                    stk.append(utils.coerce_to_int(data))
                    compustate.last_returned = bytearray(b'')
                else:
                    stk.append(0)
                    compustate.last_returned = bytearray(data)
                compustate.gas = compustate.gas - ingas + gas
            else:
                stk.append(0)
                compustate.last_returned = bytearray(b'')
        # Calls
        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 and op == 'CALL':
                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 various factors
            extra_gas = 0
            # Creating a new account
            if op == 'CALL' and not ext.account_exists(to) and (
                    value > 0 or not ext.post_spurious_dragon_hardfork()):
                extra_gas += opcodes.GCALLNEWACCOUNT
            # Value transfer
            if value > 0:
                extra_gas += opcodes.GCALLVALUETRANSFER
            # Cost increased from 40 to 700 in Tangerine Whistle
            if ext.post_anti_dos_hardfork():
                extra_gas += 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)
                compustate.last_returned = bytearray(b'')
            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 ext.post_metropolis_hardfork() and op == 'STATICCALL':
                    call_msg = Message(msg.to,
                                       to,
                                       value,
                                       submsg_gas,
                                       cd,
                                       msg.depth + 1,
                                       code_address=to,
                                       static=True)
                elif op in ('DELEGATECALL', 'STATICCALL'):
                    return vm_exception('OPCODE %s INACTIVE' % op)
                elif op == 'CALLCODE':
                    call_msg = Message(msg.to,
                                       msg.to,
                                       value,
                                       submsg_gas,
                                       cd,
                                       msg.depth + 1,
                                       code_address=to,
                                       static=msg.static)
                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)
        # Return opcode
        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])
        # Revert opcode (Metropolis)
        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])
        # SUICIDE opcode (also called SELFDESTRUCT)
        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_spurious_dragon_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 peaceful_exit('SUICIDED', compustate.gas, [])

        if trace_vm:
            vm_trace(ext, msg, compustate, opcode, pushcache)

    if trace_vm:
        compustate.reset_prev()
        vm_trace(ext, msg, compustate, 0, None)
    return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])