Esempio n. 1
0
    def sign(self, solver):
        """
        Sign bitcoin refund transaction.

        :param solver: bitcoin refund solver.
        :type solver: bitcoin.solver.RefundSolver
        :returns: RefundTransaction -- bitcoin refund transaction instance.

        >>> from shuttle.providers.bitcoin.transaction import RefundTransaction
        >>> refund_transaction = RefundTransaction(network="testnet")
        >>> refund_transaction.build_transaction(fund_transaction_id, sender_wallet, 10000)
        >>> refund_transaction.sign(refund_solver)
        <shuttle.providers.bitcoin.transaction.RefundTransaction object at 0x0409DAF0>
        """
        if not isinstance(solver, RefundSolver):
            raise TypeError(
                "invalid solver instance, only takes bitcoin RefundSolver class"
            )
        if self.transaction is None:
            raise ValueError(
                "transaction script is none, build transaction first")
        htlc = HTLC(self.network).init(
            secret_hash=sha256(solver.secret).hex(),
            recipient_address=str(self.wallet.address()),
            sender_address=str(self.sender_account.address()),
            sequence=solver.sequence)
        self.transaction.spend([
            TxOut(value=self.htlc["value"],
                  n=0,
                  script_pubkey=P2shScript.unhexlify(self.htlc["script"]))
        ], [P2shSolver(htlc.script, solver.solve())])
        return self
Esempio n. 2
0
    def build_transaction(self, wallet, htlc, amount, locktime=0):
        """
        Build bitcoin fund transaction.

        :param wallet: bitcoin sender wallet.
        :type wallet: bitcoin.wallet.Wallet
        :param htlc: bitcoin hash time lock contract (HTLC).
        :type htlc: bitcoin.htlc.HTLC
        :param amount: bitcoin amount to fund.
        :type amount: int
        :param locktime: bitcoin transaction lock time, defaults to 0.
        :type locktime: int
        :returns: FundTransaction -- bitcoin fund transaction instance.

        >>> from shuttle.providers.bitcoin.transaction import FundTransaction
        >>> fund_transaction = FundTransaction(network="testnet")
        >>> fund_transaction.build_transaction(sender_wallet, htlc, 10000)
        <shuttle.providers.bitcoin.transaction.FundTransaction object at 0x0409DAF0>
        """

        # Checking build transaction arguments instance
        if not isinstance(wallet, Wallet):
            raise TypeError(
                "invalid wallet instance, only takes bitcoin Wallet class")
        if not isinstance(htlc, HTLC):
            raise TypeError(
                "invalid htlc instance, only takes bitcoin HTLC class")
        if not isinstance(amount, int):
            raise TypeError("invalid amount instance, only takes integer type")
        # Setting wallet, htlc, amount and unspent
        self.wallet, self.htlc, self.amount = wallet, htlc, amount
        # Getting unspent transaction output
        self.unspent = self.wallet.unspent()
        # Setting previous transaction indexes
        self.previous_transaction_indexes = \
            self.get_previous_transaction_indexes(amount=self.amount)
        # Getting transaction inputs and amount
        inputs, amount = self.inputs(self.unspent,
                                     self.previous_transaction_indexes)
        # Calculating bitcoin fee
        self.fee = fee_calculator(len(inputs), 2)
        if amount < (self.amount + self.fee):
            raise BalanceError("insufficient spend utxos")
        # Building mutable bitcoin transaction
        self.transaction = MutableTransaction(
            version=self.version,
            ins=inputs,
            outs=[
                # Funding into hash time lock contract script hash
                TxOut(value=self.amount,
                      n=0,
                      script_pubkey=P2shScript.unhexlify(self.htlc.hash())),
                # Controlling amounts when we are funding on htlc script.
                TxOut(value=amount - (self.fee + self.amount),
                      n=1,
                      script_pubkey=P2pkhScript.unhexlify(self.wallet.p2pkh()))
            ],
            locktime=Locktime(locktime))
        return self
