Ejemplo 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
Ejemplo n.º 2
0
def get_address_hash(
        address: str,
        script: bool = False) -> Union[str, P2pkhScript, P2shScript]:
    """
    Get Bitcoin address hash.

    :param address: Bitcoin address.
    :type address: str
    :param script: Return script (P2pkhScript, P2shScript), default to False.
    :type script: bool

    :returns: str -- Bitcoin address hash.

    >>> from swap.providers.bitcoin.utils import get_address_hash
    >>> get_address_hash(address="mrmtGq2HMmqAogSsGDjCtXUpxrb7rHThFH", script=False)
    "7b7c4431a43b612a72f8229935c469f1f6903658"
    """

    if not is_address(address=address):
        raise AddressError(f"Invalid Bitcoin '{address}' address.")

    loaded_address = Address.from_string(address)
    get_type = loaded_address.get_type()
    if not script:
        return loaded_address.hash.hex()
    if str(get_type) == "p2pkh":
        return P2pkhScript(loaded_address)
    elif str(get_type) == "p2sh":
        return P2shScript(loaded_address)
Ejemplo n.º 3
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
Ejemplo n.º 4
0
def p2sh_p2pkh_script(network: str, address: str) -> P2shScript:
    '''p2sh embedding p2pkh'''

    network_params = net_query(network)

    addr = Address.from_string(network=network_params,
                               string=address)

    p2pkh = P2pkhScript(addr)

    return P2shScript(p2pkh)
Ejemplo n.º 5
0
def script_from_address(address, network="testnet"):

    if not is_address(address, network):
        raise AddressError("invalid %s %s address!" % (network, address))

    load_address = Address.from_string(address)
    get_type = load_address.get_type()
    if str(get_type) == "p2pkh":
        return P2pkhScript(load_address)
    elif str(get_type) == "p2sh":
        return P2shScript(load_address)
Ejemplo n.º 6
0
    def p2sh(self, address=None):
        """
        Get Bitcoin wallet p2sh.

        :param address: Bitcoin p2sh, default is None.
        :type address: str
        :return: str -- Bitcoin p2sh.

        >>> from shuttle.providers.bitcoin.wallet import Wallet
        >>> wallet = Wallet(network="testnet")
        >>> wallet.from_passphrase("meherett")
        >>> wallet.p2sh()
        "a914a3c4995d9cd0303e5f89ee1433212c797d04ee5d87"
        """
        if address is None:
            return P2shScript(P2pkhScript(self._address)).hexlify()
        if not is_address(address=address, network=self.network):
            raise AddressError("invalid %s %s address" %
                               (self.network, address))
        address = Address.from_string(address)
        return P2shScript(P2pkhScript(address)).hexlify()
Ejemplo n.º 7
0
    def hash(self):
        """
        Get Bitcoin Hash Time Lock Contract (HTLC) hash.

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

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> htlc = HTLC(network="testnet")
        >>> htlc.init("3a26da82ead15a80533a02696656b14b5dbfd84eb14790f2e1be5e9e45820eeb", "muTnffLDR5LtFeLR2i3WsKVfdyvzfyPnVB", "mphBPZf15cRFcL5tUq6mCbE84XobZ1vg7Q", 1000)
        >>> htlc.hash()
        "a9142bb013c3e4beb08421dedcf815cb65a5c388178b87"
        """

        if self.script is None:
            raise ValueError("htlc script is none, initialization htlc first")
        return str(P2shScript(self.script.p2sh_hash()).hexlify())
Ejemplo n.º 8
0
    def hash(self):
        """
        Get bitcoin Hash Time Lock Contract (HTLC) hash.

        :returns: str -- bitcoin Hash Time Lock Contract (HTLC) hash.

        >>> from shuttle.providers.bitcoin.htlc import HTLC
        >>> htlc = HTLC(network="testnet")
        >>> htlc.init(secret_hash, recipient_address, sender_address, 100)
        >>> htlc.hash()
        "a914971894c58d85981c16c2059d422bcde0b156d04487"
        """

        if self.script is None:
            raise ValueError("htlc script is none, initialization htlc first")
        return P2shScript(self.script.p2sh_hash()).hexlify()
Ejemplo n.º 9
0
    def address(self):
        """
        Get bitcoin Hash Time Lock Contract (HTLC) address.

        :returns: str -- bitcoin Hash Time Lock Contract (HTLC) address.

        >>> from shuttle.providers.bitcoin.htlc import HTLC
        >>> htlc = HTLC(network="testnet")
        >>> htlc.init(secret_hash, recipient_address, sender_address, 100)
        >>> htlc.address()
        "2N729UBGZB3xjsGFRgKivy4bSjkaJGMVSpB"
        """

        if self.script is None:
            raise ValueError("htlc script is none, initialization htlc first")
        return P2shScript(self.script.p2sh_hash()).address(mainnet=self.mainnet)
