How can I store a file hash in a Schnorr signature? Or something other than an OP_RETURN?

Expert

I have this python script that puts file hashes on the bitcoin blockchain:

from bitcoinutils.utils import to_satoshis
from bitcoinutils.setup import setup
from bitcoinutils.transactions import Transaction, TxInput, TxOutput
from bitcoinutils.script import Script
from bitcoinutils.keys import PrivateKey as utilPrivKey
from bitcoinutils.constants import SIGHASH_ALL

setup('testnet')

# private key for tb1qpeervdxrt2np74xhm4q4jjp76ey5f9fh9tf58f
sender_priv_key = utilPrivKey.from_wif('PRIVATE KEY FOR THE SENDING ADDRESS')
sender_pub_key = sender_priv_key.get_public_key().to_hex()
addrs_for_script = sender_priv_key.get_public_key().get_address()

# private key for tb1qj57tu40e9da959w4lj9s3njwteq2z89uy7kenh
receiver_priv_key = utilPrivKey.from_wif('PRIVATE KEY FOR THE RECEIVING ADDRESS')
receiver_addr = receiver_priv_key.get_public_key().get_segwit_address()

txin1 = TxInput("a86f0187be2eca4590a456e2423a2f5286b269c3b777846092ceb2fc44270aad", 1)
txin2 = TxInput("9019818039f181ed00046d682411719050a4b2dc1d1eb6aaa2d781df863522fc", 0)

script_code = Script(['OP_DUP', 'OP_HASH160', addrs_for_script.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'])
msg1 = "69eb032167c25784108b9783c9d59ce23608d00238eb9e3c03c73cdd79f74528"
op_return_script = Script(["OP_RETURN", msg1])

txoutOpR = TxOutput(0, op_return_script)

# 0.00005 left for transaction fee
txout = TxOutput(to_satoshis(0.00036275), receiver_addr.to_script_pub_key())

tx = Transaction([txin1, txin2], [txoutOpR, txout], has_segwit=True)
txsign1 = sender_priv_key.sign_segwit_input(tx, 0, script_code, to_satoshis(0.00034416), SIGHASH_ALL)
tx.witnesses = [Script([txsign1, sender_pub_key])]
txsign2 = sender_priv_key.sign_segwit_input(tx, 1, script_code, to_satoshis(0.00006859), SIGHASH_ALL)
tx.witnesses.append(Script([txsign2, sender_pub_key]))

signed_tx = tx.serialize()
print("raw tx below this line")
print(signed_tx)
print("raw tx above this line")  

The above python script works, but I am trying to move away from OP_RETURN. Some Bitcoin devs have voiced their opinion against OP_RETURN so I'm afraid it will get deprecated.

Is there any way to modify my python script to store the hash in a Schnorr signature instead of an OP_RETURN? Or some other future proof way to store a file hash in a Bitcoin transaction?

Answers 1

While it's true that some Bitcoin devs discourage the use of OP_RETURN, it's important to note that it's still the most widely used and accepted method for embedding data into the Bitcoin blockchain. However, if you're looking for an alternative, you can consider using the "null data" output (non-standard output).

One way to achieve this is by encoding your hash as a P2WSH (Pay-to-Witness-Script-Hash) output. This method doesn't require you to use a Schnorr signature, and it's a more future-proof way to store the hash in a Bitcoin transaction.

Here's a modified version of your script that encodes the hash as a P2WSH output:

from bitcoinutils.setup import setup
from bitcoinutils.transactions import Transaction, TxInput, TxOutput
from bitcoinutils.keys import PrivateKey as utilPrivKey
from bitcoinutils.constants import SIGHASH_ALL
from bitcoinutils.script import Script

setup('testnet')

sender_priv_key = utilPrivKey.from_wif('PRIVATE KEY FOR THE SENDING ADDRESS')
sender_pub_key = sender_priv_key.get_public_key().to_hex()
addrs_for_script = sender_priv_key.get_public_key().get_address()

receiver_priv_key = utilPrivKey.from_wif('PRIVATE KEY FOR THE RECEIVING ADDRESS')
receiver_addr = receiver_priv_key.get_public_key().get_segwit_address()

txin1 = TxInput("a86f0187be2eca4590a456e2423a2f5286b269c3b777846092ceb2fc44270aad", 1)
txin2 = TxInput("9019818039f181ed00046d682411719050a4b2dc1d1eb6aaa2d781df863522fc", 0)

script_code = Script(['OP_DUP', 'OP_HASH160', addrs_for_script.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG'])
msg1 = "69eb032167c25784108b9783c9d59ce23608d00238eb9e3c03c73cdd79f74528"

# Create a P2WSH script containing the hash as the redeem script
p2wsh_script = Script([msg1, "OP_DROP", "OP_SHA256", msg1, "OP_EQUAL"])

# Use the P2WSH script as the output script
txoutData = TxOutput(0, p2wsh_script)

txout = TxOutput(to_satoshis(0.00036275), receiver_addr.to_script_pub_key())

tx = Transaction([txin1, txin2], [txoutData, txout], has_segwit=True)
txsign1 = sender_priv_key.sign_segwit_input(tx, 0, script_code, to_satoshis(0.00034416), SIGHASH_ALL)
tx.witnesses = [Script([txsign1, sender_pub_key])]
txsign2 = sender_priv_key.sign_segwit_input(tx, 1, script_code, to_satoshis(0.00006859), SIGHASH_ALL)
tx.witnesses.append(Script([txsign2, sender_pub_key]))

signed_tx = tx.serialize()
print("raw tx below this line")
print(signed_tx)
print("raw tx above this line")

This script creates a P2WSH output that encodes the hash as the redeem script. Keep in mind that this method may not be as efficient as using OP_RETURN since it requires more bytes to encode the hash. Nevertheless, it's a valid alternative if you want to avoid OP_RETURN.