def proc_transfer_mnt(ext, msg): from quarkchain.evm.messages import apply_msg # Data must be >= 96 bytes if msg.data.size < 96: return 0, 0, [] gascost = 3 if msg.gas < gascost: return 0, 0, [] to = utils.int_to_addr(msg.data.extract32(0)) mnt = msg.data.extract32(32) value = msg.data.extract32(64) data = msg.data.extract_all(96) new_msg = vm.Message( msg.sender, to, value, msg.gas - gascost, data, msg.depth + 1, code_address=to, static=msg.static, transfer_token_id=mnt, gas_token_id=msg.gas_token_id, ) return apply_msg(ext, new_msg)
def proc_deploy_system_contract(ext, msg): from quarkchain.evm.messages import create_contract gascost = 3 if msg.gas < gascost: return 0, 0, [] data = msg.data.extract32(0) contract_index = data if data else 1 try: target_addr, bytecode, enable_ts = _system_contracts[SystemContract( contract_index)] except (ValueError, KeyError): # Not a valid `SystemContract` or the dict doesn't contain its info return 0, 0, [] if ext.block_timestamp < enable_ts: return 0, 0, [] new_msg = vm.Message( msg.to, # current special address b"", 0, msg.gas - gascost, bytecode, msg.depth + 1, to_full_shard_key=msg.to_full_shard_key, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) # Use predetermined contract address return create_contract(ext, new_msg, target_addr)
def proc_deploy_system_contract(ext, msg): from quarkchain.evm.messages import create_contract gascost = 3 if msg.gas < gascost: return 0, 0, [] data = msg.data.extract32(0) contract_index = data if data else 1 if contract_index not in [e.value for e in SystemContract]: return 0, 0, [] target_addr, bytecode, enable_ts = _system_contracts[SystemContract( contract_index)] if ext.block_timestamp < enable_ts: return 0, 0, [] new_msg = vm.Message( msg.to, # current special address b"", 0, msg.gas - gascost, bytecode, msg.depth + 1, to_full_shard_key=msg.to_full_shard_key, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) # Use predetermined contract address return create_contract(ext, new_msg, target_addr)
def apply_message(state, msg=None, **kwargs): if msg is None: msg = vm.Message(**kwargs) else: assert not kwargs ext = VMExt(state, transactions.Transaction(0, 0, 21000, b"", 0, b"")) result, gas_remained, data = apply_msg(ext, msg) return bytearray_to_bytestr(data) if result else None
def proc_transfer_mnt(ext, msg): from quarkchain.evm.messages import apply_msg # Data must be >= 96 bytes and static call not allowed if msg.data.size < 96 or msg.static: return 0, 0, [] to = utils.int_to_addr(msg.data.extract32(0)) token_id = msg.data.extract32(32) value = msg.data.extract32(64) data = msg.data.extract_all(96) # Token ID should be within range if token_id > TOKEN_ID_MAX: return 0, 0, [] # Doesn't allow target address to be this precompiled contract itself if to == decode_hex(b"000000000000000000000000000000514b430002"): return 0, 0, [] gascost = 0 if value > 0: gascost += opcodes.GCALLVALUETRANSFER if not ext.account_exists(to): gascost += opcodes.GCALLNEWACCOUNT # Out of gas if msg.gas < gascost: return 0, 0, [] # Handle insufficient balance or exceeding max call depth if ext.get_balance(msg.sender, token_id) < value or msg.depth >= vm.MAX_DEPTH: return 0, msg.gas - gascost, [] new_msg = vm.Message( msg.sender, to, value, msg.gas - gascost + opcodes.GSTIPEND * (value > 0), data, msg.depth + 1, code_address=to, static=False, transfer_token_id=token_id, gas_token_id=msg.gas_token_id, ) return apply_msg(ext, new_msg)
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"", )
def proc_deploy_root_chain_staking_contract(ext, msg): from quarkchain.evm.messages import create_contract gascost = 3 if msg.gas < gascost: return 0, 0, [] target_addr, bytecode = _system_contracts[SystemContract.ROOT_CHAIN_POSW] new_msg = vm.Message( msg.to, # current special address b"", 0, msg.gas - gascost, bytecode, msg.depth + 1, to_full_shard_key=msg.to_full_shard_key, transfer_token_id=msg.transfer_token_id, gas_token_id=msg.gas_token_id, ) # Use predetermined contract address return create_contract(ext, new_msg, target_addr)
def _call_general_native_token_manager(state, data: bytes) -> (int, int): contract_addr = SystemContract.GENERAL_NATIVE_TOKEN.addr() code = state.get_code(contract_addr) if not code: return 0, 0 # Only contract itself can invoke payment sender = contract_addr # Call the `calculateGasPrice` function message = vm.Message( sender, contract_addr, 0, 1000000, # Mock gas to guarantee msg will be applied data, code_address=contract_addr, ) ext = VMExt(state, sender, gas_price=0) result, _, output = apply_msg(ext, message) if not result: return 0, 0 refund_rate = int.from_bytes(output[:32], byteorder="big") converted_gas_price = int.from_bytes(output[32:64], byteorder="big") return refund_rate, converted_gas_price
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
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(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)