예제 #1
0
def validate_transaction(state, tx):

    # (1) The transaction signature is valid;
    if not tx.sender:  # sender is set and validated on Transaction initialization
        raise UnsignedTransaction(tx)

    # assert config_fork_specific_validation(
    #     state.config, state.block_number, tx)

    # (2) the transaction nonce is valid (equivalent to the
    #     sender account's current nonce);
    req_nonce = 0 if tx.sender == null_address else state.get_nonce(tx.sender)
    if req_nonce != tx.nonce:
        raise InvalidNonce(rp(tx, "nonce", tx.nonce, req_nonce))

    # (3) the gas limit is no smaller than the intrinsic gas,
    # g0, used by the transaction;
    total_gas = tx.intrinsic_gas_used
    if tx.startgas < total_gas:
        raise InsufficientStartGas(rp(tx, "startgas", tx.startgas, total_gas))

    # (4) the sender account balance contains at least the
    # cost, v0, required in up-front payment.
    total_cost = tx.value + tx.gasprice * tx.startgas

    if state.get_balance(tx.sender, tx.gas_token_id) < total_cost:
        raise InsufficientBalance(
            rp(tx, "balance", state.get_balance(tx.sender, tx.gas_token_id),
               total_cost))

    # check block gas limit
    if state.gas_used + tx.startgas > state.gas_limit:
        raise BlockGasLimitReached(
            rp(tx, "gaslimit", state.gas_used + tx.startgas, state.gas_limit))

    # EIP86-specific restrictions
    if tx.sender == null_address and (tx.value != 0 or tx.gasprice != 0):
        raise InvalidTransaction(
            "EIP86 transactions must have 0 value and gasprice")

    return True
