Ejemplo n.º 1
0
    def _get_signed_txn(self, wif_keys):
        """ :param wif_keys: list of wif keys corresponding with
        self.input_addresses addresses, in same order
        """

        # NB: they are not guaranteed to be in order as there
        # may be more than one utxo associated with a single
        # address, but there will always be 1 solver associated
        # a private key
        unordered_solvers = []
        unordered_tx_outs = []
        unsigned = self._txn

        if self.is_signed:
            raise ValueError('cannot sign txn (already signed)')

        for key in wif_keys:
            # create btcpy PrivateKeys from input WIF format keys
            private_key = PrivateKey.from_wif(key)

            if self.is_segwit:
                pub_key = private_key.pub(compressed=True)

                s_solver = P2shSolver(
                    P2wpkhV0Script(pub_key),
                    P2wpkhV0Solver(private_key)
                )

                unordered_solvers.append(s_solver)
            else:
                # create btcpy P2PKH Solvers from those PrivateKeys
                unordered_solvers.append(P2pkhSolver(private_key))

        # a dict that matches the addresses (which are ordered the same as
        # their above WIF Keys) to their solvers
        addresses_solvers = dict(zip(self.input_addresses, unordered_solvers))

        # from self._specific_utxo_data, take the output num, value and scriptPubKey
        # and create TxOuts representing the UTXO's that will be spent.
        # In a tuple with the address of the UTXO so the correct solver
        # can be found later
        for t in self._specific_utxo_data:
            unordered_tx_outs.append((t[2], TxOut(value=t[4], n=t[1], script_pubkey=Script.unhexlify(t[3]))))

        # unlike the lists defined at the top of the method, these are in
        # order i.e the solver in solvers[0] is the solver for the TxOut of
        # tx_outs[0]. this is required to pass them into the spend() method
        tx_outs = []
        solvers = []

        for t in unordered_tx_outs:
            address = t[0]

            tx_outs.append(t[1])
            solvers.append(addresses_solvers[address])

        signed = unsigned.spend(tx_outs, solvers)

        return signed
Ejemplo n.º 2
0
 def outputs(utxos, previous_transaction_indexes=None):
     outputs = list()
     for index, utxo in enumerate(utxos):
         if previous_transaction_indexes is None or index in previous_transaction_indexes:
             outputs.append(
                 TxOut(value=utxo["amount"], n=utxo["output_index"],
                       script_pubkey=Script.unhexlify(utxo["script"])))
     return outputs
Ejemplo n.º 3
0
def _build_outputs(utxos: list,
                   previous_transaction_indexes: Optional[list] = None,
                   only_dict: bool = False) -> list:
    outputs = []
    for index, utxo in enumerate(utxos):
        if previous_transaction_indexes is None or index in previous_transaction_indexes:
            outputs.append(
                TxOut(value=utxo["value"],
                      n=utxo["tx_output_n"],
                      script_pubkey=Script.unhexlify(
                          hex_string=utxo["script"]))
                if not only_dict else dict(value=utxo["value"],
                                           tx_output_n=utxo["tx_output_n"],
                                           script=utxo["script"]))
    return outputs