Esempio n. 3
0
    def sign(self, unsigned_raw, solver):
        """
        Sign unsigned refund transaction raw.

        :param unsigned_raw: bitcoin unsigned refund transaction raw.
        :type unsigned_raw: str
        :param solver: bitcoin refund solver.
        :type solver: bitcoin.solver.RefundSolver
        :returns:  RefundSignature -- bitcoin refund signature instance.

        >>> from shuttle.providers.bitcoin.signature import RefundSignature
        >>> refund_signature = RefundSignature()
        >>> refund_signature.sign(bitcoin_refund_unsigned, refund_solver)
        <shuttle.providers.bitcoin.signature.RefundSignature 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 \
                "recipient_address" not in tx_raw or "sender_address" not in tx_raw or "fee" not in tx_raw:
            raise ValueError("invalid unsigned refund transaction raw")
        self.fee = tx_raw["fee"]
        self.type = tx_raw["type"]
        if not self.type == "bitcoin_refund_unsigned":
            raise TypeError("can't sign this %s transaction using RefundSignature" % tx_raw["type"])
        if not isinstance(solver, RefundSolver):
            raise Exception("invalid solver error, only refund solver")
        htlc = HTLC(network=self.network).init(
            secret_hash=sha256(solver.secret).hex(),
            recipient_address=tx_raw["recipient_address"],
            sender_address=tx_raw["sender_address"],
            sequence=solver.sequence
        )
        output = TxOut(value=tx_raw["outputs"][0]["amount"], n=tx_raw["outputs"][0]["n"],
                       script_pubkey=P2shScript.unhexlify(tx_raw["outputs"][0]["script"]))
        self.transaction = MutableTransaction.unhexlify(tx_raw["raw"])
        self.transaction.spend([output], [
            P2shSolver(htlc.script, solver.solve())
        ])
        self.signed = b64encode(str(json.dumps(dict(
            raw=self.transaction.hexlify(),
            fee=tx_raw["fee"],
            network=tx_raw["network"],
            type="bitcoin_refund_signed"
        ))).encode()).decode()
        return self
Esempio n. 4
0
    def sign(self, solver):
        """
        Sign Bitcoin refund transaction.

        :param solver: Bitcoin refund solver.
        :type solver: bitcoin.solver.RefundSolver
        :returns: RefundTransaction -- Bitcoin refund transaction instance.

        >>> from swap.providers.bitcoin.transaction import RefundTransaction
        >>> from swap.providers.bitcoin.solver import RefundSolver
        >>> from swap.providers.bitcoin.wallet import Wallet
        >>> sender_wallet = Wallet(network="testnet").from_passphrase("meherett1234")
        >>> refund_solver = RefundSolver(sender_wallet.private_key(), "3a26da82ead15a80533a02696656b14b5dbfd84eb14790f2e1be5e9e45820eeb",  "muTnffLDR5LtFeLR2i3WsKVfdyvzfyPnVB", sender_wallet.address(), 1000)
        >>> refund_transaction = RefundTransaction(network="testnet")
        >>> refund_transaction.build_transaction("1006a6f537fcc4888c65f6ff4f91818a1c6e19bdd3130f59391c00212c552fbd", sender_wallet, 10000)
        >>> refund_transaction.sign(solver=refund_solver)
        <swap.providers.bitcoin.transaction.RefundTransaction object at 0x0409DAF0>
        """

        # Checking parameter instances
        if not isinstance(solver, RefundSolver):
            raise TypeError("invalid solver instance, only takes Bitcoin RefundSolver class")
        if self.transaction is None:
            raise ValueError("transaction script is none, build transaction first")

        self.transaction.spend([
            TxOut(
                value=self.htlc_detail["value"],
                n=0,
                script_pubkey=P2shScript.unhexlify(
                    hex_string=self.htlc_detail["script"]
                )
            )
        ], [
            P2shSolver(
                redeem_script=solver.witness(
                    network=self.network
                ),
                redeem_script_solver=solver.solve()
            )
        ])
        self._type = "bitcoin_refund_signed"
        return self
Esempio n. 5
0
    def sign(self, solver):
        """
        Sign Bitcoin claim transaction.

        :param solver: Bitcoin claim solver.
        :type solver: bitcoin.solver.ClaimSolver
        :returns: ClaimTransaction -- Bitcoin claim transaction instance.

        >>> from swap.providers.bitcoin.transaction import ClaimTransaction
        >>> from swap.providers.bitcoin.solver import ClaimSolver
        >>> from swap.providers.bitcoin.wallet import Wallet
        >>> recipient_wallet = Wallet(network="testnet").from_passphrase("meherett")
        >>> claim_solver = ClaimSolver(recipient_wallet.private_key(), "Hello Meheret!", "3a26da82ead15a80533a02696656b14b5dbfd84eb14790f2e1be5e9e45820eeb", recipient_wallet.address(), "mphBPZf15cRFcL5tUq6mCbE84XobZ1vg7Q", 1000)
        >>> claim_transaction = ClaimTransaction(network="testnet")
        >>> claim_transaction.build_transaction("1006a6f537fcc4888c65f6ff4f91818a1c6e19bdd3130f59391c00212c552fbd", recipient_wallet, 10000)
        >>> claim_transaction.sign(solver=claim_solver)
        <swap.providers.bitcoin.transaction.ClaimTransaction object at 0x0409DAF0>
        """

        # Checking parameter instances
        if not isinstance(solver, ClaimSolver):
            raise TypeError("invalid solver instance, only takes Bitcoin ClaimSolver class")
        if self.transaction is None:
            raise ValueError("transaction script is none, build transaction first")

        self.transaction.spend([
            TxOut(
                value=self.htlc_detail["value"],
                n=0,
                script_pubkey=P2shScript.unhexlify(
                    hex_string=self.htlc_detail["script"]
                )
            )
        ], [
            P2shSolver(
                redeem_script=solver.witness(
                    network=self.network
                ),
                redeem_script_solver=solver.solve()
            )
        ])
        self._type = "bitcoin_claim_signed"
        return self
Esempio n. 6
0
    def sign(self, solver: WithdrawSolver) -> "WithdrawTransaction":
        """
        Sign Bitcoin withdraw transaction.

        :param solver: Bitcoin withdraw solver.
        :type solver: bitcoin.solver.WithdrawSolver

        :returns: WithdrawTransaction -- Bitcoin withdraw transaction instance.

        >>> from swap.providers.bitcoin.transaction import WithdrawTransaction
        >>> from swap.providers.bitcoin.solver import WithdrawSolver
        >>> withdraw_transaction: WithdrawTransaction = WithdrawTransaction("testnet")
        >>> withdraw_transaction.build_transaction(address="mgS3WMHp9nvdUPeDJxr5iCF2P5HuFZSR3V", transaction_hash="a211d21110756b266925fee2fbf2dc81529beef5e410311b38578dc3a076fb31")
        >>> bytecode: str = "63aa20821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e01588876a9140a0a6590e6ba4b48118d21b86812615219ece76b88ac67040ec4d660b17576a914e00ff2a640b7ce2d336860739169487a57f84b1588ac68"
        >>> withdraw_solver: WithdrawSolver = WithdrawSolver(xprivate_key="tprv8ZgxMBicQKsPf949JcuVFLXPJ5m4VKe33gVX3FYVZYVHr2dChU8K66aEQcPdHpUgACq5GQu81Z4e3QN1vxCrV4pxcUcXHoRTamXBRaPdJhW", secret_key="Hello Meheret!", bytecode=bytecode)
        >>> withdraw_transaction.sign(solver=withdraw_solver)
        <swap.providers.bitcoin.transaction.WithdrawTransaction object at 0x0409DAF0>
        """

        # Check parameter instances
        if not isinstance(solver, WithdrawSolver):
            raise TypeError(
                f"Solver must be Bitcoin WithdrawSolver, not {type(solver).__name__} type."
            )
        if self._transaction is None:
            raise ValueError("Transaction is none, build transaction first.")

        self._transaction.spend([
            TxOut(value=self._htlc_utxo["value"],
                  n=0,
                  script_pubkey=P2shScript.unhexlify(
                      hex_string=self._htlc_utxo["script"]))
        ], [
            P2shSolver(
                redeem_script=solver.witness(network=self._network),
                redeem_script_solver=solver.solve(network=self._network))
        ])

        # Set transaction type
        self._type = "bitcoin_withdraw_signed"
        return self
Esempio n. 7
0
    def sign(self, solver: RefundSolver) -> "RefundTransaction":
        """
        Sign Bitcoin refund transaction.

        :param solver: Bitcoin refund solver.
        :type solver: bitcoin.solver.RefundSolver

        :returns: RefundTransaction -- Bitcoin refund transaction instance.

        >>> from swap.providers.bitcoin.transaction import RefundTransaction
        >>> refund_transaction: RefundTransaction = RefundTransaction("testnet")
        >>> refund_transaction.build_transaction(address="n1wgm6kkzMcNfAtJmes8YhpvtDzdNhDY5a", transaction_hash="a211d21110756b266925fee2fbf2dc81529beef5e410311b38578dc3a076fb31")
        >>> bytecode: str = "63aa20821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e01588876a9140a0a6590e6ba4b48118d21b86812615219ece76b88ac67040ec4d660b17576a914e00ff2a640b7ce2d336860739169487a57f84b1588ac68"
        >>> refund_solver: RefundSolver = RefundSolver(xprivate_key="tprv8ZgxMBicQKsPeMHMJAc6uWGYiGqi1MVM2ybmzXL2TAoDpQe85uyDpdT7mv7Nhdu5rTCBEKLZsd9KyP2LQZJzZTvgVQvENArgU8e6DoYBiXf", bytecode=bytecode, endtime=1624687630)
        >>> refund_transaction.sign(solver=refund_solver)
        <swap.providers.bitcoin.transaction.RefundTransaction object at 0x0409DAF0>
        """

        # Check parameter instances
        if not isinstance(solver, RefundSolver):
            raise TypeError(
                f"Solver must be Bitcoin RefundSolver, not {type(solver).__name__} type."
            )
        if self._transaction is None:
            raise ValueError("Transaction is none, build transaction first.")

        self._transaction.spend([
            TxOut(value=self._htlc_utxo["value"],
                  n=0,
                  script_pubkey=P2shScript.unhexlify(
                      hex_string=self._htlc_utxo["script"]))
        ], [
            P2shSolver(
                redeem_script=solver.witness(network=self._network),
                redeem_script_solver=solver.solve(network=self._network))
        ])

        # Set transaction type
        self._type = "bitcoin_refund_signed"
        return self
Esempio n. 8
0
    def sign(self, unsigned_raw, solver):
        """
        Sign unsigned refund transaction raw.

        :param unsigned_raw: Bitcoin unsigned refund transaction raw.
        :type unsigned_raw: str
        :param solver: Bitcoin refund solver.
        :type solver: bitcoin.solver.RefundSolver
        :returns:  RefundSignature -- Bitcoin refund signature instance.

        >>> from shuttle.providers.bitcoin.signature import RefundSignature
        >>> from shuttle.providers.bitcoin.solver import RefundSolver
        >>> bitcoin_refund_unsigned_raw = "eyJmZWUiOiA1NzYsICJyYXciOiAiMDIwMDAwMDAwMTUyYzIzZGM2NDU2N2IxY2ZhZjRkNzc2NjBjNzFjNzUxZjkwZTliYTVjMzc0N2ZhYzFkMDA1MTgwOGVhMGQ2NTEwMDAwMDAwMDAwZmZmZmZmZmYwMTQ4MTEwMDAwMDAwMDAwMDAxOTc2YTkxNDY0YTgzOTBiMGIxNjg1ZmNiZjJkNGI0NTcxMThkYzhkYTkyZDU1MzQ4OGFjMDAwMDAwMDAiLCAib3V0cHV0cyI6IHsidmFsdWUiOiA1MDAwLCAibiI6IDAsICJzY3JpcHRfcHVia2V5IjogImE5MTQ0MzNlOGVkNTliOWE2N2YwZjE4N2M2M2ViNDUwYjBkNTZlMjU2ZWMyODcifSwgIm5ldHdvcmsiOiAidGVzdG5ldCIsICJ0eXBlIjogImJpdGNvaW5fcmVmdW5kX3Vuc2lnbmVkIn0"
        >>> refund_solver = RefundSolver("92cbbc5990cb5090326a76feeb321cad01048635afe5756523bbf9f7a75bf38b", "3a26da82ead15a80533a02696656b14b5dbfd84eb14790f2e1be5e9e45820eeb",  "muTnffLDR5LtFeLR2i3WsKVfdyvzfyPnVB", "mphBPZf15cRFcL5tUq6mCbE84XobZ1vg7Q", 1000)
        >>> refund_signature = RefundSignature(network="testnet")
        >>> refund_signature.sign(unsigned_raw=bitcoin_refund_unsigned_raw, solver=refund_solver)
        <shuttle.providers.bitcoin.signature.RefundSignature object at 0x0409DAF0>
        """

        # Decoding and loading refund transaction
        refund_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 refund_transaction:
                raise ValueError(
                    "invalid Bitcoin unsigned refund transaction raw")
        if not refund_transaction["type"] == "bitcoin_refund_unsigned":
            raise TypeError(
                f"invalid Bitcoin refund unsigned transaction type, "
                f"you can't sign this {refund_transaction['type']} type by using RefundSignature"
            )
        if not isinstance(solver, RefundSolver):
            raise TypeError(
                "invalid Bitcoin solver, it's only takes Bitcoin RefundSolver class"
            )

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

        # Signing refund transaction
        self.transaction.spend([
            TxOut(
                value=refund_transaction["outputs"]["value"],
                n=refund_transaction["outputs"]["n"],
                script_pubkey=P2shScript.unhexlify(
                    hex_string=refund_transaction["outputs"]["script_pubkey"]))
        ], [
            P2shSolver(redeem_script=solver.witness(
                network=refund_transaction["network"]),
                       redeem_script_solver=solver.solve())
        ])

        # Encoding refund transaction raw
        self._type = "bitcoin_refund_signed"
        self._signed_raw = b64encode(
            str(
                json.dumps(
                    dict(raw=self.transaction.hexlify(),
                         fee=refund_transaction["fee"],
                         network=refund_transaction["network"],
                         type=self._type))).encode()).decode()
        return self
Esempio n. 9
0
    def sign(self, transaction_raw: str, solver: RefundSolver) -> "RefundSignature":
        """
        Sign unsigned refund transaction raw.

        :param transaction_raw: Bitcoin unsigned refund transaction raw.
        :type transaction_raw: str
        :param solver: Bitcoin refund solver.
        :type solver: bitcoin.solver.RefundSolver
        :returns:  RefundSignature -- Bitcoin refund signature instance.

        >>> from swap.providers.bitcoin.signature import Signature
        >>> from swap.providers.bitcoin.solver import RefundSolver
        >>> unsigned_refund_transaction_raw: str = "eyJmZWUiOiA1NzYsICJyYXciOiAiMDIwMDAwMDAwMTMxZmI3NmEwYzM4ZDU3MzgxYjMxMTBlNGY1ZWU5YjUyODFkY2YyZmJlMmZlMjU2OTI2NmI3NTEwMTFkMjExYTIwMDAwMDAwMDAwZmZmZmZmZmYwMTYwODQwMTAwMDAwMDAwMDAxOTc2YTkxNGUwMGZmMmE2NDBiN2NlMmQzMzY4NjA3MzkxNjk0ODdhNTdmODRiMTU4OGFjMDAwMDAwMDAiLCAib3V0cHV0cyI6IHsidmFsdWUiOiAxMDAwMDAsICJ0eF9vdXRwdXRfbiI6IDAsICJzY3JpcHQiOiAiYTkxNGM4Yzc3YTliNDNlZTJiZGYxYTA3YzQ4Njk5ODMzZDc2NjhiZjI2NGM4NyJ9LCAibmV0d29yayI6ICJ0ZXN0bmV0IiwgInR5cGUiOiAiYml0Y29pbl9yZWZ1bmRfdW5zaWduZWQifQ"
        >>> bytecode: str = "63aa20821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e01588876a9140a0a6590e6ba4b48118d21b86812615219ece76b88ac67040ec4d660b17576a914e00ff2a640b7ce2d336860739169487a57f84b1588ac68"
        >>> refund_solver: RefundSolver = RefundSolver(xprivate_key="tprv8ZgxMBicQKsPeMHMJAc6uWGYiGqi1MVM2ybmzXL2TAoDpQe85uyDpdT7mv7Nhdu5rTCBEKLZsd9KyP2LQZJzZTvgVQvENArgU8e6DoYBiXf", bytecode=bytecode, endtime=1624687630)
        >>> refund_signature: RefundSignature = RefundSignature(network="testnet")
        >>> refund_signature.sign(transaction_raw=unsigned_refund_transaction_raw, solver=refund_solver)
        <swap.providers.bitcoin.signature.RefundSignature 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_refund_unsigned":
            raise TypeError(f"Invalid Bitcoin refund unsigned transaction raw type, "
                            f"you can't sign {loaded_transaction_raw['type']} type by using refund signature.")

        # Check parameter instances
        if not isinstance(solver, RefundSolver):
            raise TypeError(f"Solver must be Bitcoin RefundSolver, 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"])
        )

        # Sign refund transaction
        self._transaction.spend([TxOut(
            value=loaded_transaction_raw["outputs"]["value"],
            n=loaded_transaction_raw["outputs"]["tx_output_n"],
            script_pubkey=P2shScript.unhexlify(
                hex_string=loaded_transaction_raw["outputs"]["script"]
            )
        )], [P2shSolver(
            redeem_script=solver.witness(
                network=self._network
            ),
            redeem_script_solver=solver.solve(
                network=self._network
            )
        )])

        # Encode refund transaction raw
        self._type = "bitcoin_refund_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
