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) success = 1 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, tx.to_full_shard_key, state.get_nonce(tx.sender) ), 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, ) ) 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 # 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)
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
def apply_transaction_message( state, message, ext, should_create_contract, gas_used_start, is_cross_shard=False, contract_address=b"", ): local_fee_rate = ( 1 - state.qkc_config.reward_tax_rate if state.qkc_config else Fraction(1) ) evm_gas_start = message.gas if not should_create_contract: result, gas_remained, data = apply_msg(ext, message) contract_address = b"" else: # CREATE result, gas_remained, data = create_contract(ext, message, contract_address) 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 = evm_gas_start - gas_remained + gas_used_start if not result: log_tx.debug( "TX FAILED", reason="out of gas or transfer value is 0 and transfer token is non-default and un-queried", gas_remained=gas_remained, ) 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 if not should_create_contract: output = bytearray_to_bytestr(data) else: output = data success = 1 # sell remaining gas state.delta_token_balance( message.sender, message.gas_token_id, ext.tx_gasprice * gas_remained ) fee = ( ext.tx_gasprice * gas_used * local_fee_rate.numerator // local_fee_rate.denominator ) state.delta_token_balance(state.block_coinbase, message.gas_token_id, fee) add_dict(state.block_fee_tokens, {message.gas_token_id: fee}) state.gas_used += gas_used # Clear suicides suicides = state.suicides state.suicides = [] for s in suicides: state.set_balances(s, {}) state.del_account(s) # Construct a receipt r = mk_receipt(state, success, state.logs, contract_address, state.full_shard_key) state.logs = [] if is_cross_shard: if ( state.qkc_config.XSHARD_ADD_RECIEPT_TIMESTAMP is not None and state.timestamp >= state.qkc_config.XSHARD_ADD_RECIEPT_TIMESTAMP ): state.add_xshard_deposit_receipt(r) else: state.add_receipt(r) return success, output
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, 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, 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, transfer_token_id=tx.transfer_token_id, gas_token_id=tx.gas_token_id, ) # 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_token_balance(tx.sender, tx.gas_token_id, tx.gasprice * gas_remained) fee = (tx.gasprice * gas_used * 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, {tx.gas_token_id: fee}) 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_token_balance(tx.sender, tx.gas_token_id, 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_token_balance(state.block_coinbase, tx.gas_token_id, fee) add_dict(state.block_fee_tokens, {tx.gas_token_id: 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_balances(s, {}) state.del_account(s) # 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