예제 #2
0
def validate_transaction(state, tx):
    # (1) The transaction signature is valid;
    if not tx.sender:  # sender is set and validated on Transaction initialization
        raise UnsignedTransaction(tx)

    if tx.version == 2:
        # When tx.version == 2 (EIP155 tx), check
        # 0. EIP155_SIGNER enable
        # 1. tx.v == tx.network_id * 2 + 35 (+ 1)
        # 2. gas_token_id & transfer_token_id should equal to default_token_id (like qkc)
        # 3. tx.from_chain_id == tx.to_chain_id and tx.from_shard_key = 0 & tx.to_shard_key = 0
        # 4. tx.network_id == chain_config.ETH_CHAIN_ID, where chain_config is derived from tx.from_chain_id
        chain_config = state.qkc_config.CHAINS[tx.from_chain_id]
        default_token_id = token_id_encode(chain_config.DEFAULT_CHAIN_TOKEN)
        if (state.qkc_config.ENABLE_EIP155_SIGNER_TIMESTAMP is not None
                and state.timestamp <
                state.qkc_config.ENABLE_EIP155_SIGNER_TIMESTAMP):
            raise InvalidTransaction("EIP155 Signer is not enable yet.")
        if tx.v != 35 + tx.network_id * 2 and tx.v != 36 + tx.network_id * 2:
            raise InvalidTransaction(
                "network_id {} does not match the signature v {}.".format(
                    tx.network_id, tx.v))
        if tx.from_chain_id != tx.to_chain_id:
            raise InvalidTransaction(
                "EIP155 Signer do not support cross shard transaction.")
        if tx.from_shard_key != 0 or tx.to_shard_key != 0:
            raise InvalidTransaction(
                "EIP155 Signer do not support cross shard transaction.")
        if tx.gas_token_id != default_token_id:
            raise InvalidTransaction(
                "EIP155 Signer only support {} as gas token.".format(
                    chain_config.DEFAULT_CHAIN_TOKEN))
        if tx.transfer_token_id != default_token_id:
            raise InvalidTransaction(
                "EIP155 Signer only support {} as transfer token.".format(
                    chain_config.DEFAULT_CHAIN_TOKEN))
        assert (tx.network_id == chain_config.ETH_CHAIN_ID,
                "Invalid network_id.")
        assert (
            tx.eth_chain_id - state.qkc_config.BASE_ETH_CHAIN_ID -
            1 == tx.from_chain_id,
            "Invalid Eth_Chain_Id.",
        )

    # (1a) startgas, gasprice, gas token id, transfer token id must be <= UINT128_MAX
    if (tx.startgas > UINT128_MAX or tx.gasprice > UINT128_MAX
            or tx.gas_token_id > TOKEN_ID_MAX
            or tx.transfer_token_id > TOKEN_ID_MAX):
        raise InvalidTransaction(
            "startgas, gasprice, and token_id must <= UINT128_MAX")

    # (2) the transaction nonce is valid (equivalent to the
    #     sender account's current nonce);
    req_nonce = state.get_nonce(tx.sender)
    if req_nonce != tx.nonce:
        raise InvalidNonce(rp(tx, "nonce", tx.nonce, req_nonce))

    # (3) the gas limit is no smaller than the intrinsic gas,
    # g0, used by the transaction;
    total_gas = tx.intrinsic_gas_used
    if tx.startgas < total_gas:
        raise InsufficientStartGas(rp(tx, "startgas", tx.startgas, total_gas))

    default_chain_token = state.shard_config.default_chain_token
    bal = {
        tx.transfer_token_id: state.get_balance(tx.sender,
                                                tx.transfer_token_id)
    }
    if tx.transfer_token_id != tx.gas_token_id:
        bal[tx.gas_token_id] = state.get_balance(tx.sender, tx.gas_token_id)

    # (4) requires non-zero balance for transfer_token_id and gas_token_id if non-default
    for token_id in [tx.transfer_token_id, tx.gas_token_id]:
        if token_id != default_chain_token and bal[token_id] == 0:
            raise InvalidNativeToken(
                "{}: non-default token {} has zero balance".format(
                    tx.__repr__(), token_id_decode(token_id)))

    # (5) the sender account balance contains at least the cost required in up-front payment
    cost = Counter({tx.transfer_token_id: tx.value}) + Counter(
        {tx.gas_token_id: tx.gasprice * tx.startgas})
    for token_id, b in bal.items():
        if b < cost[token_id]:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %s balance" % token_id_decode(token_id),
                    b,
                    cost[token_id],
                ))

    # (6) if gas token non-default, need to check system contract for gas conversion
    if tx.gasprice != 0 and tx.gas_token_id != default_chain_token:
        snapshot = state.snapshot()
        _, genesis_token_gas_price = pay_native_token_as_gas(
            state, tx.gas_token_id, tx.startgas, tx.gasprice)
        state.revert(snapshot)
        if genesis_token_gas_price == 0:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not ready for being used to pay gas"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))
        # should be guaranteed by previous check. check added to make sure
        bal_gas_reserve = state.get_balance(
            SystemContract.GENERAL_NATIVE_TOKEN.addr(), state.genesis_token)
        if bal_gas_reserve < genesis_token_gas_price * tx.startgas:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not enough reserve balance for conversion"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))

    # (7) check block gas limit
    if state.gas_used + tx.startgas > state.gas_limit:
        raise BlockGasLimitReached(
            rp(tx, "gaslimit", state.gas_used + tx.startgas, state.gas_limit))

    return True
예제 #3
0
def validate_transaction(state, tx):
    # (1) The transaction signature is valid;
    if not tx.sender:  # sender is set and validated on Transaction initialization
        raise UnsignedTransaction(tx)

    # (2) the transaction nonce is valid (equivalent to the
    #     sender account's current nonce);
    req_nonce = state.get_nonce(tx.sender)
    if req_nonce != tx.nonce:
        raise InvalidNonce(rp(tx, "nonce", tx.nonce, req_nonce))

    # (3) the gas limit is no smaller than the intrinsic gas,
    # g0, used by the transaction;
    total_gas = tx.intrinsic_gas_used
    if tx.startgas < total_gas:
        raise InsufficientStartGas(rp(tx, "startgas", tx.startgas, total_gas))

    # (4.0) require transfer_token_id and gas_token_id to be in allowed list
    if tx.transfer_token_id not in state.qkc_config.allowed_transfer_token_ids:
        raise InsufficientBalance(
            "{}: token {} is not in allowed transfer_token list".format(
                tx.__repr__(), token_id_decode(tx.transfer_token_id)))
    if tx.gas_token_id not in state.qkc_config.allowed_gas_token_ids:
        raise InsufficientBalance(
            "{}: token {} is not in allowed gas_token list".format(
                tx.__repr__(), token_id_decode(tx.gas_token_id)))

    # (4) the sender account balance contains at least the
    # cost, v0, required in up-front payment.
    if tx.transfer_token_id == tx.gas_token_id:
        total_cost = tx.value + tx.gasprice * tx.startgas
        if state.get_balance(tx.sender,
                             token_id=tx.transfer_token_id) < total_cost:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %d balance" % tx.transfer_token_id,
                    state.get_balance(tx.sender,
                                      token_id=tx.transfer_token_id),
                    total_cost,
                ))
    else:
        if state.get_balance(tx.sender,
                             token_id=tx.transfer_token_id) < tx.value:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %d balance" % tx.transfer_token_id,
                    state.get_balance(tx.sender,
                                      token_id=tx.transfer_token_id),
                    tx.value,
                ))
        if (state.get_balance(tx.sender, token_id=tx.gas_token_id) <
                tx.gasprice * tx.startgas):
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %d balance" % tx.gas_token_id,
                    state.get_balance(tx.sender, token_id=tx.gas_token_id),
                    tx.gasprice * tx.startgas,
                ))

    # check block gas limit
    if state.gas_used + tx.startgas > state.gas_limit:
        raise BlockGasLimitReached(
            rp(tx, "gaslimit", state.gas_used + tx.startgas, state.gas_limit))

    return True