Esempio n. 10
0
    def sign(self, transaction_raw: str, solver: WithdrawSolver) -> "WithdrawSignature":
        """
        Sign unsigned withdraw transaction raw.

        :param transaction_raw: Bitcoin unsigned withdraw transaction raw.
        :type transaction_raw: str
        :param solver: Bitcoin withdraw solver.
        :type solver: bitcoin.solver.WithdrawSolver

        :returns: WithdrawSignature -- Bitcoin withdraw signature instance.

        >>> from swap.providers.bitcoin.signature import WithdrawSignature
        >>> from swap.providers.bitcoin.solver import WithdrawSolver
        >>> unsigned_withdraw_transaction_raw: str = "eyJmZWUiOiA1NzYsICJyYXciOiAiMDIwMDAwMDAwMTMxZmI3NmEwYzM4ZDU3MzgxYjMxMTBlNGY1ZWU5YjUyODFkY2YyZmJlMmZlMjU2OTI2NmI3NTEwMTFkMjExYTIwMDAwMDAwMDAwZmZmZmZmZmYwMTYwODQwMTAwMDAwMDAwMDAxOTc2YTkxNDBhMGE2NTkwZTZiYTRiNDgxMThkMjFiODY4MTI2MTUyMTllY2U3NmI4OGFjMDAwMDAwMDAiLCAib3V0cHV0cyI6IHsidmFsdWUiOiAxMDAwMDAsICJ0eF9vdXRwdXRfbiI6IDAsICJzY3JpcHQiOiAiYTkxNGM4Yzc3YTliNDNlZTJiZGYxYTA3YzQ4Njk5ODMzZDc2NjhiZjI2NGM4NyJ9LCAibmV0d29yayI6ICJ0ZXN0bmV0IiwgInR5cGUiOiAiYml0Y29pbl93aXRoZHJhd191bnNpZ25lZCJ9"
        >>> bytecode: str = "63aa20821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e01588876a9140a0a6590e6ba4b48118d21b86812615219ece76b88ac67040ec4d660b17576a914e00ff2a640b7ce2d336860739169487a57f84b1588ac68"
        >>> withdraw_solver: WithdrawSolver = WithdrawSolver(xprivate_key="tprv8ZgxMBicQKsPf949JcuVFLXPJ5m4VKe33gVX3FYVZYVHr2dChU8K66aEQcPdHpUgACq5GQu81Z4e3QN1vxCrV4pxcUcXHoRTamXBRaPdJhW", secret_key="Hello Meheret!", bytecode=bytecode)
        >>> withdraw_signature: WithdrawSignature = WithdrawSignature(network="testnet")
        >>> withdraw_signature.sign(transaction_raw=unsigned_withdraw_transaction_raw, solver=withdraw_solver)
        <swap.providers.bitcoin.signature.WithdrawSignature 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_withdraw_unsigned":
            raise TypeError(f"Invalid Bitcoin withdraw unsigned transaction raw type, "
                            f"you can't sign {loaded_transaction_raw['type']} type by using withdraw signature.")

        # Check parameter instances
        if not isinstance(solver, WithdrawSolver):
            raise TypeError(f"Solver must be Bitcoin WithdrawSolver, 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"])
        )

        # Sign withdraw transaction
        self._transaction.spend([TxOut(
            value=loaded_transaction_raw["outputs"]["value"],
            n=loaded_transaction_raw["outputs"]["tx_output_n"],
            script_pubkey=P2shScript.unhexlify(
                hex_string=loaded_transaction_raw["outputs"]["script"]
            )
        )], [P2shSolver(
            redeem_script=solver.witness(
                network=self._network
            ),
            redeem_script_solver=solver.solve(
                network=self._network
            )
        )])

        # Encode withdraw transaction raw
        self._type = "bitcoin_withdraw_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
Esempio n. 11
0
    def build_transaction(self, wallet, htlc, amount, locktime=0):
        """
        Build Bitcoin fund transaction.

        :param wallet: Bitcoin sender wallet.
        :type wallet: bitcoin.wallet.Wallet
        :param htlc: Bitcoin hash time lock contract (HTLC).
        :type htlc: bitcoin.htlc.HTLC
        :param amount: Bitcoin amount to fund.
        :type amount: int
        :param locktime: Bitcoin transaction lock time, defaults to 0.
        :type locktime: int
        :returns: FundTransaction -- Bitcoin fund transaction instance.

        >>> from shuttle.providers.bitcoin.htlc import HTLC
        >>> from shuttle.providers.bitcoin.transaction import FundTransaction
        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> htlc = HTLC(network="testnet").init("821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e0158", "muTnffLDR5LtFeLR2i3WsKVfdyvzfyPnVB", "mphBPZf15cRFcL5tUq6mCbE84XobZ1vg7Q", 1000)
        >>> sender_wallet = Wallet(network="testnet").from_passphrase("meherett")
        >>> fund_transaction = FundTransaction(network="testnet")
        >>> fund_transaction.build_transaction(wallet=sender_wallet, htlc=htlc, amount=10000)
        <shuttle.providers.bitcoin.transaction.FundTransaction object at 0x0409DAF0>
        """

        # Checking parameter instances
        if not isinstance(wallet, Wallet):
            raise TypeError(
                "invalid wallet instance, only takes Bitcoin Wallet class")
        if not isinstance(htlc, HTLC):
            raise TypeError(
                "invalid htlc instance, only takes Bitcoin HTLC class")
        if not isinstance(amount, int):
            raise TypeError("invalid amount instance, only takes integer type")

        # Setting wallet, htlc, amount and unspent
        self.wallet, self.htlc, self.amount = wallet, htlc, amount
        # Getting unspent transaction output
        self.unspent = self.wallet.unspent()
        # Setting previous transaction indexes
        self.previous_transaction_indexes = \
            self.get_previous_transaction_indexes(amount=self.amount)

        # Getting transaction inputs and amount
        inputs, amount = self.inputs(
            utxos=self.unspent,
            previous_transaction_indexes=self.previous_transaction_indexes)

        # Calculating Bitcoin fee
        self._fee = fee_calculator(len(inputs), 2)
        if amount < (self.amount + self._fee):
            raise BalanceError("insufficient spend utxos")

        # Building mutable Bitcoin transaction
        self.transaction = MutableTransaction(
            version=self.version,
            ins=inputs,
            outs=[
                # Funding into hash time lock contract script hash
                TxOut(value=self.amount,
                      n=0,
                      script_pubkey=P2shScript.unhexlify(
                          hex_string=self.htlc.hash())),
                # Controlling amounts when we are funding on htlc script
                TxOut(value=(amount - (self._fee + self.amount)),
                      n=1,
                      script_pubkey=P2pkhScript.unhexlify(
                          hex_string=self.wallet.p2pkh()))
            ],
            locktime=Locktime(locktime))
        self._type = "bitcoin_fund_unsigned"
        return self
Esempio n. 12
0
    def build_transaction(self,
                          transaction_id,
                          wallet,
                          amount,
                          secret=None,
                          locktime=0):
        """
        Build bitcoin refund transaction.

        :param transaction_id: bitcoin fund transaction id to redeem.
        :type transaction_id: str
        :param wallet: bitcoin sender wallet.
        :type wallet: bitcoin.wallet.Wallet
        :param amount: bitcoin amount to withdraw.
        :type amount: int
        :param secret: secret passphrase.
        :type secret: str
        :param locktime: bitcoin transaction lock time, defaults to 0.
        :type locktime: int
        :returns: RefundTransaction -- bitcoin refund transaction instance.

        >>> from shuttle.providers.bitcoin.transaction import RefundTransaction
        >>> refund_transaction = RefundTransaction(network="testnet")
        >>> refund_transaction.build_transaction(fund_transaction_id, sender_wallet, 10000)
        <shuttle.providers.bitcoin.transaction.RefundTransaction object at 0x0409DAF0>
        """

        # Checking build transaction arguments instance
        if not isinstance(transaction_id, str):
            raise TypeError("invalid amount instance, only takes string type")
        if not isinstance(wallet, Wallet):
            raise TypeError(
                "invalid wallet instance, only takes bitcoin Wallet class")
        if secret is not None and not isinstance(secret, str):
            raise TypeError("invalid secret instance, only takes string type")
        # Setting transaction_id and wallet
        self.transaction_id, self.wallet, self.secret = transaction_id, wallet, secret
        # Getting transaction detail by id
        self.transaction_detail = get_transaction_detail(self.transaction_id)
        # Checking transaction outputs
        if "outputs" not in self.transaction_detail:
            raise NotFoundError("not found htlc in this %s hash" %
                                self.transaction_id)
        # Hash time lock contract output
        self.htlc = self.transaction_detail["outputs"][0]
        # Sender account output
        sender_address = P2pkhScript.unhexlify(
            self.transaction_detail["outputs"][1]["script"]).address(
                mainnet=self.mainnet)
        self.sender_account = Wallet(network=self.network).from_address(
            str(sender_address))
        # HTLC info's
        htlc_amount = self.htlc["value"]
        htlc_script = P2shScript.unhexlify(self.htlc["script"])
        # Calculating fee
        self.fee = fee_calculator(1, 1)
        if amount < self.fee:
            raise BalanceError("insufficient spend utxos")
        elif not htlc_amount >= (amount - self.fee):
            raise BalanceError("insufficient spend utxos",
                               "maximum withdraw %d" % htlc_amount)
        # Building mutable bitcoin transaction
        self.transaction = MutableTransaction(
            version=self.version,
            ins=[
                TxIn(txid=self.transaction_id,
                     txout=0,
                     script_sig=ScriptSig.empty(),
                     sequence=Sequence.max())
            ],
            outs=[
                TxOut(value=(amount - self.fee),
                      n=0,
                      script_pubkey=P2pkhScript.unhexlify(
                          self.sender_account.p2pkh()))
            ],
            locktime=Locktime(locktime))
        return self
Esempio n. 13
0
    def sign(self, transaction_raw: str, solver: RefundSolver) -> "RefundSignature":
        """
        Sign unsigned refund transaction raw.

        :param transaction_raw: Bitcoin unsigned refund transaction raw.
        :type transaction_raw: str
        :param solver: Bitcoin refund solver.
        :type solver: bitcoin.solver.RefundSolver
        :returns:  RefundSignature -- Bitcoin refund signature instance.

        >>> from swap.providers.bitcoin.signature import RefundSignature
        >>> from swap.providers.bitcoin.solver import RefundSolver
        >>> unsigned_refund_transaction_raw = "eyJmZWUiOiA1NzYsICJyYXciOiAiMDIwMDAwMDAwMTE4MjNmMzlhOGM1ZjZmMjc4NDVkZDEzYTY1ZTAzZmUyZWY1MTA4ZDIzNWU3YTM2ZWRiNmViMjY3YjA0NTljNWEwMDAwMDAwMDAwZmZmZmZmZmYwMWQwMjQwMDAwMDAwMDAwMDAxOTc2YTkxNDMzZWNhYjNkNjdmMGUyYmRlNDNlNTJmNDFlYzFlY2JkYzczZjExZjg4OGFjMDAwMDAwMDAiLCAib3V0cHV0cyI6IHsidmFsdWUiOiAxMDAwMCwgInR4X291dHB1dF9uIjogMCwgInNjcmlwdCI6ICJhOTE0OTQxOGZlZWQ0NjQ3ZTE1NmQ2NjYzZGIzZTBjZWY3YzA1MGQwMzg2Nzg3In0sICJuZXR3b3JrIjogInRlc3RuZXQiLCAidHlwZSI6ICJiaXRjb2luX3JlZnVuZF91bnNpZ25lZCJ9"
        >>> sender_root_xprivate_key = "xprv9s21ZrQH143K3XihXQBN8Uar2WBtrjSzK2oRDEGQ25pA2kKAADoQXaiiVXht163ZTrdtTXfM4GqNRE9gWQHky25BpvBQuuhNCM3SKwWTPNJ"
        >>> bytecode = "63aa20821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e01588876a9140e259e08f2ec9fc99a92b6f66fdfcb3c7914fd6888ac6702e803b27576a91433ecab3d67f0e2bde43e52f41ec1ecbdc73f11f888ac68"
        >>> refund_solver = RefundSolver(sender_root_xprivate_key, bytecode, 1000)
        >>> refund_signature = RefundSignature("testnet")
        >>> refund_signature.sign(transaction_raw=unsigned_refund_transaction_raw, solver=refund_solver)
        <swap.providers.bitcoin.signature.RefundSignature 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_refund_unsigned":
            raise TypeError(f"Invalid Bitcoin refund unsigned transaction raw type, "
                            f"you can't sign {loaded_transaction_raw['type']} type by using refund signature.")

        # Check parameter instances
        if not isinstance(solver, RefundSolver):
            raise TypeError(f"Solver must be Bitcoin RefundSolver, 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"])
        )

        # Sign refund transaction
        self._transaction.spend([TxOut(
            value=loaded_transaction_raw["outputs"]["value"],
            n=loaded_transaction_raw["outputs"]["tx_output_n"],
            script_pubkey=P2shScript.unhexlify(
                hex_string=loaded_transaction_raw["outputs"]["script"]
            )
        )], [P2shSolver(
            redeem_script=solver.witness(
                network=self._network
            ),
            redeem_script_solver=solver.solve(
                network=self._network
            )
        )])

        # Encode refund transaction raw
        self._type = "bitcoin_refund_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
