Beispiel #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
Beispiel #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
Beispiel #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
Beispiel #4
0
    def from_opcode(self, opcode):
        """
        Initiate bitcoin Hash Time Lock Contract (HTLC) from opcode script.

        :param opcode: Bitcoin opcode script.
        :type opcode: str.
        :returns: HTLC -- bitcoin Hash Time Lock Contract (HTLC) instance.

        >>> from shuttle.providers.bitcoin.htlc import HTLC
        >>> htlc = HTLC(network="testnet")
        >>> htlc.from_opcode(htlc_opcode_script)
        <shuttle.providers.bitcoin.htlc.HTLC object at 0x0409DAF0>
        """

        if isinstance(opcode, str):
            bytecode = Script.compile(opcode)
            self.script = ScriptBuilder.identify(bytecode)
            return self
        raise TypeError("op_code must be string format")
Beispiel #5
0
    def from_opcode(self, opcode: str) -> "HTLC":
        """
        Initiate Bitcoin Hash Time Lock Contract (HTLC) from opcode script.

        :param opcode: Bitcoin opcode script.
        :type opcode: str

        :returns: HTLC -- Bitcoin Hash Time Lock Contract (HTLC) instance.

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> htlc = HTLC(network="testnet")
        >>> opcode = "OP_IF OP_HASH256 821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e0158 OP_EQUALVERIFY OP_DUP OP_HASH160 0e259e08f2ec9fc99a92b6f66fdfcb3c7914fd68 OP_EQUALVERIFY OP_CHECKSIG OP_ELSE e803 OP_CHECKSEQUENCEVERIFY OP_DROP OP_DUP OP_HASH160 33ecab3d67f0e2bde43e52f41ec1ecbdc73f11f8 OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF" 
        >>> htlc.from_opcode(opcode=opcode)
        <swap.providers.bitcoin.htlc.HTLC object at 0x0409DAF0>
        """

        bytecode = Script.compile(opcode)
        self._script = ScriptBuilder.identify(bytecode)
        return self
Beispiel #6
0
    def from_opcode(self, opcode: str) -> "HTLC":
        """
        Initiate Bitcoin Hash Time Lock Contract (HTLC) from opcode script.

        :param opcode: Bitcoin opcode script.
        :type opcode: str

        :returns: HTLC -- Bitcoin Hash Time Lock Contract (HTLC) instance.

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> htlc: HTLC = HTLC(network="testnet")
        >>> opcode: str = "OP_IF OP_HASH256 821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e0158 OP_EQUALVERIFY OP_DUP OP_HASH160 0a0a6590e6ba4b48118d21b86812615219ece76b OP_EQUALVERIFY OP_CHECKSIG OP_ELSE 0ec4d660 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 e00ff2a640b7ce2d336860739169487a57f84b15 OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF"
        >>> htlc.from_opcode(opcode=opcode)
        <swap.providers.bitcoin.htlc.HTLC object at 0x0409DAF0>
        """

        bytecode = Script.compile(opcode)
        self._script = ScriptBuilder.identify(bytecode)
        return self
Beispiel #7
0
    def from_opcode(self, opcode):
        """
        Initiate Bitcoin Hash Time Lock Contract (HTLC) from opcode script.

        :param opcode: Bitcoin opcode script.
        :type opcode: str
        :returns: HTLC -- Bitcoin Hash Time Lock Contract (HTLC) instance.

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> htlc = HTLC(network="testnet")
        >>> htlc_opcode_script = "OP_IF OP_HASH256 821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e0158 OP_EQUALVERIFY OP_DUP OP_HASH160 98f879fb7f8b4951dee9bc8a0327b792fbe332b8 OP_EQUALVERIFY OP_CHECKSIG OP_ELSE e803 OP_CHECKSEQUENCEVERIFY OP_DROP OP_DUP OP_HASH160 64a8390b0b1685fcbf2d4b457118dc8da92d5534 OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF"        >>> htlc.from_opcode(opcode=htlc_opcode_script)
        <swap.providers.bitcoin.htlc.HTLC object at 0x0409DAF0>
        """

        if isinstance(opcode, str):
            bytecode = Script.compile(opcode)
            self.script = ScriptBuilder.identify(bytecode)
            return self
        raise TypeError("op_code must be string format")
Beispiel #8
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
Beispiel #9
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
Beispiel #10
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
Beispiel #11
0
    def build_htlc(self, secret_hash: str, recipient_address: str,
                   sender_address: str, endtime: int) -> "HTLC":
        """
        Build Bitcoin Hash Time Lock Contract (HTLC).

        :param secret_hash: secret sha-256 hash.
        :type secret_hash: str
        :param recipient_address: Bitcoin recipient address.
        :type recipient_address: str
        :param sender_address: Bitcoin sender address.
        :type sender_address: str
        :param endtime: Expiration block time (Seconds).
        :type endtime: int

        :returns: HTLC -- Bitcoin Hash Time Lock Contract (HTLC) instance.

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> from swap.utils import sha256
        >>> htlc: HTLC = HTLC(network="testnet")
        >>> htlc.build_htlc(secret_hash=sha256("Hello Meheret!"), recipient_address="mgS3WMHp9nvdUPeDJxr5iCF2P5HuFZSR3V", sender_address="n1wgm6kkzMcNfAtJmes8YhpvtDzdNhDY5a", endtime=1624687630)
        <swap.providers.bitcoin.htlc.HTLC object at 0x0409DAF0>
        """

        # Check parameter instances
        if len(secret_hash) != 64:
            raise ValueError("Invalid secret hash, length must be 64.")
        if not is_address(address=recipient_address, network=self._network):
            raise AddressError(
                f"Invalid Bitcoin recipient '{recipient_address}' {self._network} address."
            )
        if not is_address(address=sender_address, network=self._network):
            raise AddressError(
                f"Invalid Bitcoin sender '{sender_address}' {self._network} address."
            )

        # Get current working directory path (like linux or unix path).
        cwd: str = PurePosixPath(os.path.dirname(
            os.path.realpath(__file__))).__str__().replace("\\", "/")

        with open(f"{cwd}/contracts/htlc.script", "r",
                  encoding="utf-8") as htlc_script:
            htlc_opcode: str = htlc_script.readlines()[
                -1]  # HTLC OP_Code script
            htlc_script.close()

        build_htlc_opcode: str = htlc_opcode.format(
            secret_hash=hashlib.sha256(unhexlify(secret_hash)).hexdigest(),
            recipient_address_hash=get_address_hash(address=recipient_address,
                                                    script=False),
            sender_address_hash=get_address_hash(address=sender_address,
                                                 script=False),
            endtime=Locktime(n=endtime).for_script().hexlify()[2:])

        self.agreements = {
            "secret_hash": secret_hash,
            "recipient_address": recipient_address,
            "sender_address": sender_address,
            "endtime": {
                "datetime": str(datetime.fromtimestamp(endtime)),
                "timestamp": endtime
            }
        }
        bytecode: str = Script.compile(build_htlc_opcode)
        self._script = ScriptBuilder.identify(bytecode)
        return self
Beispiel #12
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
Beispiel #13
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