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 test_keystore_sign_transaction(good_signer, mock_account): transaction_dict = assoc(TRANSACTION_DICT, 'from', value=mock_account.address) signed_transaction = good_signer.sign_transaction(transaction_dict=transaction_dict) assert isinstance(signed_transaction, HexBytes) # assert valid transaction transaction = Transaction.from_bytes(signed_transaction) assert to_checksum_address(transaction.to) == transaction_dict['to']
def test_character_transacting_power_signing(testerchain, agency, test_registry): # Pretend to be a character. eth_address = testerchain.etherbase_account signer = Character(is_me=True, registry=test_registry, checksum_address=eth_address) # Manually consume the power up transacting_power = TransactingPower( password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(testerchain.client), account=eth_address) signer._crypto_power.consume_power_up(transacting_power) # Retrieve the power up power = signer._crypto_power.power_ups(TransactingPower) assert power == transacting_power assert testerchain.transacting_power == power assert power.is_active is True assert power.is_unlocked is True assert testerchain.transacting_power.is_unlocked is True # Sign Message data_to_sign = b'Premium Select Luxury Pencil Holder' signature = power.sign_message(message=data_to_sign) is_verified = verify_eip_191(address=eth_address, message=data_to_sign, signature=signature) assert is_verified is True # Sign Transaction 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'' } signed_transaction = power.sign_transaction( transaction_dict=transaction_dict) # Demonstrate that the transaction is valid RLP encoded. 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']
def recover_transaction(self, serialized_transaction): """ Get the address of the account that signed this transaction. :param serialized_transaction: the complete signed transaction :type serialized_transaction: hex str, bytes or int :returns: address of signer, hex-encoded & checksummed :rtype: str .. code-block:: python >>> raw_transaction = '0xf86a8086d55698372431831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a009ebb6ca057a0535d6186462bc0b465b561c94a295bdb0621fc19208ab149a9ca0440ffd775ce91a833ab410777204d5341a6f9fa91216a6f3ee2c051fea6a0428', # noqa: E501 >>> Account.recover_transaction(raw_transaction) '0x2c7536E3605D9C16a7a3D7b1898e529396a65c23' """ txn_bytes = HexBytes(serialized_transaction) txn = Transaction.from_bytes(txn_bytes) msg_hash = hash_of_signed_transaction(txn) return self._recover_hash(msg_hash, vrs=vrs_from(txn))
def test_trezor_sign_transaction(mock_trezor, mock_account): trezor_signer = TrezorSigner() transaction_dict = assoc(TRANSACTION_DICT, key='from', value=mock_account.address) signed_transaction = trezor_signer.sign_transaction( transaction_dict=transaction_dict) assert isinstance(signed_transaction, HexBytes) # assert valid deserializable transaction transaction = Transaction.from_bytes(signed_transaction) # Confirm the integrity of the sender and recipient address failure_message = 'WARNING: transaction "to" field was mutated' sender_checksum_address = to_checksum_address(transaction.to) assert sender_checksum_address != mock_account.address, failure_message assert sender_checksum_address == TRANSACTION_DICT['to'], failure_message assert sender_checksum_address == transaction_dict['to'] # positive assert sender_checksum_address != mock_account.address # negative
def unsigned_transaction(self, transaction_dict): return Transaction.from_dict(transaction_dict)
def sign_transaction(self, transaction_dict: dict, rlp_encoded: bool = True ) -> Union[HexBytes, Transaction]: """ Sign a transaction with a trezor hardware wallet. This non-mutative method handles transaction validation, field formatting, signing, and outgoing serialization. Accepts a standard transaction dictionary as input, and produces an RLP encoded raw signed transaction by default. Internally the standard transaction dictionary is reformatted for trezor API consumption via calls `trezorlib.client.ethereum.sign_tx`. WARNING: This function returns a raw signed transaction which can be broadcast by anyone with a connection to the ethereum network. ***Treat pre-signed raw transactions produced by this function like money.*** """ # Eager enforcement of EIP-155 # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md # # Leave the chain ID in tact for the trezor signing request so that an EIP-155 transaction signature will be applied # https://github.com/trezor/trezor-core/pull/311 if 'chainId' not in transaction_dict: raise self.SignerError('Invalid EIP-155 transaction - "chain_id" field is missing in trezor signing request.') # Consume the sender inside the transaction request's 'from field. try: sender_address = transaction_dict['from'] except KeyError: raise self.SignerError("'from' field is missing from trezor signing request.") transaction_dict = dissoc(transaction_dict, 'from') # Format contract data field for both trezor and eth_account's Transaction formatters = {'data': lambda data: Web3.toBytes(HexBytes(data))} transaction_dict = dict(apply_formatters_to_dict(formatters, transaction_dict)) # Format transaction fields for Trezor, Lookup HD path trezor_transaction = self._format_transaction(transaction_dict=transaction_dict) # Note that the derivation path on the trezor must correlate with the chain id # in the transaction. Since Trezor firmware version 2.3.1 mismatched chain_id # and derivation path will fail to sign with 'forbidden key path'. # https://github.com/trezor/trezor-firmware/issues/1050#issuecomment-640718622 hd_path = self.__get_address_path(checksum_address=sender_address) # from cache # Trezor signing request _v, _r, _s = self.__sign_transaction(n=hd_path, trezor_transaction=trezor_transaction) # Create RLP serializable Transaction instance with eth_account # chainId is not longer needed since it can later be derived from v transaction_dict = dissoc(transaction_dict, 'chainId') # 'to' may be blank if this transaction is contract creation formatters = {'to': to_canonical_address} transaction_dict = dict(apply_formatters_to_dict(formatters, transaction_dict)) signed_transaction = Transaction(v=to_int(_v), # type: int r=to_int(_r), # bytes -> int s=to_int(_s), # bytes -> int **transaction_dict) # Optionally encode as RLP for broadcasting if rlp_encoded: signed_transaction = HexBytes(rlp.encode(signed_transaction)) return signed_transaction