I wrote a little demo program which puts a snippet of data into an OP_RETURN script. It requires a bitcoin instance that accepts RPC connections, though it could be implemented without that. You can find it on github here. It's been tested, but only on testnet. I'm going to go through the code and explain what it's doing.
Start
[...]
logging.basicConfig()
logging.getLogger("BitcoinRPC").setLevel(logging.DEBUG)
This makes logging more verbose. It's handy because it shows what RPC calls are being made.
Connect to bitcoind
rpc_user = "bitcoinrpc"
rpc_password = "87Y9A2gs25E9HDPGc9axqSqzxMR2MyTtrMkYc5KiZk2Z"
rpc = AuthServiceProxy("http://%s:%[email protected]:18332/" % (rpc_user, rpc_password))
Note that your password will be different, and that you use port 8332 for mainnet instead of port 18332.
List unspent outputs
first_unspent = rpc.listunspent()[0]
txid = first_unspent['txid']
vout = first_unspent['vout']
input_amount = first_unspent['amount']
SATOSHI = Decimal("0.00000001")
change_amount = input_amount - Decimal("0.005") - SATOSHI
The - Decimal("0.005")
part is so that we pay a transaction fee.
Create transaction
# Marker address we're going to replace
# Produces a pattern that's easy to search for
mainnet = 0
if mainnet:
dummy_address = "1111111111111111111114oLvT2"
else:
dummy_address = "mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8"
These are two different encodings of a Pay to Public Key Hash of all zeros. The top one is the mainnet representation, and the bottom is the testnet representation.
# My change address
change_address = "mhZuYnuMCZLjZKeDMnY48xsR5qkjq7bAr9"
Remember, this is my change address. If you don't change it, you'll be sending money to me.
tx = rpc.createrawtransaction([{"txid": txid, "vout": vout}], \
{change_address: change_amount, \
dummy_address: SATOSHI})
And now we have an actual transaction. It doesn't contain any of our own data though, so we'll have to fix that.
Replace dummy output with our own output
# Pattern to replace
# Represents length of script, then OP_DUP OP_HASH160,
# then length of hash, then 20 bytes of zeros, OP_EQUALVERIFY OP_CHECKSIG
oldScriptPubKey = "1976a914000000000000000000000000000000000000000088ac"
This is a bit of a hack. Instead of creating our own output, we create a dummy output, then search for the pattern it makes and replace it. There's probably a better way to do this, but this seems easiest.
# Data to insert
data = "Melons."
if len(data) > 75:
raise Exception("Can't contain this much data-use OP_PUSHDATA1")
newScriptPubKey = "6a" + hexlify(chr(len(data))) + hexlify(data)
Next we create the data we want to put into the blockchain. I'm using the string Melons.
, but you could use anything. (Above 40 bytes is nonstandard, though.) I covered most of this in my other answer.
This code will break if the data is longer than 75 bytes. If you need it for more than that, you could use OP_PUSHDATA1 instead of the single-byte pushdata that I'm using here.
#Append int of length to start
newScriptPubKey = hexlify(chr(len(unhexlify(newScriptPubKey)))) + newScriptPubKey
This part is a little different than my other answer, because we need to include the length of the scriptPubKey also. This code will break with data longer than 251 bytes. If you want it to work on data longer than that, encode a varint correctly.
if oldScriptPubKey not in tx:
raise Exception("Something broke!")
Error checking for this very rickety method.
tx = tx.replace(oldScriptPubKey, newScriptPubKey)
Finally, a string replace swaps the new script for the old one.
Sign it
tx = rpc.signrawtransaction(tx)['hex']
Bitcoin handles the heaving lifting here.
Broadcast it to the network.
rpc.sendrawtransaction(tx)
Done! Now just wait for your transaction to get into a block.
I ran the code (output), and I produced a transaction, which you can see in Block Explorer here. If you copy the string next to OP_RETURN, and paste it into a Hex to ASCII converter, you get...
Melons.
Done!
Other resources
I found this webpage helpful while writing this.