karask / python-bitcoin-utils

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

Sending P2sh is encountered with an mandatory-script-verify-flag-failed (Signature is found in scriptCode) error #44

Closed mrtnetwork closed 1 year ago

mrtnetwork commented 1 year ago

Hi Code

def import_w(index: int = 0) -> HDWallet:
    hdwallet: HDWallet = HDWallet(symbol=BTCTEST).from_mnemonic(
        "olympic tip enforce tool orchard major spy defense wink maid company book")
    # Get Bitcoin HDWallet from entropy
    hdwallet.clean_derivation()
    hdwallet.from_index(44, hardened=True)
    hdwallet.from_index(0, hardened=True)
    hdwallet.from_index(0, hardened=True)
    hdwallet.from_index(0)
    hdwallet.from_index(index)
    return hdwallet

def main():
    # always remember to setup the network
    setup('testnet')
    w = import_w(1)
    r = import_w(0)
    #
    sender = PrivateKey(wif=w.wif())
    receiver = PrivateKey(wif=r.wif())

    txin = TxInput("9aab6944ad5d771cd780f1f4c8bb714cbf76137bbeed0253406d6a640f04bbd5", 1)

    rec_public = receiver.get_public_key().to_hex()
    redeem_script = Script([rec_public, 'OP_CHECKSIG'])
    txout = TxOutput(2076777, redeem_script.to_p2sh_script_pub_key())

    tx = Transaction([txin], [txout])
    sender_address = P2pkhAddress(sender.get_public_key().get_address().to_string())
    sig = sender.sign_input(tx, 0, sender_address.to_script_pub_key())
    # print(sig)

    # get public key as hex
    pk = sender.get_public_key()
    pk = pk.to_hex()
    # print (pk)

    # set the scriptSig (unlocking script)
    txin.script_sig = Script([sig, pk])
    signed_tx = tx.serialize()
    print(signed_tx)

Why does the script below give me a different address? Shouldn't it be the same?

    setup('testnet')
    w1 = import_w(0) # From the previous code
    print(w1.p2sh_address())  # 2MxnaH4QGVz34aVHQDtYbqRVJapAuk6BTBW
    pk = PrivateKey.from_wif(wif=w1.wif())
    ps = pk.get_public_key().get_address().to_script_pub_key()
    p2sh = P2shAddress(script=ps)  # 2MxnaH4QGVz34aVHQDtYbqRVJapAuk6BTBW  // same

    rec_public = pk.get_public_key().to_hex()
    redeem_script = Script([rec_public, 'OP_CHECKSIG'])
    # redeem_script.to_p2sh_script_pub_key()

    address = keys.P2shAddress.from_script(redeem_script)
    print('address: ', address.to_string())  # 2N7uZgvLwjTBteicco18NSx91g2XGiQAixZ << different address)

In the transaction, I wanted to send it to the 2MxnaH4QGVz34aVHQDtYbqRVJapAuk6BTBWbut in the last one it was sent to this 2N7uZgvLwjTBteicco18NSx91g2XGiQAixZ

Thank you, sorry, I'm a newbie

mrtnetwork commented 1 year ago

the first question was solved :D my UTXO is p2pk and we must use the public key in the transaction script (not hash) to sign a transaction and unlocking script only needs a signed hex Thanks

karask commented 1 year ago

Address 2MxnaH4QGVz34aVHQDtYbqRVJapAuk6BTBW is for a P2PKH wrapped in P2SH -> P2SH(P2PKH) i.e. that is what .to_script_pub_key() returns!

Address 2MxnaH4QGVz34aVHQDtYbqRVJapAuk6BTBW is for a P2PK wrapped in P2SH -> P2SH(P2PK)

Note that both P2PKH and P2PK are standard output types and thus can (and in most cases should) be used directly (not wrapped in P2SH, which will complicate spending). I typically use P2PK for demonstrating P2SH only because it is a small and easy to understand script -- not because I needed to.