Esempio n. 14
0
    def sign(self, transaction_raw: str, solver: ClaimSolver) -> "ClaimSignature":
        """
        Sign unsigned claim transaction raw.

        :param transaction_raw: Bitcoin unsigned claim transaction raw.
        :type transaction_raw: str
        :param solver: Bitcoin claim solver.
        :type solver: bitcoin.solver.ClaimSolver

        :returns: ClaimSignature -- Bitcoin claim signature instance.

        >>> from swap.providers.bitcoin.signature import ClaimSignature
        >>> from swap.providers.bitcoin.solver import ClaimSolver
        >>> unsigned_claim_transaction_raw = "eyJmZWUiOiA1NzYsICJyYXciOiAiMDIwMDAwMDAwMTE4MjNmMzlhOGM1ZjZmMjc4NDVkZDEzYTY1ZTAzZmUyZWY1MTA4ZDIzNWU3YTM2ZWRiNmViMjY3YjA0NTljNWEwMDAwMDAwMDAwZmZmZmZmZmYwMWQwMjQwMDAwMDAwMDAwMDAxOTc2YTkxNDBlMjU5ZTA4ZjJlYzlmYzk5YTkyYjZmNjZmZGZjYjNjNzkxNGZkNjg4OGFjMDAwMDAwMDAiLCAib3V0cHV0cyI6IHsidmFsdWUiOiAxMDAwMCwgInR4X291dHB1dF9uIjogMCwgInNjcmlwdCI6ICJhOTE0OTQxOGZlZWQ0NjQ3ZTE1NmQ2NjYzZGIzZTBjZWY3YzA1MGQwMzg2Nzg3In0sICJuZXR3b3JrIjogInRlc3RuZXQiLCAidHlwZSI6ICJiaXRjb2luX2NsYWltX3Vuc2lnbmVkIn0"
        >>> recipient_root_xprivate_key = "xprv9s21ZrQH143K4Kpce43z5guPyxLrFoc2i8aQAq835Zzp4Rt7i6nZaMCnVSDyHT6MnmJJGKHMrCUqaYpGojrug1ZN5qQDdShQffmkyv5xyUR"
        >>> bytecode = "63aa20821124b554d13f247b1e5d10b84e44fb1296f18f38bbaa1bea34a12c843e01588876a9140e259e08f2ec9fc99a92b6f66fdfcb3c7914fd6888ac6702e803b27576a91433ecab3d67f0e2bde43e52f41ec1ecbdc73f11f888ac68"
        >>> claim_solver = ClaimSolver(recipient_root_xprivate_key, "Hello Meheret!", bytecode)
        >>> claim_signature = ClaimSignature("testnet")
        >>> claim_signature.sign(transaction_raw=unsigned_claim_transaction_raw, solver=claim_solver)
        <swap.providers.bitcoin.signature.ClaimSignature 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_claim_unsigned":
            raise TypeError(f"Invalid Bitcoin claim unsigned transaction raw type, "
                            f"you can't sign {loaded_transaction_raw['type']} type by using claim signature.")

        # Check parameter instances
        if not isinstance(solver, ClaimSolver):
            raise TypeError(f"Solver must be Bitcoin ClaimSolver, 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"])
        )

        # Sign claim transaction
        self._transaction.spend([TxOut(
            value=loaded_transaction_raw["outputs"]["value"],
            n=loaded_transaction_raw["outputs"]["tx_output_n"],
            script_pubkey=P2shScript.unhexlify(
                hex_string=loaded_transaction_raw["outputs"]["script"]
            )
        )], [P2shSolver(
            redeem_script=solver.witness(
                network=self._network
            ),
            redeem_script_solver=solver.solve(
                network=self._network
            )
        )])

        # Encode claim transaction raw
        self._type = "bitcoin_claim_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