예제 #1
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"",
    )
예제 #2
0
def create_contract(ext, msg):
    log_msg.debug("CONTRACT CREATION")

    if msg.is_cross_shard:
        return 0, msg.gas, b""

    code = msg.data.extract_all()

    if ext.tx_origin != msg.sender:
        ext.increment_nonce(msg.sender)

    if ext.post_constantinople_hardfork() and msg.sender == null_address:
        msg.to = mk_contract_address(msg.sender, msg.to_full_shard_key, 0)
        # msg.to = sha3(msg.sender + code)[12:]
    else:
        nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1)
        msg.to = mk_contract_address(msg.sender, msg.to_full_shard_key, nonce)

    if ext.post_metropolis_hardfork() and (ext.get_nonce(msg.to)
                                           or len(ext.get_code(msg.to))):
        log_msg.debug("CREATING CONTRACT ON TOP OF EXISTING CONTRACT")
        return 0, 0, b""

    b = ext.get_balance(msg.to)
    if b > 0:
        ext.set_balance(msg.to, b)
        ext.set_nonce(msg.to, 0)
        ext.set_code(msg.to, b"")
        # ext.reset_storage(msg.to)

    msg.is_create = True
    # assert not ext.get_code(msg.to)
    msg.data = vm.CallData([], 0, 0)
    snapshot = ext.snapshot()

    ext.set_nonce(msg.to, 1 if ext.post_spurious_dragon_hardfork() else 0)
    res, gas, dat = _apply_msg(ext, msg, code)

    log_msg.debug(
        "CONTRACT CREATION FINISHED",
        res=res,
        gas=gas,
        dat=dat if len(dat) < 2500 else ("data<%d>" % len(dat)),
    )

    if res:
        if not len(dat):
            # ext.set_code(msg.to, b'')
            return 1, gas, msg.to
        gcost = len(dat) * opcodes.GCONTRACTBYTE
        if gas >= gcost and (len(dat) <= 24576
                             or not ext.post_spurious_dragon_hardfork()):
            gas -= gcost
        else:
            dat = []
            log_msg.debug(
                "CONTRACT CREATION FAILED",
                have=gas,
                want=gcost,
                block_number=ext.block_number,
            )
            if ext.post_homestead_hardfork():
                ext.revert(snapshot)
                return 0, 0, b""
        ext.set_code(msg.to, bytearray_to_bytestr(dat))
        log_msg.debug("SETTING CODE", addr=encode_hex(msg.to), lendat=len(dat))
        return 1, gas, msg.to
    else:
        ext.revert(snapshot)
        return 0, gas, dat
예제 #3
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
예제 #4
0
def create_contract(ext, msg, contract_recipient=b"", salt=None):
    log_msg.debug("CONTRACT CREATION")

    if msg.transfer_token_id != ext.default_chain_token:
        # TODODLL calling smart contract with non QKC transfer_token_id is not supported
        return 0, msg.gas, b""

    code = msg.data.extract_all()

    if ext.tx_origin != msg.sender:
        ext.increment_nonce(msg.sender)

    if contract_recipient != b"":
        # apply xshard deposit, where contract address has already been specified
        msg.to = contract_recipient
    elif salt is not None:
        # create2
        msg.to = mk_contract_address2(msg.sender, salt, utils.sha3(code))
    else:
        nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1)
        msg.to = mk_contract_address(msg.sender, nonce, msg.to_full_shard_key)

    if ext.get_nonce(msg.to) or len(ext.get_code(msg.to)):
        log_msg.debug("CREATING CONTRACT ON TOP OF EXISTING CONTRACT")
        return 0, 0, b""

    if ext.account_exists(msg.to):
        ext.set_nonce(msg.to, 0)
        ext.set_code(msg.to, b"")
        ext.reset_storage(msg.to)

    msg.is_create = True
    # assert not ext.get_code(msg.to)
    msg.data = vm.CallData([], 0, 0)
    snapshot = ext.snapshot()

    ext.set_nonce(msg.to, 1)
    ext.reset_storage(msg.to)
    res, gas, dat = _apply_msg(ext, msg, code)

    log_msg.debug(
        "CONTRACT CREATION FINISHED",
        res=res,
        gas=gas,
        dat=dat if len(dat) < 2500 else ("data<%d>" % len(dat)),
    )

    if res:
        if not len(dat):
            # ext.set_code(msg.to, b'')
            return 1, gas, msg.to
        gcost = len(dat) * opcodes.GCONTRACTBYTE
        if gas >= gcost and (len(dat) <= 24576):
            gas -= gcost
        else:
            log_msg.debug(
                "CONTRACT CREATION FAILED",
                have=gas,
                want=gcost,
                block_number=ext.block_number,
            )
            ext.revert(snapshot)
            return 0, 0, b""
        ext.set_code(msg.to, bytearray_to_bytestr(dat))
        log_msg.debug("SETTING CODE", addr=encode_hex(msg.to), lendat=len(dat))
        return 1, gas, msg.to
    else:
        ext.revert(snapshot)
        return 0, gas, dat
예제 #5
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
예제 #6
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)
예제 #7
0
def create_contract(ext, msg):
    log_msg.debug("CONTRACT CREATION")

    if msg.is_cross_shard:
        return 0, msg.gas, b""

    if msg.transfer_token_id != ext.default_state_token:
        # TODODLL calling smart contract with non QKC transfer_token_id is not supported
        return 0, msg.gas, b""

    code = msg.data.extract_all()

    if ext.tx_origin != msg.sender:
        ext.increment_nonce(msg.sender)

    if msg.sender == null_address:
        msg.to = mk_contract_address(msg.sender, msg.to_full_shard_key, 0)
        # msg.to = sha3(msg.sender + code)[12:]
    else:
        nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1)
        msg.to = mk_contract_address(msg.sender, msg.to_full_shard_key, nonce)

    if ext.get_nonce(msg.to) or len(ext.get_code(msg.to)):
        log_msg.debug("CREATING CONTRACT ON TOP OF EXISTING CONTRACT")
        return 0, 0, b""

    b = ext.get_balances(msg.to)
    if b != {}:
        ext.set_balances(msg.to, b)
        ext.set_nonce(msg.to, 0)
        ext.set_code(msg.to, b"")
        # ext.reset_storage(msg.to)

    msg.is_create = True
    # assert not ext.get_code(msg.to)
    msg.data = vm.CallData([], 0, 0)
    snapshot = ext.snapshot()

    ext.set_nonce(msg.to, 1)
    res, gas, dat = _apply_msg(ext, msg, code)

    log_msg.debug(
        "CONTRACT CREATION FINISHED",
        res=res,
        gas=gas,
        dat=dat if len(dat) < 2500 else ("data<%d>" % len(dat)),
    )

    if res:
        if not len(dat):
            # ext.set_code(msg.to, b'')
            return 1, gas, msg.to
        gcost = len(dat) * opcodes.GCONTRACTBYTE
        if gas >= gcost and (len(dat) <= 24576):
            gas -= gcost
        else:
            dat = []
            log_msg.debug(
                "CONTRACT CREATION FAILED",
                have=gas,
                want=gcost,
                block_number=ext.block_number,
            )
            ext.revert(snapshot)
            return 0, 0, b""
        ext.set_code(msg.to, bytearray_to_bytestr(dat))
        log_msg.debug("SETTING CODE", addr=encode_hex(msg.to), lendat=len(dat))
        return 1, gas, msg.to
    else:
        ext.revert(snapshot)
        return 0, gas, dat