Ejemplo n.º 4
0
    def sign(self, unsigned_raw, solver):
        """
        Sign unsigned fund transaction raw.

        :param unsigned_raw: bitcoin unsigned fund transaction raw.
        :type unsigned_raw: str
        :param solver: bitcoin fund solver.
        :type solver: bitcoin.solver.FundSolver
        :returns:  FundSignature -- bitcoin fund signature instance.

        >>> from shuttle.providers.bitcoin.signature import FundSignature
        >>> fund_signature = FundSignature()
        >>> fund_signature.sign(bitcoin_fund_unsigned, fund_solver)
        <shuttle.providers.bitcoin.signature.FundSignature object at 0x0409DAF0>
        """
        
        tx_raw = json.loads(b64decode(str(unsigned_raw).encode()).decode())
        if "raw" not in tx_raw or "outputs" not in tx_raw or "type" not in tx_raw or "fee" not in tx_raw:
            raise ValueError("invalid unsigned fund transaction raw")
        self.fee = tx_raw["fee"]
        self.type = tx_raw["type"]
        if not self.type == "bitcoin_fund_unsigned":
            raise TypeError("can't sign this %s transaction using FundSignature" % tx_raw["type"])
        if not isinstance(solver, FundSolver):
            raise TypeError("invalid solver instance, only takes bitcoin FundSolver class")
        self.transaction = MutableTransaction.unhexlify(tx_raw["raw"])
        outputs = list()
        for output in tx_raw["outputs"]:
            outputs.append(
                TxOut(value=output["amount"], n=output["n"],
                      script_pubkey=Script.unhexlify(output["script"])))
        self.transaction.spend(outputs, [solver.solve() for _ in outputs])
        self.signed = b64encode(str(json.dumps(dict(
            raw=self.transaction.hexlify(),
            fee=tx_raw["fee"],
            network=tx_raw["network"],
            type="bitcoin_fund_signed"
        ))).encode()).decode()
        return self
Ejemplo n.º 5
0
    def sign(self, unsigned_raw, solver):
        """
        Sign unsigned fund transaction raw.

        :param unsigned_raw: Bitcoin unsigned fund transaction raw.
        :type unsigned_raw: str
        :param solver: Bitcoin fund solver.
        :type solver: bitcoin.solver.FundSolver
        :returns:  FundSignature -- Bitcoin fund signature instance.

        >>> from shuttle.providers.bitcoin.signature import FundSignature
        >>> from shuttle.providers.bitcoin.solver import FundSolver
        >>> bitcoin_fund_unsigned_raw = "eyJmZWUiOiA2NzgsICJyYXciOiAiMDIwMDAwMDAwMTg4OGJlN2VjMDY1MDk3ZDk1NjY0NzYzZjI3NmQ0MjU1NTJkNzM1ZmIxZDk3NGFlNzhiZjcyMTA2ZGNhMGYzOTEwMTAwMDAwMDAwZmZmZmZmZmYwMjEwMjcwMDAwMDAwMDAwMDAxN2E5MTQyYmIwMTNjM2U0YmViMDg0MjFkZWRjZjgxNWNiNjVhNWMzODgxNzhiODdiY2RkMGUwMDAwMDAwMDAwMTk3NmE5MTQ2NGE4MzkwYjBiMTY4NWZjYmYyZDRiNDU3MTE4ZGM4ZGE5MmQ1NTM0ODhhYzAwMDAwMDAwIiwgIm91dHB1dHMiOiBbeyJhbW91bnQiOiA5ODQ5NDYsICJuIjogMSwgInNjcmlwdCI6ICI3NmE5MTQ2NGE4MzkwYjBiMTY4NWZjYmYyZDRiNDU3MTE4ZGM4ZGE5MmQ1NTM0ODhhYyJ9XSwgIm5ldHdvcmsiOiAidGVzdG5ldCIsICJ0eXBlIjogImJpdGNvaW5fZnVuZF91bnNpZ25lZCJ9"
        >>> fund_solver = FundSolver("92cbbc5990cb5090326a76feeb321cad01048635afe5756523bbf9f7a75bf38b")
        >>> fund_signature = FundSignature(network="testnet")
        >>> fund_signature.sign(bitcoin_fund_unsigned_raw, fund_solver)
        <shuttle.providers.bitcoin.signature.FundSignature object at 0x0409DAF0>
        """

        # Decoding and loading refund transaction
        fund_transaction = json.loads(
            b64decode(
                str(unsigned_raw + "=" *
                    (-len(unsigned_raw) % 4)).encode()).decode())
        # Checking refund transaction keys
        for key in ["raw", "outputs", "type", "fee", "network"]:
            if key not in fund_transaction:
                raise ValueError(
                    "invalid Bitcoin unsigned fund transaction raw")
        if not fund_transaction["type"] == "bitcoin_fund_unsigned":
            raise TypeError(
                f"invalid Bitcoin fund unsigned transaction type, "
                f"you can't sign this {fund_transaction['type']} type by using FundSignature"
            )
        if not isinstance(solver, FundSolver):
            raise TypeError(
                "invalid Bitcoin solver, it's only takes Bitcoin FundSolver class"
            )

        # Setting transaction fee, type, network and transaction
        self._fee, self._type, self.network, self.transaction = (
            fund_transaction["fee"], fund_transaction["type"],
            fund_transaction["network"],
            MutableTransaction.unhexlify(fund_transaction["raw"]))

        # Organizing outputs
        outputs = []
        for output in fund_transaction["outputs"]:
            outputs.append(
                TxOut(value=output["amount"],
                      n=output["n"],
                      script_pubkey=Script.unhexlify(
                          hex_string=output["script"])))
        # Signing fund transaction
        self.transaction.spend(outputs, [solver.solve() for _ in outputs])

        # Encoding fund transaction raw
        self._type = "bitcoin_fund_signed"
        self._signed_raw = b64encode(
            str(
                json.dumps(
                    dict(raw=self.transaction.hexlify(),
                         fee=fund_transaction["fee"],
                         network=fund_transaction["network"],
                         type=self._type))).encode()).decode()
        return self