Ejemplo n.º 10
0
    def address(self):
        """
        Get Bitcoin Hash Time Lock Contract (HTLC) address.

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

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> htlc = HTLC(network="testnet")
        >>> htlc.init("3a26da82ead15a80533a02696656b14b5dbfd84eb14790f2e1be5e9e45820eeb", "muTnffLDR5LtFeLR2i3WsKVfdyvzfyPnVB", "mphBPZf15cRFcL5tUq6mCbE84XobZ1vg7Q", 1000)
        >>> htlc.address()
        "2MwEDybGC34949zgzWX4M9FHmE3crDSUydP"
        """

        if self.script is None:
            raise ValueError("htlc script is none, initialization htlc first")
        return str(
            P2shScript(self.script.p2sh_hash()).address(mainnet=self.mainnet))
Ejemplo n.º 11
0
    def hash(self) -> str:
        """
        Get Bitcoin HTLC hash.

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

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> from swap.utils import sha256
        >>> htlc: HTLC = HTLC(network="testnet")
        >>> htlc.build_htlc(sha256("Hello Meheret!"), "mgS3WMHp9nvdUPeDJxr5iCF2P5HuFZSR3V", "n1wgm6kkzMcNfAtJmes8YhpvtDzdNhDY5a", 1624687630)
        >>> htlc.hash()
        "a914c8c77a9b43ee2bdf1a07c48699833d7668bf264c87"
        """

        if self._script is None:
            raise ValueError("HTLC script is None, first build HTLC.")
        return str(P2shScript(self._script.p2sh_hash()).hexlify())
Ejemplo n.º 12
0
    def hash(self) -> str:
        """
        Get Bitcoin HTLC hash.

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

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> from swap.utils import sha256
        >>> htlc = HTLC(network="testnet")
        >>> htlc.build_htlc(sha256("Hello Meheret!"), "mgokpSJoX7npmAK1Zj8ze1926CLxYDt1iF", "mkFWGt4hT11XS8dJKzzRFsTrqjjAwZfQAC", 1000)
        >>> htlc.hash()
        "a9149418feed4647e156d6663db3e0cef7c050d0386787"
        """

        if self._script is None:
            raise ValueError("HTLC script is None, first build HTLC.")
        return str(P2shScript(self._script.p2sh_hash()).hexlify())
Ejemplo n.º 13
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
Ejemplo n.º 14
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
Ejemplo n.º 15
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
Ejemplo n.º 16
0
    def address(self) -> str:
        """
        Get Bitcoin Hash Time Lock Contract (HTLC) address.

        :returns: str -- Bitcoin HTLC address.

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> from swap.utils import sha256
        >>> htlc = HTLC(network="testnet")
        >>> htlc.build_htlc(sha256("Hello Meheret!"), "mgokpSJoX7npmAK1Zj8ze1926CLxYDt1iF", "mkFWGt4hT11XS8dJKzzRFsTrqjjAwZfQAC", 1000)
        >>> htlc.address()
        "2N6kHwQy6Ph5EdKNgzGrcW2WhGHKGfmP5ae"
        """

        if self._script is None:
            raise ValueError("HTLC script is None, first build HTLC.")
        return str(
            P2shScript(self._script.p2sh_hash()).address(
                mainnet=(True if self._network == "mainnet" else False)))
Ejemplo n.º 17
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
Ejemplo n.º 18
0
    def contract_address(self) -> str:
        """
        Get Bitcoin Hash Time Lock Contract (HTLC) address.

        :returns: str -- Bitcoin HTLC address.

        >>> from swap.providers.bitcoin.htlc import HTLC
        >>> from swap.utils import sha256
        >>> htlc: HTLC = HTLC(network="testnet")
        >>> htlc.build_htlc(sha256("Hello Meheret!"), "mgS3WMHp9nvdUPeDJxr5iCF2P5HuFZSR3V", "n1wgm6kkzMcNfAtJmes8YhpvtDzdNhDY5a", 1624687630)
        >>> htlc.contract_address()
        "2NBYr6gvh4ujsRwKKjDrrRr2vGonazzX6Z6"
        """

        if self._contract_address:
            return self._contract_address
        if self._script is None:
            raise ValueError("HTLC script is None, first build HTLC.")
        return str(
            P2shScript(self._script.p2sh_hash()).address(
                mainnet=(True if self._network == "mainnet" else False)))
Ejemplo n.º 19
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
Ejemplo n.º 20
0
def script_from_address(address, network="testnet"):
    """
    Get script from address.

    :param address: Bitcoin address.
    :type address: str
    :param network: Bitcoin network, defaults to testnet.
    :type network: str
    :returns: P2pkhScript, P2shScript -- Bitcoin p2pkh or p2sh script instance.

    >>> from shuttle.providers.bitcoin.utils import script_from_address
    >>> script_from_address("mrmtGq2HMmqAogSsGDjCtXUpxrb7rHThFH", "testnet")
    P2pkhScript('7b7c4431a43b612a72f8229935c469f1f6903658')
    """

    if not is_address(address, network):
        raise AddressError("invalid %s %s address!" % (network, address))
    load_address = Address.from_string(address)
    get_type = load_address.get_type()
    if str(get_type) == "p2pkh":
        return P2pkhScript(load_address)
    elif str(get_type) == "p2sh":
        return P2shScript(load_address)
Ejemplo n.º 21
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
Ejemplo n.º 22
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
Ejemplo n.º 23
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
Ejemplo n.º 24
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
Ejemplo n.º 25
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
Ejemplo n.º 26
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
Ejemplo n.º 27
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