def sign_transaction(self, account: Account, tx: transactions.TxObject, metadata: dict = {}, **kwargs) -> tuple: """ Sign a transaction :return: the transaction for the transaction """ # first retrieve the account from the node # so we can check if it is generalized or not on_chain_account = self.get_account(account.get_address()) # if the account is not generalized sign and return the transaction if not on_chain_account.is_generalized(): s = transactions.TxSigner(account, self.config.network_id) signature = s.sign_transaction(tx, metadata) return self.tx_builder.tx_signed([signature], tx, metadata=metadata) # if the account is generalized then prepare the ga_meta_tx # 1. wrap the tx into a signed tx (without signatures) sg_tx = self.tx_builder.tx_signed([], tx) # 2. wrap the tx into a ga_meta_tx # get the absolute ttl ttl = self.compute_absolute_ttl(kwargs.get( "ttl", defaults.TX_TTL)).absolute_ttl # get abi version _, abi = self.get_vm_abi_versions() # check that the parameter auth_data is provided auth_data = kwargs.get("auth_data") if auth_data is None: raise TypeError( "the auth_data parameter is required for ga accounts") # verify the gas amount TODO: add a tx verification gas = kwargs.get("gas", defaults.GA_MAX_AUTH_FUN_GAS) if gas > defaults.GA_MAX_AUTH_FUN_GAS: raise TypeError( f"the maximum gas value for ga auth_fun is {defaults.GA_MAX_AUTH_FUN_GAS}, got {gas}" ) # build the ga_sg_tx = self.tx_builder.tx_ga_meta( account.get_address(), auth_data, kwargs.get("abi_version", abi), kwargs.get("fee", defaults.FEE), gas, kwargs.get("gas_price", defaults.CONTRACT_GAS_PRICE), ttl, sg_tx) # 3. wrap the the ga into a signed transaction sg_ga_sg_tx = self.tx_builder.tx_signed([], ga_sg_tx, metadata=metadata) return sg_ga_sg_tx
def test_transaction_tx_signer(): sk = 'ed067bef18b3e2be42822b32e3fa468ceee1c8c2c8744ca15e96855b0db10199af08c7e24c71c39f119f07616621cb86d774c7af07b84e9fd82cc9592c7f7d0a' pk = 'ak_2L61wjvTKBKK985sbgn7vryr66K8F4ZwyUVrzYYvro85j5sCeU' tx = 'tx_+FAMAaEBrwjH4kxxw58RnwdhZiHLhtd0x68HuE6f2CzJWSx/fQqhAa8Ix+JMccOfEZ8HYWYhy4bXdMevB7hOn9gsyVksf30KZIUukO3QAAABgJoyIic=' sg = 'sg_Tzrf8pDzK53RVfiTdr3GnM86E4jWoGmA2RR6XaCws4PFfnbUTGQ2adRWc8Y55NpxXBaEYD5b1FP5RzNST1GpBZUVfZrLo' network_id = "ae_testnet" account = Account.from_secret_key_string(sk) with raises(ValueError): txs = transactions.TxSigner(None, network_id) with raises(ValueError): txs = transactions.TxSigner(account, None) # parse the transaction txo = transactions.TxBuilder().parse_tx_string(tx) # verify the signature signer = transactions.TxSigner(account, network_id) signature = signer.sign_transaction(txo) assert f"{signer}" == f"{network_id}:{account.get_address()}" assert signature == sg # incorrect network_id signer = transactions.TxSigner(account, "not_good") signature = signer.sign_transaction(txo) assert signature != sg
def sign_transaction(self, account: Account, tx: transactions.TxObject, metadata: dict = {}, **kwargs) -> transactions.TxObject: """ The function sign a transaction to be broadcast to the chain. It automatically detect if the account is Basic or GA and return the correct transaction to be broadcast. :param account: the account signing the transaction :param tx: the transaction to be signed :param metadata: additional metadata to maintain in the TxObject :param `**kwargs`: for GA accounts, see below Kwargs: :auth_data (str): the encoded calldata for the GA auth function :gas (int): the gas limit for the GA auth function [optional] :gas_price (int): the gas price for the GA auth function [optional] :fee (int): the fee for the GA transaction [optional, automatically calculated] :abi_version (int): the abi_version to select FATE or AEVM [optional, default 3/FATE] :ttl (str): the transaction ttl expressed in relative number of blocks [optional, default 0/max_ttl]: :return: a TxObject of the signed transaction or metat transaction for GA :raises TypeError: if the auth_data is missing and the account is GA :raises TypeError: if the gas for auth_func is gt defaults.GA_MAX_AUTH_FUN_GAS """ # first retrieve the account from the node # so we can check if it is generalized or not on_chain_account = self.get_account(account.get_address()) # if the account is not generalized sign and return the transaction if not on_chain_account.is_generalized(): s = transactions.TxSigner(account, self.config.network_id) signature = s.sign_transaction(tx, metadata) return self.tx_builder.tx_signed([signature], tx, metadata=metadata) # if the account is generalized then prepare the ga_meta_tx # 1. wrap the tx into a signed tx (without signatures) sg_tx = self.tx_builder.tx_signed([], tx) # 2. wrap the tx into a ga_meta_tx # get the absolute ttl ttl = self.compute_absolute_ttl(kwargs.get("ttl", defaults.TX_TTL)).absolute_ttl # get abi version _, abi = self.get_vm_abi_versions() # check that the parameter auth_data is provided auth_data = kwargs.get("auth_data") if auth_data is None: raise TypeError("the auth_data parameter is required for ga accounts") # verify the gas amount TODO: add a tx verification gas = kwargs.get("gas", defaults.GA_MAX_AUTH_FUN_GAS) if gas > defaults.GA_MAX_AUTH_FUN_GAS: raise TypeError(f"the maximum gas value for ga auth_fun is {defaults.GA_MAX_AUTH_FUN_GAS}, got {gas}") # build the ga_sg_tx = self.tx_builder.tx_ga_meta( account.get_address(), auth_data, kwargs.get("abi_version", abi), kwargs.get("fee", defaults.FEE), gas, kwargs.get("gas_price", defaults.CONTRACT_GAS_PRICE), ttl, sg_tx ) # 3. wrap the the ga into a signed transaction sg_ga_sg_tx = self.tx_builder.tx_signed([], ga_sg_tx, metadata=metadata) return sg_ga_sg_tx