<p>You can make life easy by using one of our OP_RETURN libraries:</p>

<ul>
<li>PHP: <a href="https://github.com/coinspark/php-OP_RETURN" rel="nofollow">https://github.com/coinspark/php-OP_RETURN</a></li>
<li>Python: <a href="https://github.com/coinspark/python-OP_RETURN" rel="nofollow">https://github.com/coinspark/python-OP_RETURN</a></li>
</ul>

<p>The code will also show you exactly how it's done.</p>

CC BY-SA 3.0
2015-07-16T07:18:55.567Z
2015-07-16T07:18:55.567Z
Gideon Greenspan
<p><a href="http://digitalcommons.augustana.edu/cscfaculty/1/" rel="nofollow noreferrer">http://digitalcommons.augustana.edu/cscfaculty/1/</a></p>

<p>Academic paper describing the different methods of data insertion for Bitcoin's blockchain.</p>

CC BY-SA 3.0
2017-07-22T19:40:36.037Z
2017-07-22T19:40:36.037Z
Math
<p>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 <a href="https://github.com/nickodell/rawtransactiondemo" rel="noreferrer">here</a>. It's been tested, but only on testnet. I'm going to go through the code and explain what it's doing.</p>
<h3>Start</h3>
<pre><code>[...]
logging.basicConfig()
logging.getLogger(&quot;BitcoinRPC&quot;).setLevel(logging.DEBUG)
</code></pre>
<p>This makes logging more verbose. It's handy because it shows what RPC calls are being made.</p>
<h3>Connect to bitcoind</h3>
<pre><code>rpc_user = &quot;bitcoinrpc&quot;
rpc_password = &quot;87Y9A2gs25E9HDPGc9axqSqzxMR2MyTtrMkYc5KiZk2Z&quot;