Ejemplo n.º 6
0
    def sign(self, transaction_raw: str, solver: FundSolver) -> "FundSignature":
        """
        Sign unsigned fund transaction raw.

        :param transaction_raw: Bitcoin unsigned fund transaction raw.
        :type transaction_raw: str
        :param solver: Bitcoin fund solver.
        :type solver: bitcoin.solver.FundSolver

        :returns: FundSignature -- Bitcoin fund signature instance.

        >>> from swap.providers.bitcoin.signature import FundSignature
        >>> from swap.providers.bitcoin.solver import FundSolver
        >>> unsigned_fund_transaction_raw: str = "eyJmZWUiOiAxMTIyLCAicmF3IjogIjAyMDAwMDAwMDIzMWZiNzZhMGMzOGQ1NzM4MWIzMTEwZTRmNWVlOWI1MjgxZGNmMmZiZTJmZTI1NjkyNjZiNzUxMDExZDIxMWEyMDEwMDAwMDAwMGZmZmZmZmZmMDgwYjgyZWVjMzMyOTk2YTQyMmFlNGYwODBmNzRlNTNmZDJjYTRmMDcwMTFkNDdjNTkwODUzZTFlMzA1ZmUxMTAxMDAwMDAwMDBmZmZmZmZmZjAyYTA4NjAxMDAwMDAwMDAwMDE3YTkxNGM4Yzc3YTliNDNlZTJiZGYxYTA3YzQ4Njk5ODMzZDc2NjhiZjI2NGM4NzMyOWMwZDAwMDAwMDAwMDAxOTc2YTkxNGUwMGZmMmE2NDBiN2NlMmQzMzY4NjA3MzkxNjk0ODdhNTdmODRiMTU4OGFjMDAwMDAwMDAiLCAib3V0cHV0cyI6IFt7InZhbHVlIjogOTQzMzAsICJ0eF9vdXRwdXRfbiI6IDEsICJzY3JpcHQiOiAiNzZhOTE0ZTAwZmYyYTY0MGI3Y2UyZDMzNjg2MDczOTE2OTQ4N2E1N2Y4NGIxNTg4YWMifSwgeyJ2YWx1ZSI6IDg5ODc0NiwgInR4X291dHB1dF9uIjogMSwgInNjcmlwdCI6ICI3NmE5MTRlMDBmZjJhNjQwYjdjZTJkMzM2ODYwNzM5MTY5NDg3YTU3Zjg0YjE1ODhhYyJ9XSwgIm5ldHdvcmsiOiAidGVzdG5ldCIsICJ0eXBlIjogImJpdGNvaW5fZnVuZF91bnNpZ25lZCJ9"
        >>> fund_solver: FundSolver = FundSolver(xprivate_key="tprv8ZgxMBicQKsPeMHMJAc6uWGYiGqi1MVM2ybmzXL2TAoDpQe85uyDpdT7mv7Nhdu5rTCBEKLZsd9KyP2LQZJzZTvgVQvENArgU8e6DoYBiXf")
        >>> fund_signature: FundSignature = FundSignature(network="testnet")
        >>> fund_signature.sign(transaction_raw=unsigned_fund_transaction_raw, solver=fund_solver)
        <swap.providers.bitcoin.signature.FundSignature object at 0x0409DAF0>
        """

        if not is_transaction_raw(transaction_raw=transaction_raw):
            raise TransactionRawError("Invalid Bitcoin unsigned transaction raw.")

        transaction_raw = clean_transaction_raw(transaction_raw)
        decoded_transaction_raw = b64decode(transaction_raw.encode())
        loaded_transaction_raw = json.loads(decoded_transaction_raw.decode())

        if not loaded_transaction_raw["type"] == "bitcoin_fund_unsigned":
            raise TypeError(f"Invalid Bitcoin fund unsigned transaction raw type, "
                            f"you can't sign {loaded_transaction_raw['type']} type by using fund signature.")

        # Check parameter instances
        if not isinstance(solver, FundSolver):
            raise TypeError(f"Solver must be Bitcoin FundSolver, not {type(solver).__name__} type.")

        # Set transaction fee, type, network and transaction
        self._fee, self._type, self._network, self._transaction = (
            loaded_transaction_raw["fee"], loaded_transaction_raw["type"],
            loaded_transaction_raw["network"], MutableTransaction.unhexlify(loaded_transaction_raw["raw"])
        )

        # Organize outputs
        outputs = []
        for output in loaded_transaction_raw["outputs"]:
            outputs.append(TxOut(
                value=output["value"],
                n=output["tx_output_n"],
                script_pubkey=Script.unhexlify(
                    hex_string=output["script"]
                )
            ))

        # Sign fund transaction
        self._transaction.spend(
            txouts=outputs,
            solvers=[solver.solve(network=self._network) for _ in outputs]
        )

        # Encode fund transaction raw
        self._type = "bitcoin_fund_signed"
        self._signed_raw = b64encode(str(json.dumps(dict(
            raw=self._transaction.hexlify(),
            fee=self._fee,
            network=self._network,
            type=self._type
        ))).encode()).decode()
        return self
