Beispiel #1
0
    def _transact(self, tx: Dict, allow_revert: bool) -> None:
        if allow_revert is None:
            allow_revert = bool(CONFIG.network_type == "development")
        if not allow_revert:
            self._check_for_revert(tx)

        formatters = {
            "nonce": web3.toHex,
            "gasPrice": web3.toHex,
            "gas": web3.toHex,
            "value": web3.toHex,
            "chainId": web3.toHex,
            "data": web3.toHex,
            "from": to_address,
        }
        if "to" in tx:
            formatters["to"] = to_address

        tx["chainId"] = web3.chain_id
        tx = apply_formatters_to_dict(formatters, tx)

        response = self._provider.make_request("account_signTransaction", [tx])
        if "error" in response:
            raise ValueError(response["error"]["message"])
        return web3.eth.send_raw_transaction(response["result"]["raw"])
Beispiel #2
0
    def sign_transaction(self, transaction_dict: dict) -> HexBytes:
        formatters = {
            'nonce': Web3.toHex,
            'gasPrice': Web3.toHex,
            'gas': Web3.toHex,
            'value': Web3.toHex,
            'chainId': Web3.toHex,
            'from': to_checksum_address
        }

        # Workaround for contract creation TXs
        if transaction_dict['to'] == b'':
            transaction_dict['to'] = None
        elif transaction_dict['to']:
            formatters['to'] = to_checksum_address

        formatted_transaction = apply_formatters_to_dict(
            formatters, transaction_dict)
        signed = self.__ipc_request("account_signTransaction",
                                    formatted_transaction)
        return HexBytes(signed.raw)
Beispiel #3
0
    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