2

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?

1 Answer 1

2

It is usualy discouraged to embed data directly on the blockchain because it is not supposed to be used like that and by doing so you can congest the network and make the blockchain grow in size for no real reason, but if you still want to do it you should be using a P2WSH ( Pay-to-Witness-Script-Hash ) you could do it with taproot too but we will be using P2WSH here. with this transaction you would create a transaction output that can only be spent by providing a valid witness script that satisfies the spending conditions and since the condition can be arbitrary, such as requiring a specific signature it can do what you want and the witness script will also contain the file hash you want to store.

For your script we will modify it by removing the OP_RETURN and using a P2WSH instead:

edit: there was a misunderstanding in your code you try to call get_address() on a Script object, lets use to_p2wsh_script_pub_key directly on witness_script then we convert it to hash160

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 the sender address
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 the receiver 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"
witness_script = Script(['OP_IF', 'OP_SHA256', msg1, 'OP_EQUAL', 'OP_ELSE', 'OP_DUP', 'OP_HASH160', addrs_for_script.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG', 'OP_ENDIF'])
p2wsh_script_pub_key = witness_script.to_p2wsh_script_pub_key()
witness_script_hash = p2wsh_script_pub_key.to_hash160()

txoutP2WSH = TxOutput(0, Script(['OP_0', witness_script_hash]))

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

tx = Transaction([txin1, txin2], [txoutP2WSH, 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("Signed raw transaction:", signed_tx)
4
  • I get AttributeError: 'Script' object has no attribute 'get_address' on this line: witness_script_hash = witness_script.to_p2wsh_script_pub_key().get_address().to_hash160() Commented Apr 21, 2023 at 20:13
  • 1
    I modified my answer
    – Saxtheowl
    Commented Apr 21, 2023 at 20:34
  • Getting closer... I got AttributeError: 'Script' object has no attribute 'to_hash160' on the line: witness_script_hash = p2wsh_script_pub_key.to_hash160() Commented Apr 21, 2023 at 21:52
  • I am kinda lost here, not used to this library
    – Saxtheowl
    Commented Apr 22, 2023 at 6:14

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.