Ejemplo n.º 7
0
    def sign(self, transaction_raw: str,
             solver: NormalSolver) -> "NormalSignature":
        """
        Sign unsigned normal transaction raw.

        :param transaction_raw: Bitcoin unsigned normal transaction raw.
        :type transaction_raw: str
        :param solver: Bitcoin normal solver.
        :type solver: bitcoin.solver.NormalSolver

        :returns: NormalSignature -- Bitcoin normal signature instance.

        >>> from swap.providers.bitcoin.signature import NormalSignature
        >>> from swap.providers.bitcoin.solver import NormalSolver
        >>> unsigned_normal_transaction_raw = "eyJmZWUiOiA2NzgsICJyYXciOiAiMDIwMDAwMDAwMTA4MjVlMDBiYTU5NmFiMTExMjZjZDg5MjAzYjg4MmJjZTYwYTdkYjAxOWU1MTIxNzA1NmM0NzFmNTEwY2ZkODUwMDAwMDAwMDAwZmZmZmZmZmYwMjEwMjcwMDAwMDAwMDAwMDAxN2E5MTQ0Njk1MTI3YjFkMTdjNDU0ZjRiYWU5YzQxY2I4ZTNjZGI1ZTg5ZDI0ODdlYTVjMDEwMDAwMDAwMDAwMTk3NmE5MTQzM2VjYWIzZDY3ZjBlMmJkZTQzZTUyZjQxZWMxZWNiZGM3M2YxMWY4ODhhYzAwMDAwMDAwIiwgIm91dHB1dHMiOiBbeyJ2YWx1ZSI6IDEwMDAwMCwgInR4X291dHB1dF9uIjogMCwgInNjcmlwdCI6ICI3NmE5MTQzM2VjYWIzZDY3ZjBlMmJkZTQzZTUyZjQxZWMxZWNiZGM3M2YxMWY4ODhhYyJ9XSwgIm5ldHdvcmsiOiAidGVzdG5ldCIsICJ0eXBlIjogImJpdGNvaW5fZnVuZF91bnNpZ25lZCJ9"
        >>> normal_solver: NormalSolver = NormalSolver(xprivate_key="tprv8ZgxMBicQKsPeMHMJAc6uWGYiGqi1MVM2ybmzXL2TAoDpQe85uyDpdT7mv7Nhdu5rTCBEKLZsd9KyP2LQZJzZTvgVQvENArgU8e6DoYBiXf")
        >>> normal_signature: NormalSignature = NormalSignature(network="testnet")
        >>> normal_signature.sign(transaction_raw=unsigned_normal_transaction_raw, solver=normal_solver)
        <swap.providers.bitcoin.signature.NormalSignature object at 0x0409DAF0>
        """

        if not is_transaction_raw(transaction_raw=transaction_raw):
            raise TransactionRawError(
                "Invalid Bitcoin unsigned transaction raw.")

        transaction_raw = clean_transaction_raw(transaction_raw)
        decoded_transaction_raw = b64decode(transaction_raw.encode())
        loaded_transaction_raw = json.loads(decoded_transaction_raw.decode())

        if not loaded_transaction_raw["type"] == "bitcoin_normal_unsigned":
            raise TypeError(
                f"Invalid Bitcoin normal unsigned transaction raw type, "
                f"you can't sign {loaded_transaction_raw['type']} type by using normal signature."
            )

        # Check parameter instances
        if not isinstance(solver, NormalSolver):
            raise TypeError(
                f"Solver must be Bitcoin NormalSolver, not {type(solver).__name__} type."
            )

        # Set transaction fee, type, network and transaction
        self._fee, self._type, self._network, self._transaction = (
            loaded_transaction_raw["fee"], loaded_transaction_raw["type"],
            loaded_transaction_raw["network"],
            MutableTransaction.unhexlify(loaded_transaction_raw["raw"]))

        # Organize outputs
        outputs = []
        for output in loaded_transaction_raw["outputs"]:
            outputs.append(
                TxOut(value=output["value"],
                      n=output["tx_output_n"],
                      script_pubkey=Script.unhexlify(
                          hex_string=output["script"])))

        # Sign normal transaction
        self._transaction.spend(
            txouts=outputs,
            solvers=[solver.solve(network=self._network) for _ in outputs])

        # Encode normal transaction raw
        self._type = "bitcoin_normal_signed"
        self._signed_raw = b64encode(
            str(
                json.dumps(
                    dict(raw=self._transaction.hexlify(),
                         fee=self._fee,
                         network=self._network,
                         type=self._type))).encode()).decode()
        return self
