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))
async def sendTransaction(self, **data): def get_data_default(key, decoder, default=None): if key in data: return decoder(data[key]) return default to = get_data_default("to", recipient_decoder, b"") startgas = get_data_default("gas", quantity_decoder, DEFAULT_STARTGAS) gasprice = get_data_default("gasPrice", quantity_decoder, DEFAULT_GASPRICE) gas_token_id = get_data_default("gasTokenId", quantity_decoder, 0) value = get_data_default("value", quantity_decoder, 0) transfer_token_id = get_data_default("transfer_token_id", quantity_decoder, 0) data_ = get_data_default("data", data_decoder, b"") v = get_data_default("v", quantity_decoder, 0) r = get_data_default("r", quantity_decoder, 0) s = get_data_default("s", quantity_decoder, 0) nonce = get_data_default("nonce", quantity_decoder, None) to_full_shard_id = get_data_default("toFullShardId", full_shard_id_decoder, None) from_full_shard_id = get_data_default("fromFullShardId", full_shard_id_decoder, None) network_id = get_data_default( "networkId", quantity_decoder, self.master.env.quark_chain_config.NETWORK_ID) if nonce is None: raise InvalidParams("Missing nonce") if not (v and r and s): raise InvalidParams("Missing v, r, s") if from_full_shard_id is None: raise InvalidParams("Missing fromFullShardId") if to_full_shard_id is None: to_full_shard_id = from_full_shard_id evm_tx = EvmTransaction( nonce, gasprice, startgas, gas_token_id, to, value, transfer_token_id, data_, v, r, s, from_full_shard_id=from_full_shard_id, to_full_shard_id=to_full_shard_id, network_id=network_id, ) 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_full_shard_id)
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=[])
async def createTransactions(self, **load_test_data): """Create transactions for load testing""" def get_data_default(key, decoder, default=None): if key in load_test_data: return decoder(load_test_data[key]) return default num_tx_per_shard = load_test_data["numTxPerShard"] x_shard_percent = load_test_data["xShardPercent"] to = get_data_default("to", recipient_decoder, b"") startgas = get_data_default("gas", quantity_decoder, DEFAULT_STARTGAS) gasprice = get_data_default("gasPrice", quantity_decoder, int(DEFAULT_GASPRICE / 10)) value = get_data_default("value", quantity_decoder, 0) data = get_data_default("data", data_decoder, b"") # FIXME: can't support specifying full shard ID to 0. currently is regarded as not set from_full_shard_id = get_data_default("fromFullShardId", full_shard_id_decoder, 0) # build sample tx evm_tx_sample = EvmTransaction( 0, gasprice, startgas, to, value, data, from_full_shard_id=from_full_shard_id, ) tx = Transaction(code=Code.create_evm_code(evm_tx_sample)) return await self.master.create_transactions(num_tx_per_shard, x_shard_percent, tx)
def test_serialized_evm_tx(self): evm_tx = EvmTransaction(1, 2, 3, b"", 4, b"", 5, 6) serialized_evm_tx = SerializedEvmTransaction.from_evm_tx(evm_tx) typed_tx = TypedTransaction(serialized_evm_tx) typed_tx_bytes = typed_tx.serialize() recovered_typed_tx = TypedTransaction.deserialize(typed_tx_bytes) self.assertEqual(recovered_typed_tx.tx, serialized_evm_tx) self.assertEqual(recovered_typed_tx.tx.to_evm_tx(), evm_tx)
async def _call_or_estimate_gas(self, is_call: bool, **data): """ Returns the result of the transaction application without putting in block chain """ if not isinstance(data, dict): raise InvalidParams("Transaction must be an object") def get_data_default(key, decoder, default=None): if key in data: return decoder(data[key]) return default to = get_data_default("to", address_decoder, None) if to is None: raise InvalidParams("Missing to") to_full_shard_key = int.from_bytes(to[20:], "big") gas = get_data_default("gas", quantity_decoder, 0) gas_price = get_data_default("gasPrice", quantity_decoder, 0) value = get_data_default("value", quantity_decoder, 0) data_ = get_data_default("data", data_decoder, b"") sender = get_data_default("from", address_decoder, b"\x00" * 20 + to[20:]) sender_address = Address.create_from(sender) gas_token_id = get_data_default( "gas_token_id", quantity_decoder, self.env.quark_chain_config.genesis_token) transfer_token_id = get_data_default( "transfer_token_id", quantity_decoder, self.env.quark_chain_config.genesis_token, ) network_id = self.master.env.quark_chain_config.NETWORK_ID nonce = 0 # slave will fill in the real nonce evm_tx = EvmTransaction( nonce, gas_price, gas, to[:20], value, data_, from_full_shard_key=sender_address.full_shard_key, to_full_shard_key=to_full_shard_key, network_id=network_id, gas_token_id=gas_token_id, transfer_token_id=transfer_token_id, ) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) if is_call: res = await self.master.execute_transaction( tx, sender_address, data["block_height"]) return data_encoder(res) if res is not None else None else: # estimate gas res = await self.master.estimate_gas(tx, sender_address) return quantity_encoder(res) if res is not None else None
async def sendUnsigedTransaction(self, **data): """ Returns the unsigned hash of the evm transaction """ if not isinstance(data, dict): raise InvalidParams("Transaction must be an object") def get_data_default(key, decoder, default=None): if key in data: return decoder(data[key]) return default nonce = get_data_default("nonce", quantity_decoder, None) to = get_data_default("to", recipient_decoder, b"") startgas = get_data_default("gas", quantity_decoder, DEFAULT_STARTGAS) gasprice = get_data_default("gasPrice", quantity_decoder, DEFAULT_GASPRICE) value = get_data_default("value", quantity_decoder, 0) data_ = get_data_default("data", data_decoder, b"") from_full_shard_id = get_data_default("fromFullShardId", full_shard_id_decoder, None) to_full_shard_id = get_data_default("toFullShardId", full_shard_id_decoder, None) if nonce is None: raise InvalidParams("nonce is missing") if from_full_shard_id is None: raise InvalidParams("fromFullShardId is missing") if to_full_shard_id is None: to_full_shard_id = from_full_shard_id evm_tx = EvmTransaction( nonce, gasprice, startgas, to, value, data_, from_full_shard_id=from_full_shard_id, to_full_shard_id=to_full_shard_id, network_id=self.master.env.quark_chain_config.NETWORK_ID, ) return { "txHashUnsigned": data_encoder(evm_tx.hash_unsigned), "nonce": quantity_encoder(evm_tx.nonce), "to": data_encoder(evm_tx.to), "fromFullShardId": full_shard_id_encoder(evm_tx.from_full_shard_id), "toFullShardId": full_shard_id_encoder(evm_tx.to_full_shard_id), "value": quantity_encoder(evm_tx.value), "gasPrice": quantity_encoder(evm_tx.gasprice), "gas": quantity_encoder(evm_tx.startgas), "data": data_encoder(evm_tx.data), "networkId": quantity_encoder(evm_tx.network_id), }
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 make_test_tx(s=100000, g=50, data=b"", nonce=0): return EvmTransaction( nonce=nonce, startgas=s, gasprice=g, value=0, data=data, to=b"\x35" * 20, gas_token_id=0, transfer_token_id=0, )
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 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 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 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))
async def sendTransaction(self, data): def get_data_default(key, decoder, default=None): if key in data: return decoder(data[key]) return default to = get_data_default("to", recipient_decoder, b"") startgas = get_data_default("gas", quantity_decoder, DEFAULT_STARTGAS) gasprice = get_data_default("gasPrice", quantity_decoder, DEFAULT_GASPRICE) value = get_data_default("value", quantity_decoder, 0) data_ = get_data_default("data", data_decoder, b"") v = get_data_default("v", quantity_decoder, 0) r = get_data_default("r", quantity_decoder, 0) s = get_data_default("s", quantity_decoder, 0) nonce = get_data_default("nonce", quantity_decoder, None) to_full_shard_key = get_data_default( "toFullShardKey", full_shard_key_decoder, None ) from_full_shard_key = get_data_default( "fromFullShardKey", full_shard_key_decoder, None ) network_id = get_data_default( "networkId", quantity_decoder, self.master.env.quark_chain_config.NETWORK_ID ) gas_token_id = get_data_default( "gas_token_id", quantity_decoder, self.env.quark_chain_config.genesis_token ) transfer_token_id = get_data_default( "transfer_token_id", quantity_decoder, self.env.quark_chain_config.genesis_token, ) if nonce is None: raise InvalidParams("Missing nonce") if not (v and r and s): raise InvalidParams("Missing v, r, s") if from_full_shard_key is None: raise InvalidParams("Missing fromFullShardKey") if to_full_shard_key is None: to_full_shard_key = from_full_shard_key evm_tx = EvmTransaction( nonce, gasprice, startgas, to, value, data_, v=v, r=r, s=s, from_full_shard_key=from_full_shard_key, to_full_shard_key=to_full_shard_key, network_id=network_id, gas_token_id=gas_token_id, transfer_token_id=transfer_token_id, ) tx = TypedTransaction(SerializedEvmTransaction.from_evm_tx(evm_tx)) success = await self.master.add_transaction(tx) if not success: return EMPTY_TX_ID return id_encoder(tx.get_hash(), from_full_shard_key)