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
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
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
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