Ejemplo n.º 8
0
    def sign(self, transaction_raw: str, solver: FundSolver) -> "FundSignature":
        """
        Sign unsigned fund transaction raw.

        :param transaction_raw: Bitcoin unsigned fund transaction raw.
        :type transaction_raw: str
        :param solver: Bitcoin fund solver.
        :type solver: bitcoin.solver.FundSolver

        :returns: FundSignature -- Bitcoin fund signature instance.

        >>> from swap.providers.bitcoin.signature import Signature
        >>> from swap.providers.bitcoin.solver import FundSolver
        >>> unsigned_fund_transaction_raw = "eyJmZWUiOiA2NzgsICJyYXciOiAiMDIwMDAwMDAwMTA4MjVlMDBiYTU5NmFiMTExMjZjZDg5MjAzYjg4MmJjZTYwYTdkYjAxOWU1MTIxNzA1NmM0NzFmNTEwY2ZkODUwMDAwMDAwMDAwZmZmZmZmZmYwMjEwMjcwMDAwMDAwMDAwMDAxN2E5MTQ0Njk1MTI3YjFkMTdjNDU0ZjRiYWU5YzQxY2I4ZTNjZGI1ZTg5ZDI0ODdlYTVjMDEwMDAwMDAwMDAwMTk3NmE5MTQzM2VjYWIzZDY3ZjBlMmJkZTQzZTUyZjQxZWMxZWNiZGM3M2YxMWY4ODhhYzAwMDAwMDAwIiwgIm91dHB1dHMiOiBbeyJ2YWx1ZSI6IDEwMDAwMCwgInR4X291dHB1dF9uIjogMCwgInNjcmlwdCI6ICI3NmE5MTQzM2VjYWIzZDY3ZjBlMmJkZTQzZTUyZjQxZWMxZWNiZGM3M2YxMWY4ODhhYyJ9XSwgIm5ldHdvcmsiOiAidGVzdG5ldCIsICJ0eXBlIjogImJpdGNvaW5fZnVuZF91bnNpZ25lZCJ9"
        >>> sender_root_xprivate_key = "xprv9s21ZrQH143K3XihXQBN8Uar2WBtrjSzK2oRDEGQ25pA2kKAADoQXaiiVXht163ZTrdtTXfM4GqNRE9gWQHky25BpvBQuuhNCM3SKwWTPNJ"
        >>> fund_solver = FundSolver(sender_root_xprivate_key)
        >>> fund_signature = FundSignature("testnet")
        >>> fund_signature.sign(unsigned_fund_transaction_raw, fund_solver)
        <swap.providers.bitcoin.signature.FundSignature object at 0x0409DAF0>
        """

        if not is_transaction_raw(transaction_raw=transaction_raw):
            raise TransactionRawError("Invalid Bitcoin unsigned transaction raw.")

        transaction_raw = clean_transaction_raw(transaction_raw)
        decoded_transaction_raw = b64decode(transaction_raw.encode())
        loaded_transaction_raw = json.loads(decoded_transaction_raw.decode())

        if not loaded_transaction_raw["type"] == "bitcoin_fund_unsigned":
            raise TypeError(f"Invalid Bitcoin fund unsigned transaction raw type, "
                            f"you can't sign {loaded_transaction_raw['type']} type by using fund signature.")

        # Check parameter instances
        if not isinstance(solver, FundSolver):
            raise TypeError(f"Solver must be Bitcoin FundSolver, not {type(solver).__name__} type.")

        # Set transaction fee, type, network and transaction
        self._fee, self._type, self._datas, self._network, self._transaction = (
            loaded_transaction_raw["fee"], loaded_transaction_raw["type"], loaded_transaction_raw["datas"],
            loaded_transaction_raw["network"], MutableTransaction.unhexlify(loaded_transaction_raw["raw"])
        )

        # Organize outputs
        outputs = []
        for output in loaded_transaction_raw["outputs"]:
            outputs.append(TxOut(
                value=output["value"],
                n=output["tx_output_n"],
                script_pubkey=Script.unhexlify(
                    hex_string=output["script"]
                )
            ))

        # Sign fund transaction
        self._transaction.spend(
            txouts=outputs,
            solvers=[solver.solve(network=self._network) for _ in outputs]
        )

        # Encode fund transaction raw
        self._type = "bitcoin_fund_signed"
        self._signed_raw = b64encode(str(json.dumps(dict(
            raw=self._transaction.hexlify(),
            fee=self._fee,
            network=self._network,
            type=self._type,
            datas=self._datas
        ))).encode()).decode()
        return self