1

I have 0.00036134 testnet bitcoin at the segwit v1 address: tb1p3au39skpdd8suuhunr4ymp6ca6akv9keq20v5twuagpmyqntqaasl22f4w

I wrote this script to achieve 2 things: archive a hash in a transaction and send the money in tb1p3au39skpdd8suuhunr4ymp6ca6akv9keq20v5twuagpmyqntqaasl22f4w to a new address.

import random
from io import BytesIO

from util import CTxInWitness, CTxOut, create_spending_transaction_nonode
from helpers.address import program_to_witness_testnet
from helpers.key import ECKey, generate_key_pair, generate_bip340_key_pair, generate_schnorr_nonce, int_or_bytes
from helpers.segwit_addr2 import Encoding, decode, encode, segwit_scriptpubkey, bech32_decode
from helpers.script import CScript, CTransaction, OP_RETURN, SIGHASH_ALL, SIGHASH_ALL_TAPROOT, TaprootSignatureHash, tagged_hash


# Sending wallet creation
sendPrivKeyParsed1 = ECKey().set(int('SENDING PRIVATE KEY IN INTEGER'))
sendPubKeyObj = sendPrivKeyParsed1.get_pubkey()
recPubKeyBytes = sendPubKeyObj.get_bytes()

if sendPubKeyObj.get_y()%2 != 0:
    sendPrivKeyParsed1.negate()
    sendPubKeyObj.negate()

# Receiving wallet creation
recPrivKeyParsed1 = ECKey().set(int('RECEIVING ADDRESS PRIVATE KEY IN INTEGER'))
recPubKeyObj = recPrivKeyParsed1.get_pubkey()
recPubKeyBytes = recPubKeyObj.get_bytes()

if recPubKeyObj.get_y()%2 != 0:
    recPrivKeyParsed1.negate()
    recPubKeyObj.negate()

# Example tweak
taptweak = bytes.fromhex('3e75c57bb5493f932d418f8ff604f96a5d0565a056c0f0f6a9c1c07f25e2a403')

# Tweak the private key
# Method: ECKey.add()
tweaked_privkey = recPrivKeyParsed1.add(taptweak)

# Tweak the public key
# Method: use tweak_add()
taproot_pubkey = recPubKeyObj.tweak_add(taptweak)
taproot_pubkey_b = taproot_pubkey.get_bytes()

# Derive the bech32 address
# Use program_to_witness(version_int, pubkey_bytes)
address = program_to_witness_testnet(0x01, taproot_pubkey_b)

hrp = "bc"
witver, witprog = decode(hrp, address)
if witver is None:
    hrp = "tb"
    witver, witprog = decode(hrp, address)
scriptpubkey = segwit_scriptpubkey(witver, witprog)
print("the scriptPublicKey is: {}".format(scriptpubkey.hex()))

# Create a spending transaction
spending_tx = create_spending_transaction_nonode(txid='3f75f413db8ba76d7e26a9e93b9fe94990d58922cd0aaba09eac4b46b74437c2', scriptpubkey=scriptpubkey, amount_c=0.00032134)
print("Spending transaction:\n{}".format(spending_tx))

# Sign transaction with tweaked private key
# Method: TaprootSignatureHash(tx, output_list, hash_type=int, input_index=int, scriptpath=bool)
txout1 = CTxOut(nValue=36134, scriptPubKey=scriptpubkey)
sighash = TaprootSignatureHash(spending_tx, [txout1], SIGHASH_ALL, 0, False)
sig = sendPrivKeyParsed1.sign_schnorr(sighash)

# Add witness to transaction
spending_tx.wit.vtxinwit.append(CTxInWitness([sig]))
print(spending_tx.serialize().hex())

print("Success!")

I got this when trying to broadcast the transaction:

sendrawtransaction RPC error: {"code":-26,"message":"non-mandatory-script-verify-flag (Invalid Schnorr signature)"}  

So why won't the network accept this transaction and how do I modify this transaction so it is accepted?

2
  • 3
    Hi @ChristianOConnor, your question is great and in-line with the scope of our site. Thank you for also answering it, after you found the solution. Please feel free to disregard the above comments, they are not in line with our community policies.—I’ll be removing them shortly.
    – Murch
    Commented May 5, 2023 at 10:10
  • Oh nice! Haha thank you! Commented May 5, 2023 at 10:18

1 Answer 1

1

Fixed it, the main problem is that:

# Sending wallet creation
sendPrivKeyParsed1 = ECKey().set(int('SENDING PRIVATE KEY IN INTEGER'))
sendPubKeyObj = sendPrivKeyParsed1.get_pubkey()
recPubKeyBytes = sendPubKeyObj.get_bytes()  

Was supposed to be this:

# Sending wallet creation
sendPrivKeyParsed1 = ECKey().set(int('SENDING PRIVATE KEY IN INTEGER'))
sendPubKeyObj = sendPrivKeyParsed1.get_pubkey()
sendPubKeyBytes = sendPubKeyObj.get_bytes()

The scriptPubKey needs to be calculated for both the sending and receiving addresses. In my original code, I did this to calculate the receiving address's scriptPubKey:

hrp = "bc"
witver, witprog = decode(hrp, address)
if witver is None:
    hrp = "tb"
    witver, witprog = decode(hrp, address)
scriptpubkey = segwit_scriptpubkey(witver, witprog)
print("the scriptPublicKey is: {}".format(scriptpubkey.hex()))  

So instead of scriptpubkey, you have scriptpubkeyRec and scriptpubkeySend. Maybe put the above code into a function that returns scriptpubkey and run it once for the sending address and once for the receiving address. If you write such a function called get_scriptPubKey(), the scriptPubKey generation would look like this:

# Derive the bech32 address
# Use program_to_witness(version_int, pubkey_bytes)
addressSend = program_to_witness_testnet(0x01, sendPubKeyBytes)
addressRec = program_to_witness_testnet(0x01, taproot_pubkey_b)

scriptpubkeySend = get_scriptPubKey1(addressSend)
scriptpubkeyRec = get_scriptPubKey1(addressRec)

Use scriptpubkeyRec here:

# Create a spending transaction
spending_tx = create_spending_transaction_nonode(txid='3f75f413db8ba76d7e26a9e93b9fe94990d58922cd0aaba09eac4b46b74437c2', scriptpubkey=scriptpubkeyRec, amount_c=0.00032134)  

Use scriptpubkeySend here:

txout1 = CTxOut(nValue=36134, scriptPubKey=scriptpubkeySend)  

The last problem is that you need to sign with the sending key like this:

sig = sendPrivKeyParsed1.sign_schnorr(sighash)  

After I made these changes the transaction worked https://blockstream.info/testnet/tx/55d8281e28240aa84f23026f335128d0c4e0c8868617cb65bc92a1ddc9c1f322

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.