def create_transfer_transaction( shard_state, key, from_address, to_address, value, gas=21000, # transfer tx min gas gas_price=1, nonce=None, data=b"", gas_token_id=None, transfer_token_id=None, ): if gas_token_id is None: gas_token_id = shard_state.env.quark_chain_config.genesis_token if transfer_token_id is None: transfer_token_id = shard_state.env.quark_chain_config.genesis_token """ Create an in-shard xfer tx """ evm_tx = EvmTransaction( nonce=shard_state.get_transaction_count(from_address.recipient) if nonce is None else nonce, gasprice=gas_price, startgas=gas, to=to_address.recipient, value=value, data=data, from_full_shard_key=from_address.full_shard_key, to_full_shard_key=to_address.full_shard_key, network_id=shard_state.env.quark_chain_config.NETWORK_ID, gas_token_id=gas_token_id, transfer_token_id=transfer_token_id, ) evm_tx.sign(key=key) return TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx))
def contract_creation_tx( shard_state, key, from_address, to_full_shard_key, bytecode, gas=100000, gas_token_id=None, transfer_token_id=None, ): if gas_token_id is None: gas_token_id = shard_state.env.quark_chain_config.genesis_token if transfer_token_id is None: transfer_token_id = shard_state.env.quark_chain_config.genesis_token evm_tx = EvmTransaction( nonce=shard_state.get_transaction_count(from_address.recipient), gasprice=1, startgas=gas, value=0, to=b"", data=bytes.fromhex(bytecode), from_full_shard_key=from_address.full_shard_key, to_full_shard_key=to_full_shard_key, network_id=shard_state.env.quark_chain_config.NETWORK_ID, gas_token_id=gas_token_id, transfer_token_id=transfer_token_id, ) evm_tx.sign(key) return TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx))
def create_transfer_transaction( shard_state, key, from_address, to_address, value, gas=21000, # transfer tx min gas gas_price=1, nonce=None, data=b"", ): """ Create an in-shard xfer tx """ evm_tx = EvmTransaction( nonce=shard_state.get_transaction_count(from_address.recipient) if nonce is None else nonce, gasprice=gas_price, startgas=gas, to=to_address.recipient, value=value, data=data, from_full_shard_id=from_address.full_shard_id, to_full_shard_id=to_address.full_shard_id, network_id=shard_state.env.quark_chain_config.NETWORK_ID, ) evm_tx.sign(key=key) return Transaction(in_list=[], code=Code.create_evm_code(evm_tx), out_list=[])
def create_transaction(self, account, nonce, x_shard_percent, sample_evm_tx) -> Optional[Transaction]: shard_size = self.qkc_config.SHARD_SIZE shard_mask = shard_size - 1 from_shard = self.shard_id # skip if from shard is specified and not matching current branch # FIXME: it's possible that clients want to specify '0x0' as the full shard ID, however it will not be supported if (sample_evm_tx.from_full_shard_id and (sample_evm_tx.from_full_shard_id & shard_mask) != from_shard): return None if sample_evm_tx.from_full_shard_id: from_full_shard_id = sample_evm_tx.from_full_shard_id else: from_full_shard_id = (account.address.full_shard_id & (~shard_mask) | from_shard) if not sample_evm_tx.to: to_address = random.choice(self.accounts).address recipient = to_address.recipient to_full_shard_id = to_address.full_shard_id & ( ~shard_mask) | from_shard else: recipient = sample_evm_tx.to to_full_shard_id = from_full_shard_id if random.randint(1, 100) <= x_shard_percent: # x-shard tx to_shard = random.randint(0, self.qkc_config.SHARD_SIZE - 1) if to_shard == self.shard_id: to_shard = (to_shard + 1) % self.qkc_config.SHARD_SIZE to_full_shard_id = to_full_shard_id & (~shard_mask) | to_shard value = sample_evm_tx.value if not sample_evm_tx.data: value = random.randint(1, 100) * (10**15) gas_token_id = 1 transfer_token_id = 1 evm_tx = EvmTransaction( nonce=nonce, gasprice=sample_evm_tx.gasprice, startgas=sample_evm_tx.startgas, gas_token_id=gas_token_id, to=recipient, value=value, transfer_token_id=transfer_token_id, data=sample_evm_tx.data, from_full_shard_id=from_full_shard_id, to_full_shard_id=to_full_shard_id, network_id=self.qkc_config.NETWORK_ID, ) evm_tx.sign(account.key) return Transaction(code=Code.create_evm_code(evm_tx))
def test_sendTransaction(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_key=0) acc2 = Address.create_random_account(full_shard_key=1) with ClusterContext( 1, acc1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): slaves = clusters[0].slave_list master = clusters[0].master block = call_async( master.get_next_block_to_mine(address=acc2, branch_value=None)) call_async(master.add_root_block(block)) evm_tx = EvmTransaction( nonce=0, gasprice=6, startgas=30000, to=acc2.recipient, value=15, data=b"", from_full_shard_key=acc1.full_shard_key, to_full_shard_key=acc2.full_shard_key, network_id=slaves[0].env.quark_chain_config.NETWORK_ID, gas_token_id=master.env.quark_chain_config.genesis_token, transfer_token_id=master.env.quark_chain_config.genesis_token, ) evm_tx.sign(id1.get_key()) request = dict( to="0x" + acc2.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", # 15 v=quantity_encoder(evm_tx.v), r=quantity_encoder(evm_tx.r), s=quantity_encoder(evm_tx.s), nonce="0x0", fromFullShardKey="0x00000000", toFullShardKey="0x00000001", network_id=hex(slaves[0].env.quark_chain_config.NETWORK_ID), ) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) response = send_request("sendTransaction", [request]) self.assertEqual(response, "0x" + tx.get_hash().hex() + "00000000") state = clusters[0].get_shard_state(2 | 0) self.assertEqual(len(state.tx_queue), 1) self.assertEqual( state.tx_queue.pop_transaction(state.get_transaction_count), evm_tx)
def create_transaction(address, key, nonce, data, network_id) -> EvmTransaction: evm_tx = EvmTransaction( nonce=nonce, gasprice=1, startgas=1000000, to=b"", value=0, data=data, from_full_shard_key=address.full_shard_key, to_full_shard_key=address.full_shard_key, network_id=network_id, ) evm_tx.sign(key) return evm_tx
def create_transaction(address, key, nonce, to, data, network_id) -> EvmTransaction: evm_tx = EvmTransaction( nonce=nonce, gasprice=1, startgas=1000000, to=to.recipient, value=1000000 * (10**18), data=data, from_full_shard_id=address.full_shard_id, to_full_shard_id=to.full_shard_id, network_id=network_id, ) evm_tx.sign(key) return evm_tx
def make_test_tx(s=100000, g=50, data='', nonce=0): return Transaction(nonce=nonce, startgas=s, gasprice=g, value=0, data=data, to=b'\x35' * 20)
def create_transaction(self, account, nonce, x_shard_percent, sample_evm_tx) -> Optional[TypedTransaction]: # skip if from shard is specified and not matching current branch # FIXME: it's possible that clients want to specify '0x0' as the full shard ID, however it will not be supported if (sample_evm_tx.from_full_shard_key and self.qkc_config.get_full_shard_id_by_full_shard_key( sample_evm_tx.from_full_shard_key) != self.full_shard_id): return None if sample_evm_tx.from_full_shard_key: from_full_shard_key = sample_evm_tx.from_full_shard_key else: from_full_shard_key = self.full_shard_id if not sample_evm_tx.to: to_address = random.choice(self.accounts).address recipient = to_address.recipient to_full_shard_key = self.full_shard_id else: recipient = sample_evm_tx.to to_full_shard_key = from_full_shard_key if random.randint(1, 100) <= x_shard_percent: # x-shard tx to_full_shard_id = random.choice( self.qkc_config.get_full_shard_ids() - [self.full_shard_id]) to_full_shard_key = to_full_shard_id value = sample_evm_tx.value if not sample_evm_tx.data: value = random.randint(1, 100) * (10**15) evm_tx = EvmTransaction( nonce=nonce, gasprice=sample_evm_tx.gasprice, startgas=sample_evm_tx.startgas, to=recipient, value=value, data=sample_evm_tx.data, gas_token_id=sample_evm_tx.gas_token_id, transfer_token_id=sample_evm_tx.transfer_token_id, from_full_shard_key=from_full_shard_key, to_full_shard_key=to_full_shard_key, network_id=self.qkc_config.NETWORK_ID, ) evm_tx.sign(account.key) return TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx))
async def donate(self, from_address, to_address, value=hex(10 ** 18)): """Faucet function to send value (default 1 token) from from_address to to_address. from_address must be one of the addresses in genesis_data/alloc.json. Only allow one pending tx at a time. Return tx id if success else None """ if value > 100 * (10 ** 18): return None key = self.master.env.quark_chain_config.alloc_accounts.get( from_address.hex(), None ) if not key: return None from_address = Address.deserialize(from_address) to_address = Address.deserialize(to_address) # Do nothing if there is already a pending tx result = await self.master.get_transactions_by_address( from_address, bytes(1), 1 ) if result: tx_list, next_token = result if tx_list: return None account_branch_data = await self.master.get_primary_account_data(from_address) nonce = account_branch_data.transaction_count network_id = self.master.env.quark_chain_config.NETWORK_ID evm_tx = EvmTransaction( nonce, 10 ** 9, 30000, to_address.recipient, value, b"", from_full_shard_id=from_address.full_shard_id, to_full_shard_id=to_address.full_shard_id, network_id=network_id, ) evm_tx.sign(key) tx = Transaction(code=Code.create_evm_code(evm_tx)) success = await self.master.add_transaction(tx) if not success: return None return id_encoder(tx.get_hash(), from_address.full_shard_id)
def _contract_tx_gen(shard_state, key, from_address, to_full_shard_id, bytecode): evm_tx = EvmTransaction( nonce=shard_state.get_transaction_count(from_address.recipient), gasprice=1, startgas=1000000, value=0, to=b"", data=bytes.fromhex(bytecode), from_full_shard_id=from_address.full_shard_id, to_full_shard_id=to_full_shard_id, network_id=shard_state.env.quark_chain_config.NETWORK_ID, ) evm_tx.sign(key) return Transaction(in_list=[], code=Code.create_evm_code(evm_tx), out_list=[])
def make_test_tx(s=100000, g=50, data=b"", nonce=0, key=None): evm_tx = EvmTransaction( nonce=nonce, startgas=s, gasprice=g, value=0, data=data, to=b"\x35" * 20, gas_token_id=0, transfer_token_id=0, r=1, s=1, v=28, ) if key: evm_tx.sign(key=key) return TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx))
def test_sendTransaction(self): id1 = Identity.create_random_identity() acc1 = Address.create_from_identity(id1, full_shard_id=0) acc2 = Address.create_random_account(full_shard_id=1) with ClusterContext( 1, acc1, small_coinbase=True) as clusters, jrpc_server_context( clusters[0].master): slaves = clusters[0].slave_list branch = Branch.create(2, 0) evm_tx = EvmTransaction( nonce=0, gasprice=6, startgas=30000, to=acc2.recipient, value=15, data=b"", from_full_shard_id=acc1.full_shard_id, to_full_shard_id=acc2.full_shard_id, network_id=slaves[0].env.quark_chain_config.NETWORK_ID, ) evm_tx.sign(id1.get_key()) request = dict( to="0x" + acc2.recipient.hex(), gasPrice="0x6", gas=hex(30000), value="0xf", # 15 v=quantity_encoder(evm_tx.v), r=quantity_encoder(evm_tx.r), s=quantity_encoder(evm_tx.s), nonce="0x0", fromFullShardId="0x00000000", toFullShardId="0x00000001", network_id=hex(slaves[0].env.quark_chain_config.NETWORK_ID), ) tx = Transaction(code=Code.create_evm_code(evm_tx)) response = send_request("sendTransaction", [request]) self.assertEqual(response, "0x" + tx.get_hash().hex() + "00000000") self.assertEqual(len(slaves[0].shards[branch].state.tx_queue), 1) self.assertEqual( slaves[0].shards[branch].state.tx_queue.pop_transaction(), evm_tx)
def test_perf_evm(): N = 5000 IDN = 10 print("Creating %d identities" % IDN) id_list = [] for i in range(IDN): id_list.append(Identity.create_random_identity()) acc_list = [] for i in range(IDN): acc_list.append(Address.create_from_identity(id_list[i])) print("Creating %d transactions..." % N) start_time = time.time() tx_list = [] from_list = [] for i in range(N): from_id = id_list[random.randint(0, IDN - 1)] to_addr = acc_list[random.randint(0, IDN - 1)] evm_tx = EvmTransaction( nonce=0, gasprice=1, startgas=2, to=to_addr.recipient, value=3, data=b"", from_full_shard_key=0, to_full_shard_key=0, network_id=1, ) evm_tx.sign(key=from_id.get_key()) tx_list.append(evm_tx) from_list.append(from_id.get_recipient()) duration = time.time() - start_time print("Creations PS: %.2f" % (N / duration)) print("Verifying transactions") start_time = time.time() for i in range(N): tx_list[i]._sender = None assert tx_list[i].sender == from_list[i] duration = time.time() - start_time print("Verifications PS: %.2f" % (N / duration))
def test_tx_size(self): id1 = Identity.create_from_key(b"0" * 32) acc1 = Address.create_from_identity(id1, full_shard_key=0) evm_tx = EvmTransaction( nonce=0, gasprice=1, startgas=30000, to=acc1.recipient, value=0, data=b"", from_full_shard_key=0xFFFF, to_full_shard_key=0xFFFF, network_id=1, gas_token_id=12345, transfer_token_id=1234, ) evm_tx.sign(key=id1.get_key()) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) self.assertEqual(len(tx.serialize()), TX_MIN_SIZE) evm_tx = EvmTransaction( nonce=TT256 - 1, gasprice=TT256 - 1, startgas=TT256 - 1, to=acc1.recipient, value=TT256 - 1, data=b"", from_full_shard_key=SHARD_KEY_MAX, to_full_shard_key=SHARD_KEY_MAX, network_id=1, gas_token_id=TOKEN_ID_MAX, transfer_token_id=TOKEN_ID_MAX, ) evm_tx.sign(key=id1.get_key()) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) self.assertEqual(len(tx.serialize()), TX_MAX_SIZE)
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 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)
class TestTypedSignature(unittest.TestCase): raw_tx = Transaction( nonce=0x0d, gasprice=0x02540be400, startgas=0x7530, to=bytes.fromhex("314b2cd22c6d26618ce051a58c65af1253aecbb8"), value=0x056bc75e2d63100000, data=b"", from_full_shard_key=0xc47decfd, to_full_shard_key=0xc49c1950, network_id=0x03, ) tx = [ { "type": "uint256", "name": "nonce", "value": "0x0d" }, { "type": "uint256", "name": "gasPrice", "value": "0x02540be400" }, { "type": "uint256", "name": "gasLimit", "value": "0x7530" }, { "type": "uint160", "name": "to", "value": "0x314b2cd22c6d26618ce051a58c65af1253aecbb8", }, { "type": "uint256", "name": "value", "value": "0x056bc75e2d63100000" }, { "type": "bytes", "name": "data", "value": "0x" }, { "type": "uint32", "name": "fromFullShardId", "value": "0xc47decfd" }, { "type": "uint32", "name": "toFullShardId", "value": "0xc49c1950" }, { "type": "uint256", "name": "networkId", "value": "0x03" }, { "type": "string", "name": "qkcDomain", "value": "bottom-quark" }, ] def test_typed(self): assert tx_to_typed_data(self.raw_tx) == self.tx def test_solidity_pack(self): schema = list( map(lambda x: "{} {}".format(x["type"], x["name"]), self.tx)) types = list(map(lambda x: x["type"], self.tx)) data = list( map( lambda x: bytes.fromhex(x["value"][2:]) if x["type"] == "bytes" else x["value"], self.tx, )) h1 = solidity_pack(["string"] * len(self.tx), schema) h2 = solidity_pack(types, data) assert ( h1.hex() == "75696e74323536206e6f6e636575696e7432353620676173507269636575696e74323536206761734c696d697475696e7431363020746f75696e743235362076616c75656279746573206461746175696e7433322066726f6d46756c6c5368617264496475696e74333220746f46756c6c5368617264496475696e74323536206e6574776f726b4964737472696e6720716b63446f6d61696e" ) assert ( h2.hex() == "000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000000000000000007530314b2cd22c6d26618ce051a58c65af1253aecbb80000000000000000000000000000000000000000000000056bc75e2d63100000c47decfdc49c19500000000000000000000000000000000000000000000000000000000000000003626f74746f6d2d717561726b" ) def test_typed_signature_hash(self): h = typed_signature_hash(self.tx) assert h == "0x57dfcc7be8e4249fb6e75a45dc5ecdfed0309ed951b6adc69b8a659c7eca33bf" def test_recover(self): """ """ self.raw_tx._in_mutable_context = True self.raw_tx.version = 1 self.raw_tx.r = ( 0xb5145678e43df2b7ea8e0e969e51dbf72c956dd52e234c95393ad68744394855) self.raw_tx.s = ( 0x44515b465dbbf746a484239c11adb98f967e35347e17e71b84d850d8e5c38a6a) self.raw_tx.v = 0x1b self.raw_tx._in_mutable_context = False assert self.raw_tx.sender == bytes.fromhex( "8b74a79290a437aa9589be3227d9bb81b22beff1")
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
class TestTypedSignature(unittest.TestCase): raw_tx = Transaction( nonce=0x0d, gasprice=0x02540be400, startgas=0x7530, to=bytes.fromhex("314b2cd22c6d26618ce051a58c65af1253aecbb8"), value=0x056bc75e2d63100000, data=b"", network_id=0x03, from_full_shard_key=0xc47decfd, to_full_shard_key=0xc49c1950, gas_token_id=0x0111, transfer_token_id=0x0222, ) tx = [ { "type": "uint256", "name": "nonce", "value": "0x0d" }, { "type": "uint256", "name": "gasPrice", "value": "0x02540be400" }, { "type": "uint256", "name": "gasLimit", "value": "0x7530" }, { "type": "uint160", "name": "to", "value": "0x314b2cd22c6d26618ce051a58c65af1253aecbb8", }, { "type": "uint256", "name": "value", "value": "0x056bc75e2d63100000" }, { "type": "bytes", "name": "data", "value": "0x" }, { "type": "uint256", "name": "networkId", "value": "0x03" }, { "type": "uint32", "name": "fromFullShardKey", "value": "0xc47decfd" }, { "type": "uint32", "name": "toFullShardKey", "value": "0xc49c1950" }, { "type": "uint64", "name": "gasTokenId", "value": "0x0111" }, { "type": "uint64", "name": "transferTokenId", "value": "0x0222" }, { "type": "string", "name": "qkcDomain", "value": "bottom-quark" }, ] def test_typed(self): self.assertEqual(tx_to_typed_data(self.raw_tx), self.tx) def test_solidity_pack(self): schema = list( map(lambda x: "{} {}".format(x["type"], x["name"]), self.tx)) types = list(map(lambda x: x["type"], self.tx)) data = list( map( lambda x: bytes.fromhex(x["value"][2:]) if x["type"] == "bytes" else x["value"], self.tx, )) h1 = solidity_pack(["string"] * len(self.tx), schema) h2 = solidity_pack(types, data) self.assertEqual( h1.hex(), "75696e74323536206e6f6e636575696e7432353620676173507269636575696e74323536206761734c696d697475696e7431363020746f75696e743235362076616c75656279746573206461746175696e74323536206e6574776f726b496475696e7433322066726f6d46756c6c53686172644b657975696e74333220746f46756c6c53686172644b657975696e74363420676173546f6b656e496475696e743634207472616e73666572546f6b656e4964737472696e6720716b63446f6d61696e", ) self.assertEqual( h2.hex(), "000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000000000000000007530314b2cd22c6d26618ce051a58c65af1253aecbb80000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000000000000000000003c47decfdc49c195000000000000001110000000000000222626f74746f6d2d717561726b", ) def test_typed_signature_hash(self): h = typed_signature_hash(self.tx) self.assertEqual( h, "0xe768719d0a211ffb0b7f9c7bc6af9286136b3dd8b6be634a57dc9d6bee35b492" ) def test_recover(self): """ """ self.raw_tx._in_mutable_context = True self.raw_tx.version = 1 self.raw_tx.r = ( 0xb5145678e43df2b7ea8e0e969e51dbf72c956dd52e234c95393ad68744394855) self.raw_tx.s = ( 0x44515b465dbbf746a484239c11adb98f967e35347e17e71b84d850d8e5c38a6a) self.raw_tx.v = 0x1b self.raw_tx._in_mutable_context = False self.assertEqual(self.raw_tx.sender.hex(), "2e6144d0a4786e6f62892eee59c24d1e81e33272")