karask / python-bitcoin-utils

Library to interact with the Bitcoin network. Ideal for low-level learning and experimenting.
MIT License
262 stars 99 forks source link

Creating PSBT multiple inputs from diferent wallets #67

Closed polespinasa closed 3 months ago

polespinasa commented 3 months ago

Hi, I am trying to create a PSBT transaction with 2 inputs from different wallets and an output to export the tx in raw and sign it from an external wallet (Sparrow in this case). Everything is segwitt v0 (no taproot).

My code is something like this:


    hdw_from_mnemonic_user1 = HDWallet(mnemonic=mnemonic)
    hdw_from_mnemonic_user2 = HDWallet(mnemonic=mnemonic)
    hdw_from_mnemonic_user1.from_path(derivation_user1)
    hdw_from_mnemonic_user2.from_path(derivation_user2)

    private_user1 = hdw_from_mnemonic_user1.get_private_key()
    private_user2 = hdw_from_mnemonic_user2.get_private_key()

    public_user1 = private_user1.get_public_key()
    public_user2 = private_user2.get_public_key()

    address_user1 = public_user1.get_segwit_address()
    address_user2 = public_user2.get_segwit_address()

    # inputs

    tx_input1 = TxInput(
        "something", 2
    )

    tx_input2 = TxInput(
        "something2", 1
    )

    script_code_user1 = Script(
        ["OP_DUP", "OP_HASH160", public_user1.to_hash160(), "OP_EQUALVERIFY", "OP_CHECKSIG"]
    )

    script_code_user2 = Script(
        ["OP_DUP", "OP_HASH160", public_user2.to_hash160(), "OP_EQUALVERIFY", "OP_CHECKSIG"]
    )

    user1_funds = 0.001
    user2_funds = 0.001

    amount1 = to_satoshis(user1_funds)
    amount2 = to_satoshis(user2_funds)

    amounts = [amount1, amount2]

    script_pubkey1 = address_user1.to_script_pub_key()
    script_pubkey2 = address_user2.to_script_pub_key()
    utxos_script_pubkey = [script_pubkey1, script_pubkey2]

    # output

    destination_addr = P2wpkhAddress("destinationAddress")
    txout = TxOutput(to_satoshis(0.001995), destination_addr.to_script_pub_key())

    # final tx

    tx = Transaction([tx_input1, tx_input2], [txout], has_segwit=True)

    # TESTS DONE TO SEE IF WITNESS DATA IS WRONG
    #tx = Transaction([tx_input1], [txout], has_segwit=True)
    #tx = Transaction([tx_input1, tx_input2], [txout])
    #tx = Transaction([tx_input1], [txout], has_segwit=True)
    #tx.witnesses.append(TxWitnessInput(["", ""]))
    #tx.witnesses.append(TxWitnessInput(["", ""]))
    #tx.witnesses.append(TxWitnessInput(["", public_user1.to_hex()]))
    #tx.witnesses.append(TxWitnessInput([public_user1.to_hex()]))
    #tx.witnesses.append(TxWitnessInput([public_user2.to_hex()]))

    # final tx with signature
    sig_user1 = private_user1.sign_input(tx, 0, script_code_user1)
    sig_user2 = private_user2.sign_input(tx, 1, script_code_user2)

    tx.witnesses.append(TxWitnessInput([sig_user1, public_user1.to_hex()]))
    tx.witnesses.append(TxWitnessInput([sig_user2, public_user2.to_hex()]))

    print(tx.serialize())

This gives me back the signed tx, as it should be, but I want the PSBT without the signature.

If I use .serialize() before adding the segwit data to sign it, Sparrow doesn't let me sign it. If I export using has_segwit=True but without segwit data, then Sparrow does not let me to import the tx. And if I set has_segwit=False then the tx is detected as non segwit. I've also tried setting has_segwit=True with different data on the witness but it's not working.

Is there any previous step I should do to process the tx? I have not been able to find any example of how to create a psbt.

karask commented 3 months ago

Hi @polespinasa ,

Unfortunately, PSBT is not implemented yet.

polespinasa commented 3 months ago

Good to know, thanks for the answer!