예제 #4
0
def validate_transaction(state, tx):
    # (1) The transaction signature is valid;
    if not tx.sender:  # sender is set and validated on Transaction initialization
        raise UnsignedTransaction(tx)

    # (1a) startgas, gasprice, gas token id, transfer token id must be <= UINT128_MAX
    if (tx.startgas > UINT128_MAX or tx.gasprice > UINT128_MAX
            or tx.gas_token_id > TOKEN_ID_MAX
            or tx.transfer_token_id > TOKEN_ID_MAX):
        raise InvalidTransaction(
            "startgas, gasprice, and token_id must <= UINT128_MAX")

    # (2) the transaction nonce is valid (equivalent to the
    #     sender account's current nonce);
    req_nonce = state.get_nonce(tx.sender)
    if req_nonce != tx.nonce:
        raise InvalidNonce(rp(tx, "nonce", tx.nonce, req_nonce))

    # (3) the gas limit is no smaller than the intrinsic gas,
    # g0, used by the transaction;
    total_gas = tx.intrinsic_gas_used
    if tx.startgas < total_gas:
        raise InsufficientStartGas(rp(tx, "startgas", tx.startgas, total_gas))

    default_chain_token = state.shard_config.default_chain_token
    bal = {
        tx.transfer_token_id: state.get_balance(tx.sender,
                                                tx.transfer_token_id)
    }
    if tx.transfer_token_id != tx.gas_token_id:
        bal[tx.gas_token_id] = state.get_balance(tx.sender, tx.gas_token_id)

    # (4) requires non-zero balance for transfer_token_id and gas_token_id if non-default
    for token_id in [tx.transfer_token_id, tx.gas_token_id]:
        if token_id != default_chain_token and bal[token_id] == 0:
            raise InvalidNativeToken(
                "{}: non-default token {} has zero balance".format(
                    tx.__repr__(), token_id_decode(token_id)))

    # (5) the sender account balance contains at least the cost required in up-front payment
    cost = Counter({tx.transfer_token_id: tx.value}) + Counter(
        {tx.gas_token_id: tx.gasprice * tx.startgas})
    for token_id, b in bal.items():
        if b < cost[token_id]:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %s balance" % token_id_decode(token_id),
                    b,
                    cost[token_id],
                ))

    # (6) if gas token non-default, need to check system contract for gas conversion
    if tx.gasprice != 0 and tx.gas_token_id != default_chain_token:
        snapshot = state.snapshot()
        _, genesis_token_gas_price = pay_native_token_as_gas(
            state, tx.gas_token_id, tx.startgas, tx.gasprice)
        state.revert(snapshot)
        if genesis_token_gas_price == 0:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not ready for being used to pay gas"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))
        # should be guaranteed by previous check. check added to make sure
        bal_gas_reserve = state.get_balance(
            SystemContract.GENERAL_NATIVE_TOKEN.addr(), state.genesis_token)
        if bal_gas_reserve < genesis_token_gas_price * tx.startgas:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not enough reserve balance for conversion"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))

    # (7) check block gas limit
    if state.gas_used + tx.startgas > state.gas_limit:
        raise BlockGasLimitReached(
            rp(tx, "gaslimit", state.gas_used + tx.startgas, state.gas_limit))

    return True