def test_transacting_power_sign_transaction(testerchain): eth_address = testerchain.unassigned_accounts[2] power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(testerchain.client), account=eth_address) transaction_dict = { 'nonce': testerchain.client.w3.eth.getTransactionCount(eth_address), 'gasPrice': testerchain.client.w3.eth.gasPrice, 'gas': 100000, 'from': eth_address, 'to': testerchain.unassigned_accounts[1], 'value': 1, 'data': b'' } # Sign power.activate() signed_transaction = power.sign_transaction( transaction_dict=transaction_dict) # Demonstrate that the transaction is valid RLP encoded. from eth_account._utils.transactions import Transaction restored_transaction = Transaction.from_bytes( serialized_bytes=signed_transaction) restored_dict = restored_transaction.as_dict() assert to_checksum_address(restored_dict['to']) == transaction_dict['to'] # Try signing with missing transaction fields del transaction_dict['gas'] del transaction_dict['nonce'] with pytest.raises(TypeError): power.sign_transaction(transaction_dict=transaction_dict)
def test_transacting_power_sign_agent_transaction(testerchain, agency, test_registry): token_agent = NucypherTokenAgent(registry=test_registry) contract_function = token_agent.contract.functions.approve( testerchain.etherbase_account, 100) payload = { 'chainId': int(testerchain.client.chain_id), 'nonce': testerchain.client.w3.eth.getTransactionCount( testerchain.etherbase_account), 'from': testerchain.etherbase_account, 'gasPrice': testerchain.client.gas_price } unsigned_transaction = contract_function.buildTransaction(payload) # Sign with Transacting Power transacting_power = TransactingPower( password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(testerchain.client), account=testerchain.etherbase_account) signed_raw_transaction = transacting_power.sign_transaction( unsigned_transaction) # Demonstrate that the transaction is valid RLP encoded. restored_transaction = Transaction.from_bytes( serialized_bytes=signed_raw_transaction) restored_dict = restored_transaction.as_dict() assert to_checksum_address( restored_dict['to']) == unsigned_transaction['to']
def test_transacting_power_sign_transaction(testerchain): eth_address = testerchain.unassigned_accounts[2] power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(testerchain.client), account=eth_address) assert power.is_active is False assert power.is_unlocked is False transaction_dict = { 'nonce': testerchain.client.w3.eth.getTransactionCount(eth_address), 'gasPrice': testerchain.client.w3.eth.gasPrice, 'gas': 100000, 'from': eth_address, 'to': testerchain.unassigned_accounts[1], 'value': 1, 'data': b'' } # The default state of the account is locked. assert not power.is_unlocked # Test a signature without unlocking the account with pytest.raises(power.AccountLocked): power.sign_transaction(transaction_dict=transaction_dict) # Sign power.activate() assert power.is_unlocked is True signed_transaction = power.sign_transaction( transaction_dict=transaction_dict) # Demonstrate that the transaction is valid RLP encoded. from eth_account._utils.transactions import Transaction restored_transaction = Transaction.from_bytes( serialized_bytes=signed_transaction) restored_dict = restored_transaction.as_dict() assert to_checksum_address(restored_dict['to']) == transaction_dict['to'] # Try signing with missing transaction fields del transaction_dict['gas'] del transaction_dict['nonce'] with pytest.raises(TypeError): power.sign_transaction(transaction_dict=transaction_dict) # Try signing with a re-locked account. power.lock_account() with pytest.raises(power.AccountLocked): power.sign_transaction(transaction_dict=transaction_dict) power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD) assert power.is_unlocked is True # Tear-Down Test power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(testerchain.client), account=testerchain.etherbase_account) power.activate(password=INSECURE_DEVELOPMENT_PASSWORD)
def sign_and_broadcast_transaction( self, transacting_power: TransactingPower, transaction_dict: TransactionDict, transaction_name: str = "", confirmations: int = 0, fire_and_forget: bool = False) -> Union[TxReceipt, HexBytes]: """ Takes a transaction dictionary, signs it with the configured signer, then broadcasts the signed transaction using the ethereum provider's eth_sendRawTransaction RPC endpoint. Optionally blocks for receipt and confirmation with 'confirmations', and 'fire_and_forget' flags. If 'fire and forget' is True this method returns the transaction hash only, without waiting for a receipt - otherwise return the transaction receipt. """ # # Setup # # TODO # 1754 - Move this to singleton - I do not approve... nor does Bogdan? if GlobalLoggerSettings._json_ipc: emitter = JSONRPCStdoutEmitter() else: emitter = StdoutEmitter() # # Sign # # TODO: Show the USD Price: https://api.coinmarketcap.com/v1/ticker/ethereum/ price = transaction_dict['gasPrice'] price_gwei = Web3.fromWei(price, 'gwei') cost_wei = price * transaction_dict['gas'] cost = Web3.fromWei(cost_wei, 'ether') if transacting_power.is_device: emitter.message( f'Confirm transaction {transaction_name} on hardware wallet... ' f'({cost} ETH @ {price_gwei} gwei)', color='yellow') signed_raw_transaction = transacting_power.sign_transaction( transaction_dict) # # Broadcast # emitter.message( f'Broadcasting {transaction_name} Transaction ({cost} ETH @ {price_gwei} gwei)', color='yellow') try: txhash = self.client.send_raw_transaction( signed_raw_transaction) # <--- BROADCAST emitter.message(f'TXHASH {txhash.hex()}', color='yellow') except (TestTransactionFailed, ValueError): raise # TODO: Unify with Transaction failed handling -- Entry point for _handle_failed_transaction else: if fire_and_forget: return txhash # # Receipt # try: # TODO: Handle block confirmation exceptions waiting_for = 'receipt' if confirmations: waiting_for = f'{confirmations} confirmations' emitter.message( f'Waiting {self.TIMEOUT} seconds for {waiting_for}', color='yellow') receipt = self.client.wait_for_receipt(txhash, timeout=self.TIMEOUT, confirmations=confirmations) except TimeExhausted: # TODO: #1504 - Handle transaction timeout raise else: self.log.debug( f"[RECEIPT-{transaction_name}] | txhash: {receipt['transactionHash'].hex()}" ) # # Confirmations # # Primary check transaction_status = receipt.get('status', UNKNOWN_TX_STATUS) if transaction_status == 0: failure = f"Transaction transmitted, but receipt returned status code 0. " \ f"Full receipt: \n {pprint.pformat(receipt, indent=2)}" raise self.InterfaceError(failure) if transaction_status is UNKNOWN_TX_STATUS: self.log.info( f"Unknown transaction status for {txhash} (receipt did not contain a status field)" ) # Secondary check tx = self.client.get_transaction(txhash) if tx["gas"] == receipt["gasUsed"]: raise self.InterfaceError( f"Transaction consumed 100% of transaction gas." f"Full receipt: \n {pprint.pformat(receipt, indent=2)}") return receipt