rpc = AuthServiceProxy(&quot;http://%s:%s@127.0.0.1:18332/&quot; % (rpc_user, rpc_password))
</code></pre>
<p>Note that your password will be different, and that you use port 8332 for mainnet instead of port 18332.</p>
<h3>List unspent outputs</h3>
<pre><code>first_unspent = rpc.listunspent()[0]
txid = first_unspent['txid']
vout = first_unspent['vout']
input_amount = first_unspent['amount']
SATOSHI = Decimal(&quot;0.00000001&quot;)
change_amount = input_amount - Decimal(&quot;0.005&quot;) - SATOSHI
</code></pre>
<p>The <code>- Decimal(&quot;0.005&quot;)</code> part is so that we pay a transaction fee.</p>
<h3>Create transaction</h3>
<pre><code># Marker address we're going to replace
# Produces a pattern that's easy to search for
mainnet = 0
if mainnet:
    dummy_address = &quot;1111111111111111111114oLvT2&quot;
else:
    dummy_address = &quot;mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8&quot;
</code></pre>
<p>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.</p>
<pre><code># My change address
change_address = &quot;mhZuYnuMCZLjZKeDMnY48xsR5qkjq7bAr9&quot;
</code></pre>
<p>Remember, this is my change address. If you don't change it, you'll be sending money to me.</p>
<pre><code>tx = rpc.createrawtransaction([{&quot;txid&quot;: txid, &quot;vout&quot;: vout}], \
                              {change_address: change_amount, \
                               dummy_address: SATOSHI})
</code></pre>
<p>And now we have an actual transaction. It doesn't contain any of our own data though, so we'll have to fix that.</p>
<h3>Replace dummy output with our own output</h3>
<pre><code># 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 = &quot;1976a914000000000000000000000000000000000000000088ac&quot;
</code></pre>
<p>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.</p>
<pre><code># Data to insert
data = &quot;Melons.&quot;
if len(data) &gt; 75:
    raise Exception(&quot;Can't contain this much data-use OP_PUSHDATA1&quot;)

newScriptPubKey = &quot;6a&quot; + hexlify(chr(len(data))) + hexlify(data)
</code></pre>
<p>Next we create the data we want to put into the blockchain. I'm using the string <code>Melons.</code>, but you could use anything. (Above 40 bytes is nonstandard, though.) I covered most of this in my <a href="https://bitcoin.stackexchange.com/a/32053">other answer</a>.</p>
<p>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.</p>
<pre><code>#Append int of length to start
newScriptPubKey = hexlify(chr(len(unhexlify(newScriptPubKey)))) + newScriptPubKey
</code></pre>
<p>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.</p>
<pre><code>if oldScriptPubKey not in tx:
    raise Exception(&quot;Something broke!&quot;)
</code></pre>
<p>Error checking for this very rickety method.</p>
<pre><code>tx = tx.replace(oldScriptPubKey, newScriptPubKey)
</code></pre>
<p>Finally, a string replace swaps the new script for the old one.</p>
<h3>Sign it</h3>
<pre><code>tx = rpc.signrawtransaction(tx)['hex']
</code></pre>
<p>Bitcoin handles the heaving lifting here.</p>
<h3>Broadcast it to the network.</h3>
<pre><code>rpc.sendrawtransaction(tx)
</code></pre>
<p>Done! Now just wait for your transaction to get into a block.</p>
<p>I ran the code (<a href="http://pastebin.com/S4KWwPSN" rel="noreferrer">output</a>), and I produced a transaction, which you can see in Block Explorer <a href="http://blockexplorer.com/testnet/tx/83c93f0f108af06e67e339a741b8c4dce3cc51eacb5c181c2a7e52cb866a4941" rel="noreferrer">here</a>. If you copy the string next to OP_RETURN, and paste it into a <a href="http://www.rapidtables.com/convert/number/hex-to-ascii.htm" rel="noreferrer">Hex to ASCII converter</a>, you get...</p>
<pre><code>Melons.
</code></pre>
<p>Done!</p>
<h3>Other resources</h3>
<p>I found <a href="https://en.bitcoin.it/wiki/Raw_Transactions" rel="noreferrer">this webpage</a> helpful while writing this.</p>

CC BY-SA 3.0
2015-03-13T13:28:42.637Z
Nice, as it's an OP_RETURN scriptsig (If I understands correctly), does that mean the tx will be removed / invalidated by the network?
Valmond
CC BY-SA 3.0
2015-03-13T14:24:24.350Z
@Valmond `scriptsig` scriptPubKey, actually. `does that mean the tx will be removed / invalidated by the network?` Not... really. It will be removed from the unspent transaction output set in some clients, but many clients will still remember it. (They need to, because they need to prove to new clients that it's just an OP_RETURN transaction.)
Nick ODell
CC BY-SA 3.0
2015-03-17T22:31:16.747Z
Note, the transaction you produced would not be a standard transaction on mainnet because the op_return output has a value > 0.  It gets relayed/mined-by-default on testnet because testnet accepts almost all transactions as standard.
David A. Harding
CC BY-SA 3.0
2015-03-18T01:17:46.600Z
@DavidA.Harding I did that because createrawtransaction wouldn't accept a value of 0. Can you provide a source to that claim? I can't find any check like that in Solver or IsStandard.
Nick ODell
CC BY-SA 3.0
2015-03-18T12:17:56.567Z
@NickODell oh, bugger---you seem to be correct; I've been giving people bad advice for a year now.  Sorry.
David A. Harding
CC BY-SA 3.0
2018-03-29T10:35:51.583Z
I got a few questions.
1. How did you calculate the dummy addresses with such precision?
2. If I enter a real address as dummy (`getnewaddress`) and then `txhex = createrawtransaction`. Then I get the script hex by `decodedtx = decoderawtransaction txhex` and `oldScriptHex = decodedtx.scriptPubKey.hex`. Then `txhex.replace(oldScriptHex, newScriptHex)` and this approach doesn't decode anymore (code -22; TX decode failed). why? `newScriptHex` is identical in your version and mine.
krivar
CC BY-SA 3.0
2018-03-29T12:29:05.323Z
Currently you can use `createrawtransaction` with a `vout` like `{"data": "your hex data"}` and don't have to deal with the replacing.
krivar
CC BY-SA 3.0
2015-03-13T00:07:53.260Z
2015-03-13T00:13:12.083Z
2020-06-16T11:10:03.350Z
Nick ODell
<p>I was following <a href="https://bitcointalk.org/index.php?topic=453086.0" rel="noreferrer">https://bitcointalk.org/index.php?topic=453086.0</a>
for creating a message in OP_RETURN. I am able to decode my raw transaction, but on signrawtransaction it gives </p>

<blockquote>
  <p>error: {"code":-22,"message":"TX decode failed"}</p>
</blockquote>

<p>I validated the address, is there something that is missing in that tutorial.</p>

CC BY-SA 3.0
2014-05-09T18:09:02.653Z
Best way would be to post the resulting tx. Is the data you're embedding less than 40 bytes?
Matthieu
CC BY-SA 3.0
2014-05-10T04:31:56.220Z
Thanks Matthieu, you were right about the size. But I still had to do more steps. So was looking for a complete Step by Step.
wizgot
CC BY-SA 3.0
2015-05-22T11:28:07.693Z
I got this message when I'd forgotten to replace the original script length with the new one for my OP_RETURN hex string.
Echelon
<transactions><blockchain><bitcoind><signature><op-return>
What is a step by step way to insert data in OP_RETURN?
wizgot
# What is a step by step way to insert data in OP_RETURN?

(Asked by: username_0 on 2014-05-09)

I was following <https://bitcointalk.org/index.php?topic=453086.0>
for creating a message in OP\_RETURN. I am able to decode my raw transaction, but on signrawtransaction it gives

> error: {"code":-22,"message":"TX decode failed"}

I validated the address, is there something that is missing in that tutorial.

**Tags:** transactions, blockchain, bitcoind, signature, op-return

> **Comments:**
> **username_1**: Best way would be to post the resulting tx. Is the data you're embedding less than 40 bytes?  
> **username_0**: Thanks username_1, you were right about the size. But I still had to do more steps. So was looking for a complete Step by Step.  
> **username_2**: I got this message when I'd forgotten to replace the original script length with the new one for my OP_RETURN hex string.  

---

## 3 answers

### Answer by username_5 on 2015-03-13. Score: 18 | Accepted answer

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](https://github.com/username_5/rawtransactiondemo). 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:%s@127.0.0.1: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](https://bitcoin.stackexchange.com/a/32053).

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](http://pastebin.com/S4KWwPSN)), and I produced a transaction, which you can see in Block Explorer [here](http://blockexplorer.com/testnet/tx/83c93f0f108af06e67e339a741b8c4dce3cc51eacb5c181c2a7e52cb866a4941). If you copy the string next to OP\_RETURN, and paste it into a [Hex to ASCII converter](http://www.rapidtables.com/convert/number/hex-to-ascii.htm), you get...

```
Melons.

```

Done!

### Other resources

I found [this webpage](https://en.bitcoin.it/wiki/Raw_Transactions) helpful while writing this.

> **Comments:**
> **username_6**: Nice, as it's an OP_RETURN scriptsig (If I understands correctly), does that mean the tx will be removed / invalidated by the network?  
> **username_5**: @username_6 `scriptsig` scriptPubKey, actually. `does that mean the tx will be removed / invalidated by the network?` Not... really. It will be removed from the unspent transaction output set in some clients, but many clients will still remember it. (They need to, because they need to prove to new clients that it's just an OP_RETURN transaction.)  
> **username_7**: Note, the transaction you produced would not be a standard transaction on mainnet because the op_return output has a value > 0.  It gets relayed/mined-by-default on testnet because testnet accepts almost all transactions as standard.  
> **username_5**: @username_7 I did that because createrawtransaction wouldn't accept a value of 0. Can you provide a source to that claim? I can't find any check like that in Solver or IsStandard.  
> **username_7**: @username_5 oh, bugger---you seem to be correct; I've been giving people bad advice for a year now.  Sorry.  
> **username_8**: I got a few questions. 1. How did you calculate the dummy addresses with such precision? 2. If I enter a real address as dummy (`getnewaddress`) and then `txhex = createrawtransaction`. Then I get the script hex by `decodedtx = decoderawtransaction txhex` and `oldScriptHex = decodedtx.scriptPubKey.hex`. Then `txhex.replace(oldScriptHex, newScriptHex)` and this approach doesn't decode anymore (code -22; TX decode failed). why? `newScriptHex` is identical in your version and mine.  
> **username_8**: Currently you can use `createrawtransaction` with a `vout` like `{"data": "your hex data"}` and don't have to deal with the replacing.  

---

### Answer by username_3 on 2015-07-16. Score: 3

You can make life easy by using one of our OP\_RETURN libraries:

* PHP: <https://github.com/coinspark/php-OP_RETURN>
* Python: <https://github.com/coinspark/python-OP_RETURN>

The code will also show you exactly how it's done.


---

### Answer by username_4 on 2017-07-22. Score: 1

<http://digitalcommons.augustana.edu/cscfaculty/1/>

Academic paper describing the different methods of data insertion for Bitcoin's blockchain.


---

