<p>This is a bash command that can also give you the file:</p>
<pre><code>bitcoin-cli getrawtransaction 54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713 0 00000000000000ecbbff6bafb7efa2f7df05b227d5c73dca8f2635af32a2e949  | sed 's/0100000000000000/\n/g' | tail -n +2 | cut -c7-136,139-268,271-400 | tr -d '\n' | cut -c17-368600 | xxd -p -r &gt; bitcoin.pdf
</code></pre>
<p>It doesn't need <code>-txindex</code>, but it does need a full node, not pruned. I suppose it's possible to make a variant that works with <code>gettxout</code> that works with pruned nodes, but that's left as an exercise for the reader.</p>

CC BY-SA 4.0
2021-01-21T22:38:37.867Z
Exercise done by @jb55 :-) `seq 0 947 | (while read -r n; do bitcoin-cli gettxout 54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713 $n | jq -r '.scriptPubKey.asm' | awk '{ print $2 $3 $4 }'; done) | tr -d '\n' | cut -c 17-368600 | xxd -r -p > bitcoin.pdf` Source: https://bitcoinhackers.org/@jb55/105595146491662406
Antoine Poinsot
CC BY-SA 4.0
2021-01-21T22:31:03.760Z
2021-01-21T22:31:03.760Z
Steven Roose
<p><strong>The PDF uses Python uploader/downloader scripts present in the Bitcoin blockchain itself</strong></p>
<p>Those scripts are present at:</p>
<ul>
<li>uploader: tx 4b72a223007eab8a951d43edc171befeabc7b5dca4213770c88e09ba5b936e17 block 229991 reproduced at: <a href="https://gist.github.com/cirosantilli/ade4dde7c2f2f5020d792872681763e8" rel="nofollow noreferrer">https://gist.github.com/cirosantilli/ade4dde7c2f2f5020d792872681763e8</a></li>
<li>downloader: tx 6c53cd987119ef797d5adccd76241247988a0a5ef783572a9972e7371c5fb0cc block 229991 reproduced at <a href="https://gist.github.com/cirosantilli/e90bd2e6c3fab25a20898e61e3ab3e90" rel="nofollow noreferrer">https://gist.github.com/cirosantilli/e90bd2e6c3fab25a20898e61e3ab3e90</a></li>
</ul>
<p>These are my forks of Ken Shirriff's Gists who presumably found them while researching for: <a href="http://www.righto.com/2014/02/ascii-bernanke-wikileaks-photographs.html" rel="nofollow noreferrer">http://www.righto.com/2014/02/ascii-bernanke-wikileaks-photographs.html</a></p>
<p>Both scripts have the copyright header:</p>
<pre><code>#
# File downloader
# Requires git://github.com/jgarzik/python-bitcoinrpc.git
#
# (c) 2013 Satoshi Nakamoto All Rights Reserved
#
# UNAUTHORIZED DUPLICATION AND/OR USAGE OF THIS PROGRAM IS PROHIBITED BY US AND INTERNATIONAL COPYRIGHT LAW
</code></pre>
<p>which is likely a joke and not really written by Satoshi in my opninon.</p>
<p>Both uploader and downloader are uploaded with the uploader encoding, which creates a slight chicken-and-egg bootstrap issue to download the downloader. But the encoding is so simple that you could just download the raw downloader data and fix it up manually.</p>
<p>After downloading the downloader manually, I managed to run it to extract a byte-by-byte match of the PDF present at <a href="https://web.archive.org/web/20210418161957/https://bitcoin.org/bitcoin.pdf" rel="nofollow noreferrer">https://web.archive.org/web/20210418161957/https://bitcoin.org/bitcoin.pdf</a> (sha256 == b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553).</p>
<p>First you have to be running a Bitcoin core server locally. Suppose that your <code>.bitcon/bitoin.conf</code> contains:</p>
<pre><code>rpcuser=asdf
rpcpassword=qwer
server=1
txindex=1
</code></pre>
<p>Then I run:</p>
<pre><code>git clone git://github.com/jgarzik/python-bitcoinrpc.git
git -C python-bitcoinrpc checkout cdf43b41f982b4f811cd4ebfbc787ab2abf5c94a
wget https://gist.githubusercontent.com/shirriff/64f48fa09a61b56ffcf9/raw/ad1d2e041edc0fb7ef23402e64eeb92c045b5ef7/bitcoin-file-downloader.py
pip install python-bitcoinrpc==1.0
BTCRPCURL=http://asdf:qwer@127.0.0.1:8332 \
  PYTHONPATH=&quot;$(pwd)/python-bitcoinrpc:$PYTHONPATH&quot; \
  python3 bitcoin-file-downloader.py \
  54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713
</code></pre>
<p>where tx 54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713 (block 230009) is the transaction in which the whitepaper is encoded.</p>
<p><strong>Encoding used by the downloader script</strong></p>
<p>The downloader is not super strict about the subtleties of the uploader. We can immediately understand its format from the source:</p>
<pre><code>data = b''
for txout in tx['vout'][0:-2]:
    for op in txout['scriptPubKey']['asm'].split(' '):
        if not op.startswith('OP_') and len(op) &gt;= 40:
            data += unhexlify(op.encode('utf8'))

length = struct.unpack('&lt;L', data[0:4])[0]
checksum = struct.unpack('&lt;L', data[4:8])[0]
data = data[8:8+length]

if checksum != crc32(data):
    print('Checksum mismatch; expected %d but calculated %d' % (checksum, crc32(data)),
          file=sys.stderr)
    sys.exit()
</code></pre>
<p>so we see that it:</p>
<ul>
<li>ignores the two last outputs</li>
<li>ignores operands</li>
<li>ignores any integer constant with less than 20 bytes (40 hex bytes)</li>
</ul>
<p>Then, from the data above:</p>
<ul>
<li>the first 4 bytes are the payload length</li>
<li>the next 4 bytes are a crc32 of the rest of payload data</li>
<li>the payload starts at the 8th byte</li>
</ul>
<p><strong>Encoding used by the uploader script</strong></p>
<p>The uploader is a bit more specific. Perhaps it is easiest to understand it by looking at the disassembly of a small upload, e.g. the downloader itself: <a href="https://www.blockchain.com/btc/tx/6c53cd987119ef797d5adccd76241247988a0a5ef783572a9972e7371c5fb0cc" rel="nofollow noreferrer">https://www.blockchain.com/btc/tx/6c53cd987119ef797d5adccd76241247988a0a5ef783572a9972e7371c5fb0cc</a></p>
<p>First we have a bunch of outputs of type:</p>
<pre><code>OP_1 data OP_3 OP_CHECKMULTISIG
</code></pre>
<p>and worth a tiny 0.00000001 BTC which contain the bulk of the data. These cannot presumably be spent, due to the <code>OP_CHECKMULTISIG</code>, but this cannot be proven either.</p>
<p>Then at then end we have two different transactions, which as we saw above the downloader ignores:</p>
<ul>
<li><p>a standard P2PKH <code>OP_DUP OP_HASH160 &lt;hash&gt; OP_EQUALVERIFY OP_CHECKSIG</code> with minimal value 0.00000001 BTC and unspent. TODO why? This corresponds to the part of the code:</p>
<pre><code># dest output
out_value = Decimal(sys.argv[3])
change -= out_value
txouts.append((out_value, OP_DUP + OP_HASH160 + pushdata(addr2bytes(sys.argv[2])) + OP_EQUALVERIFY + OP_CHECKSIG))
</code></pre>
<p>which shows that you just pass an arbitrary target value and address from your script.</p>
</li>
<li><p>a standard spent P2PKH change transaction with a considerable value, to recover leftovers from the input. It is sent to a new address owned by you that is created on the fly via the RPC call:</p>
<pre><code>change_addr = proxy.getnewaddress()
txouts.append([change, OP_DUP + OP_HASH160 + pushdata(addr2bytes(change_addr)) + OP_EQUALVERIFY + OP_CHECKSIG])
</code></pre>
</li>
</ul>
<p>Reading the source basically confirms what we see on the disassembly. Some other pointers follow.</p>
<p>At:</p>
<pre><code>data = open(sys.argv[1],'rb').read()
data = struct.pack('&lt;L', len(data)) + struct.pack('&lt;L', crc32(data)) + data
fd = io.BytesIO(data)
</code></pre>
<p>we see the:</p>
<ul>
<li>payload length <code>struct.pack('&lt;L', len(data)) </code></li>
<li>crc32</li>
</ul>
<p>being added to the start of the file.</p>
<p>At:</p>
<pre><code>def checkmultisig_scriptPubKey_dump(fd):
    data = fd.read(65*3)
    if not data:
        return None

    r = pushint(1)

    n = 0
    while data:
        chunk = data[0:65]
        data = data[65:]

        if len(chunk) &lt; 33:
            chunk += b'\x00'*(33-len(chunk))
        elif len(chunk) &lt; 65:
            chunk += b'\x00'*(65-len(chunk))

        r += pushdata(chunk)
        n += 1

    r += pushint(n) + OP_CHECKMULTISIG
    return r
</code></pre>
<p>we see the main data encoding loop. This clarifies from the disassembly:</p>
<pre><code>OP_1 data OP_3 OP_CHECKMULTISIG
</code></pre>
<ul>
<li><p>the <code>OP_1</code> is fixed at <code>pushint(1)</code></p>
</li>
<li><p>the <code>OP_3</code> can be less than 3, it encodes how many 65 byte chunks were encoded, with a maximum of 3 chunks present on each output due to <code>fd.read(65*3)</code></p>
<p>The last payload transaction can therefore have <code>OP_1</code>, <code>OP_2</code> or <code>OP_3</code> depending on granularity.</p>
</li>
</ul>
<p><strong>What else was the uploader script used for and was it popular?</strong></p>
<p>At:</p>
<ul>
<li><a href="https://github.com/cirosantilli/bitcoin-strings-with-txids/blob/master/data/satoshi_uploader" rel="nofollow noreferrer">https://github.com/cirosantilli/bitcoin-strings-with-txids/blob/master/data/satoshi_uploader</a></li>
<li><a href="https://github.com/cirosantilli/bitcoin-strings-with-txids#satoshi_uploader" rel="nofollow noreferrer">https://github.com/cirosantilli/bitcoin-strings-with-txids#satoshi_uploader</a></li>
</ul>
<p>I have indexed every single blockchain transaction that follows the above format, notably having a matching CRC32 in the first 8 bytes.</p>
<p>I have also managed to download and interpret every single one of the transactions as described briefly at: <a href="https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/illegal-content-of-block-229k" rel="nofollow noreferrer">https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/illegal-content-of-block-229k</a></p>
<p>Basically, very soon after the uploader/downloader were uploaded on 229991, besides the whitepaper, several uploads were made intentionally containing what some countries might consider illegal data, although none of them eye-popping for my crazy standards, and as we've seen so far, the Bitcoin blockchain has survived them for the time being as of 2021.</p>
<p>Since the illegal content was uploaded soon after the uploader, I find it exceedingly likely that it was uploaded by the same person who created the uploader.</p>
<p>And after a short flurry of activity, the uploads stopped, and only one single upload was made much later at tx 89248ecadd51ada613cf8bdf46c174c57842e51de4f99f4bbd8b8b34d3cb7792 which caught my attention because it contains an ASCII art of a Buddha between two Yin Yang symbols as described at: <a href="https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/ascii-art" rel="nofollow noreferrer">https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/ascii-art</a></p>
<pre><code>          69696969                         69696969
       6969    696969                   696969    6969
     969    69  6969696               6969  6969     696
    969        696969696             696969696969     696
   969        69696969696           6969696969696      696
   696      9696969696969           969696969696       969
    696     696969696969             969696969        969
     696     696  96969      _=_      9696969  69    696
       9696    969696      q(-_-)p      696969    6969
          96969696         '_) (_`         69696969
             96            /__/  \            69
             69          _(&lt;_   / )_          96
            6969        (__\_\_|_/__)        9696
</code></pre>
<p>Therefore my conclusion is that the uploader and downloader scripts simply never reached considerable popularity.</p>
<p>They are just too over-engineered and clunkier than needed. Why would you need a CRC32 when the blockchain itself is already hashed byte-by-byte to the brim? Perhaps it was intended simply as a marker of &quot;interesting information&quot;. But much more natural and dominant later on was to use ASCII strings, or simply magic bytes of certain file formats as markers, see e.g. my comments on more directly encoded images at:</p>
<ul>
<li><a href="https://github.com/cirosantilli/bitcoin-strings-with-txids#image-indexing" rel="nofollow noreferrer">https://github.com/cirosantilli/bitcoin-strings-with-txids#image-indexing</a></li>
<li><a href="https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/images" rel="nofollow noreferrer">https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/images</a></li>
</ul>

CC BY-SA 4.0
2021-04-20T11:50:38.527Z
2021-04-21T17:45:00.983Z
2021-04-21T17:45:00.983Z
Ciro Santilli OurBigBook.com
<p>This is a fun little puzzle on the blockchain, basically. First, you need to know a little about pdf's and how they're structured, which you can find <a href="http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf" rel="nofollow noreferrer">here</a>.</p>
<p>Second, you'll note from section 3.4.1 that all pdf's start with this string:</p>
<pre><code>%PDF-
</code></pre>
<p>In hex, that is <code>255044462d</code>. And indeed that is in the very first output in the very first bare multisig pubkey:</p>
<pre><code>&lt;e4cf0200&gt;&lt;067daf13&gt;**255044462d**312e340a25c3a4c3bcc3b6c39f0a322030206f626a0a3c3c2f4c656e6774682033203020522f46696c7465722f466c6174654465
</code></pre>
<p>I haven't figured out what the first 8 bytes are for (♦<strong>edit</strong>: <code>e4cf0200067daf13 = 2x 4byte little Endian &quot;checksums&quot;, see @WizardOfOzzie comment below</code>), but the rest of the bare multisig keys (everything in between 1 and 3 OP_CHECKMULTISIG in each output -- note the last one is a 1 of 1, so it's 1 OP_CHECKMULTISIG) are pieces of data for the pdf and they are in order. If you can put all the hex digits of the bare multisig keys into a single file (no whitespaces) called &quot;fromblockchain.hex&quot;, you can run this very simple program to extract the pdf:</p>
<pre><code>contents = open('fromblockchain.hex').read()
bytes = contents[16:].decode('hex')
f = open(&quot;bitcoin.pdf&quot;)
f.write(bytes)
f.close()
</code></pre>
<p>This should create a bitcoin.pdf which is the actual satoshi whitepaper. I've tested this and indeed it is the whitepaper. Good to know it's literally in the blockchain.</p>
<p>Alternatively, if you have bitcoind running on your machine, you can run this python script to grab the bitcoin whitepaper:</p>
<pre><code>import subprocess

# raw = full hex of raw Tx using Bitcoin-cli
raw = subprocess.check_output([&quot;bitcoin-cli&quot;, &quot;getrawtransaction&quot;, &quot;54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713&quot;])

outputs = raw.split(&quot;0100000000000000&quot;)

pdf = &quot;&quot;
for output in outputs[1:-2]:
    # there are 3 65-byte parts in this that we need
    cur = 6
    pdf += output[cur:cur+130].decode('hex')
    cur += 132
    pdf += output[cur:cur+130].decode('hex')
    cur += 132
    pdf += output[cur:cur+130].decode('hex')

pdf += outputs[-2][6:-4].decode(&quot;hex&quot;)
f = open(&quot;bitcoin.pdf&quot;, &quot;wb&quot;)
f.write(pdf[8:-8])
f.close()
</code></pre>
<p>Finally checking checksum</p>
<pre><code>sha256sum bitcoin.pdf
b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553  bitcoin.pdf
</code></pre>

CC BY-SA 3.0
2015-02-10T06:19:09.790Z
Ahh ha! Another fantastic answer! I was trying to get a script like the above working and couldn't parse the hex after `0x5141` and between the `0x41`s. What is up with the last two outputs being standard `76a914____88ac`?
Wizard Of Ozzie
CC BY-SA 3.0
2015-02-10T06:31:50.860Z
Looked to me like the person spent 1 satoshi on each output and wanted the change back for the last one. Not sure about the second to last.
Jimmy Song
CC BY-SA 3.0
2015-02-10T08:02:30.263Z
Perhaps the it's a dual purpose: allows output to be collected and the data length can't be padded, since 20 bytes x2 % 8 whereas doing that with 65 bytes is much harder (actually 65x3)
Wizard Of Ozzie
CC BY-SA 3.0
2015-02-10T10:32:35.557Z
Did you want to edit the bottom script to add the output at outputs[-2]? The script needs this after the for loop: `pdf += outputs[-2][6:-4].decode('hex')`. Also *open* needs to be using `open("bitcoin.pdf", 'wb')` (binary mode). I'll have to look into multisig more b/c I still fail to see why the rest of the multisigs are pushing 0x41 bytes Vs the last only pushing 0x21 bytes
Wizard Of Ozzie
CC BY-SA 3.0
2015-03-25T01:49:01.557Z
Can you edit your fantastic answer to also show reverse functionality (ie `bitcoin.pdf` => `rawhextx`) using Python? Python-specific forums/subreddits are having a hard time understanding the Bitcoin aspect. *EDIT: yes I am aware this is a non_standard Tx; 'tis for intellectual curiosity only*
Wizard Of Ozzie
CC BY-SA 3.0
2015-03-26T05:26:57.123Z
Re: `0xe4cf0200067daf13` (*"I haven't figured out what the first 8 bytes are for"*). It's actually 2x 4 byte "checksums". More specifically, little Endian hex representations of: `<total pdf size>` `<crc32 for pdf>` ie `184292` `330267910` ;)
Wizard Of Ozzie
CC BY-SA 3.0
2015-03-26T19:37:49.397Z
Nice catch @WizardOfOzzie. I'll try to write something up to do the opposite direction (any pdf, not just bitcoin whitepaper) soon.
Jimmy Song
CC BY-SA 3.0
2015-03-26T23:15:28.013Z
this code - https://gist.github.com/shirriff/bfc4df70a02732493a28#file-bitcoin-insertion-tool-py - is what was used, I believe. The problem is it depends on actual UTXOs and counts values, checking both, whereas I'm just trying to create a raw unsigned Tx, and sign it (ie no need for Bitcoincore RPC, I'll provide the input data like txid, VOUT etc)
Wizard Of Ozzie
CC BY-SA 4.0
2021-01-21T12:42:49.613Z
I did a binary diff against the version previous on `bitcoincore.org` and it seems to differ. My guess is that it's some irrelevant metadata thing.
Sjors Provoost
CC BY-SA 4.0
2021-01-21T13:00:47.370Z
Also, `getrawtransaction` now takes an optional `blockhash` argument. So that removes the need for `-txindex` here.
Sjors Provoost
CC BY-SA 4.0
2021-04-20T11:56:35.603Z
@WizardOfOzzie I have done some further analysis of that uploader script at: https://bitcoin.stackexchange.com/a/105574/21282 and I agree that it is almost certainly what was used.
Ciro Santilli OurBigBook.com
CC BY-SA 4.0
2015-02-09T20:21:40.270Z
2021-01-22T21:03:12.007Z
2021-01-22T21:03:12.007Z
Jimmy Song
<p>The whitepaper is apparently encoded at <a href="https://mempool.space/tx/54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713" rel="nofollow noreferrer">54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713</a>, which is an <em>m of n multisig</em> Tx with <strong>947 outputs</strong> (just under the scriptsig limit of 20kB!).</p>
<p>Using the <a href="https://github.com/blocktrail/blocktrail-sdk-python" rel="nofollow noreferrer">Blocktrail Python SDK</a>, I get a list of the outputs as hex using the following Python (2.7) code (NB, the <em>APIKEY</em>, <em>APISECRET</em> parameters are available if required from <a href="http://www.blocktrail.com" rel="nofollow noreferrer">www.blocktrail.com</a>):</p>
<pre><code>from blocktrail import APIClient
bt_client = APIClient(APIKEY, APISECRET, network='BTC')
txnObj = bt_client.transaction('54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713')
hashes = [(t['script_hex']) for t in (txnObj)['outputs']]   
</code></pre>
<p>The resulting list is available <a href="https://gist.github.com/406e53c06488e8305be3" rel="nofollow noreferrer">here in full</a> and is essentially all pay-to-pubkey-script Txns. An excerpt:</p>
<pre><code>[u'5141e4cf0200067daf13255044462d312e340a25c3a4c3bcc3b6c39f0a322030206f626a0a3c3c2f4c656e6774682033203020522f46696c7465722f466c617465446541636f64653e3e0a73747265616d0a789cad5c4b8b24b911becfafa8b3a1da292925654253d0d55373f06d61c007e39bbd061f0cde8bffbe25c55b5266f61ab3905d419ba54728e28bb76a963777fbcfb77fdf96db7d291f93f3e599f7fafcedefb73fffe1f6aff665fdefb77f7c7bfefce6c2fa166e695bdfd6dbcfbfddfef8c3dd5cf953ae',
.....
u'514130206e200a30303030313832353430203030303030206e200a747261696c65720a3c3c2f53697a652036382f526f6f74203636203020520a2f496e666f20363720413020520a2f4944205b203c43413142304134344244353432343533424546393138464643443436444330343e0a3c4341314230413434424435343234353342454641393138464643443436444330343e205d0a2f446f63436865636b73756d202f36463732454137353134444641443233464142434337413535303032314146370a3e53ae',
 u'51213e0a7374617274787265660a3138323732370a2525454f460a000000000000000051ae',
 u'76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac',
 u'76a914031c79236ff3017496cf8d9a883f494458f245f288ac']
</code></pre>
<p><strong>QUESTION:</strong> How is this array of hex data parsed into the <a href="http://www.bitcoin.org/bitcoin.pdf" rel="nofollow noreferrer">bitcoin.pdf</a>? Specific Python framed answers would be appreciated!</p>

CC BY-SA 3.0
2015-02-09T12:41:38.903Z
I should add its 945x *? of 3*(?) txns and 2x paytopublickey outputs
Wizard Of Ozzie
CC BY-SA 3.0
2015-02-09T16:18:09.730Z
`scriptsig limit of 20kB!` I was aware of a 10 kB limit, enforced in `EvalScript`, but what 20 kB limit are you referring to?
Nick ODell
CC BY-SA 3.0
2015-02-10T06:20:27.663Z
@NickODell isn't the limit for scripts 20kb?
Wizard Of Ozzie
<blockchain><python><raw-data><whitepaper>
How is the whitepaper decoded from the blockchain (Tx with ~1000x m of n multisig outputs)
Wizard Of Ozzie
# How is the whitepaper decoded from the blockchain (Tx with ~1000x m of n multisig outputs)

(Asked by: username_0 on 2015-02-09)

The whitepaper is apparently encoded at [54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713](https://mempool.space/tx/54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713), which is an *m of n multisig* Tx with **947 outputs** (just under the scriptsig limit of 20kB!).

Using the [Blocktrail Python SDK](https://github.com/blocktrail/blocktrail-sdk-python), I get a list of the outputs as hex using the following Python (2.7) code (NB, the *APIKEY*, *APISECRET* parameters are available if required from [www.blocktrail.com](http://www.blocktrail.com)):

```
from blocktrail import APIClient
bt_client = APIClient(APIKEY, APISECRET, network='BTC')
txnObj = bt_client.transaction('54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713')
hashes = [(t['script_hex']) for t in (txnObj)['outputs']]   

```

The resulting list is available [here in full](https://gist.github.com/406e53c06488e8305be3) and is essentially all pay-to-pubkey-script Txns. An excerpt:

```
[u'5141e4cf0200067daf13255044462d312e340a25c3a4c3bcc3b6c39f0a322030206f626a0a3c3c2f4c656e6774682033203020522f46696c7465722f466c617465446541636f64653e3e0a73747265616d0a789cad5c4b8b24b911becfafa8b3a1da292925654253d0d55373f06d61c007e39bbd061f0cde8bffbe25c55b5266f61ab3905d419ba54728e28bb76a963777fbcfb77fdf96db7d291f93f3e599f7fafcedefb73fffe1f6aff665fdefb77f7c7bfefce6c2fa166e695bdfd6dbcfbfddfef8c3dd5cf953ae',
.....
u'514130206e200a30303030313832353430203030303030206e200a747261696c65720a3c3c2f53697a652036382f526f6f74203636203020520a2f496e666f20363720413020520a2f4944205b203c43413142304134344244353432343533424546393138464643443436444330343e0a3c4341314230413434424435343234353342454641393138464643443436444330343e205d0a2f446f63436865636b73756d202f36463732454137353134444641443233464142434337413535303032314146370a3e53ae',
 u'51213e0a7374617274787265660a3138323732370a2525454f460a000000000000000051ae',
 u'76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac',
 u'76a914031c79236ff3017496cf8d9a883f494458f245f288ac']

```

**QUESTION:** How is this array of hex data parsed into the [bitcoin.pdf](http://www.bitcoin.org/bitcoin.pdf)? Specific Python framed answers would be appreciated!

**Tags:** blockchain, python, raw-data, whitepaper

> **Comments:**
> **username_0**: I should add its 945x *? of 3*(?) txns and 2x paytopublickey outputs  
> **username_1**: `scriptsig limit of 20kB!` I was aware of a 10 kB limit, enforced in `EvalScript`, but what 20 kB limit are you referring to?  
> **username_0**: @username_1 isn't the limit for scripts 20kb?  

---

## 3 answers

### Answer by username_5 on 2015-02-09. Score: 32 | Accepted answer

This is a fun little puzzle on the blockchain, basically. First, you need to know a little about pdf's and how they're structured, which you can find [here](http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf).

Second, you'll note from section 3.4.1 that all pdf's start with this string:

```
%PDF-

```

In hex, that is `255044462d`. And indeed that is in the very first output in the very first bare multisig pubkey:

```
<e4cf0200><067daf13>**255044462d**312e340a25c3a4c3bcc3b6c39f0a322030206f626a0a3c3c2f4c656e6774682033203020522f46696c7465722f466c6174654465

```

I haven't figured out what the first 8 bytes are for (♦**edit**: `e4cf0200067daf13 = 2x 4byte little Endian "checksums", see @username_0 comment below`), but the rest of the bare multisig keys (everything in between 1 and 3 OP\_CHECKMULTISIG in each output -- note the last one is a 1 of 1, so it's 1 OP\_CHECKMULTISIG) are pieces of data for the pdf and they are in order. If you can put all the hex digits of the bare multisig keys into a single file (no whitespaces) called "fromblockchain.hex", you can run this very simple program to extract the pdf:

```
contents = open('fromblockchain.hex').read()
bytes = contents[16:].decode('hex')
f = open("bitcoin.pdf")
f.write(bytes)
f.close()

```

This should create a bitcoin.pdf which is the actual satoshi whitepaper. I've tested this and indeed it is the whitepaper. Good to know it's literally in the blockchain.

Alternatively, if you have bitcoind running on your machine, you can run this python script to grab the bitcoin whitepaper:

```
import subprocess

# raw = full hex of raw Tx using Bitcoin-cli
raw = subprocess.check_output(["bitcoin-cli", "getrawtransaction", "54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713"])

outputs = raw.split("0100000000000000")

pdf = ""
for output in outputs[1:-2]:
    # there are 3 65-byte parts in this that we need
    cur = 6
    pdf += output[cur:cur+130].decode('hex')
    cur += 132
    pdf += output[cur:cur+130].decode('hex')
    cur += 132
    pdf += output[cur:cur+130].decode('hex')

pdf += outputs[-2][6:-4].decode("hex")
f = open("bitcoin.pdf", "wb")
f.write(pdf[8:-8])
f.close()

```

Finally checking checksum

```
sha256sum bitcoin.pdf
b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553  bitcoin.pdf

```

> **Comments:**
> **username_0**: Ahh ha! Another fantastic answer! I was trying to get a script like the above working and couldn't parse the hex after `0x5141` and between the `0x41`s. What is up with the last two outputs being standard `76a914____88ac`?  
> **username_5**: Looked to me like the person spent 1 satoshi on each output and wanted the change back for the last one. Not sure about the second to last.  
> **username_0**: Perhaps the it's a dual purpose: allows output to be collected and the data length can't be padded, since 20 bytes x2 % 8 whereas doing that with 65 bytes is much harder (actually 65x3)  
> **username_0**: Did you want to edit the bottom script to add the output at outputs[-2]? The script needs this after the for loop: `pdf += outputs[-2][6:-4].decode('hex')`. Also *open* needs to be using `open("bitcoin.pdf", 'wb')` (binary mode). I'll have to look into multisig more b/c I still fail to see why the rest of the multisigs are pushing 0x41 bytes Vs the last only pushing 0x21 bytes  
> **username_0**: Can you edit your fantastic answer to also show reverse functionality (ie `bitcoin.pdf` => `rawhextx`) using Python? Python-specific forums/subreddits are having a hard time understanding the Bitcoin aspect. *EDIT: yes I am aware this is a non_standard Tx; 'tis for intellectual curiosity only*  
> **username_0**: Re: `0xe4cf0200067daf13` (*"I haven't figured out what the first 8 bytes are for"*). It's actually 2x 4 byte "checksums". More specifically, little Endian hex representations of: `<total pdf size>` `<crc32 for pdf>` ie `184292` `330267910` ;)  
> **username_5**: Nice catch @username_0. I'll try to write something up to do the opposite direction (any pdf, not just bitcoin whitepaper) soon.  
> **username_0**: this code - https://gist.github.com/shirriff/bfc4df70a02732493a28#file-bitcoin-insertion-tool-py - is what was used, I believe. The problem is it depends on actual UTXOs and counts values, checking both, whereas I'm just trying to create a raw unsigned Tx, and sign it (ie no need for Bitcoincore RPC, I'll provide the input data like txid, VOUT etc)  
> **username_6**: I did a binary diff against the version previous on `bitcoincore.org` and it seems to differ. My guess is that it's some irrelevant metadata thing.  
> **username_6**: Also, `getrawtransaction` now takes an optional `blockhash` argument. So that removes the need for `-txindex` here.  
> **username_4**: @username_0 I have done some further analysis of that uploader script at: https://bitcoin.stackexchange.com/a/105574/21282 and I agree that it is almost certainly what was used.  

---

### Answer by username_2 on 2021-01-21. Score: 8

This is a bash command that can also give you the file:

```
bitcoin-cli getrawtransaction 54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713 0 00000000000000ecbbff6bafb7efa2f7df05b227d5c73dca8f2635af32a2e949  | sed 's/0100000000000000/\n/g' | tail -n +2 | cut -c7-136,139-268,271-400 | tr -d '\n' | cut -c17-368600 | xxd -p -r > bitcoin.pdf

```

It doesn't need `-txindex`, but it does need a full node, not pruned. I suppose it's possible to make a variant that works with `gettxout` that works with pruned nodes, but that's left as an exercise for the reader.

> **Comments:**
> **username_3**: Exercise done by @jb55 :-) `seq 0 947 | (while read -r n; do bitcoin-cli gettxout 54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713 $n | jq -r '.scriptPubKey.asm' | awk '{ print $2 $3 $4 }'; done) | tr -d '\n' | cut -c 17-368600 | xxd -r -p > bitcoin.pdf` Source: https://bitcoinhackers.org/@jb55/105595146491662406  

---

### Answer by username_4 on 2021-04-20. Score: 3

**The PDF uses Python uploader/downloader scripts present in the Bitcoin blockchain itself**

Those scripts are present at:

* uploader: tx 4b72a223007eab8a951d43edc171befeabc7b5dca4213770c88e09ba5b936e17 block 229991 reproduced at: <https://gist.github.com/cirosantilli/ade4dde7c2f2f5020d792872681763e8>
* downloader: tx 6c53cd987119ef797d5adccd76241247988a0a5ef783572a9972e7371c5fb0cc block 229991 reproduced at <https://gist.github.com/cirosantilli/e90bd2e6c3fab25a20898e61e3ab3e90>

These are my forks of Ken Shirriff's Gists who presumably found them while researching for: <http://www.righto.com/2014/02/ascii-bernanke-wikileaks-photographs.html>

Both scripts have the copyright header:

```
#
# File downloader
# Requires git://github.com/jgarzik/python-bitcoinrpc.git
#
# (c) 2013 Satoshi Nakamoto All Rights Reserved
#
# UNAUTHORIZED DUPLICATION AND/OR USAGE OF THIS PROGRAM IS PROHIBITED BY US AND INTERNATIONAL COPYRIGHT LAW

```

which is likely a joke and not really written by Satoshi in my opninon.

Both uploader and downloader are uploaded with the uploader encoding, which creates a slight chicken-and-egg bootstrap issue to download the downloader. But the encoding is so simple that you could just download the raw downloader data and fix it up manually.

After downloading the downloader manually, I managed to run it to extract a byte-by-byte match of the PDF present at <https://web.archive.org/web/20210418161957/https://bitcoin.org/bitcoin.pdf> (sha256 == b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553).

First you have to be running a Bitcoin core server locally. Suppose that your `.bitcon/bitoin.conf` contains:

```
rpcuser=asdf
rpcpassword=qwer
server=1
txindex=1

```

Then I run:

```
git clone git://github.com/jgarzik/python-bitcoinrpc.git
git -C python-bitcoinrpc checkout cdf43b41f982b4f811cd4ebfbc787ab2abf5c94a
wget https://gist.githubusercontent.com/shirriff/64f48fa09a61b56ffcf9/raw/ad1d2e041edc0fb7ef23402e64eeb92c045b5ef7/bitcoin-file-downloader.py
pip install python-bitcoinrpc==1.0
BTCRPCURL=http://asdf:qwer@127.0.0.1:8332 \
  PYTHONPATH="$(pwd)/python-bitcoinrpc:$PYTHONPATH" \
  python3 bitcoin-file-downloader.py \
  54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713

```

where tx 54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713 (block 230009) is the transaction in which the whitepaper is encoded.

**Encoding used by the downloader script**

The downloader is not super strict about the subtleties of the uploader. We can immediately understand its format from the source:

```
data = b''
for txout in tx['vout'][0:-2]:
    for op in txout['scriptPubKey']['asm'].split(' '):
        if not op.startswith('OP_') and len(op) >= 40:
            data += unhexlify(op.encode('utf8'))

length = struct.unpack('<L', data[0:4])[0]
checksum = struct.unpack('<L', data[4:8])[0]
data = data[8:8+length]

if checksum != crc32(data):
    print('Checksum mismatch; expected %d but calculated %d' % (checksum, crc32(data)),
          file=sys.stderr)
    sys.exit()

```

so we see that it:

* ignores the two last outputs
* ignores operands
* ignores any integer constant with less than 20 bytes (40 hex bytes)

Then, from the data above:

* the first 4 bytes are the payload length
* the next 4 bytes are a crc32 of the rest of payload data
* the payload starts at the 8th byte

**Encoding used by the uploader script**

The uploader is a bit more specific. Perhaps it is easiest to understand it by looking at the disassembly of a small upload, e.g. the downloader itself: <https://www.blockchain.com/btc/tx/6c53cd987119ef797d5adccd76241247988a0a5ef783572a9972e7371c5fb0cc>

First we have a bunch of outputs of type:

```
OP_1 data OP_3 OP_CHECKMULTISIG

```

and worth a tiny 0.00000001 BTC which contain the bulk of the data. These cannot presumably be spent, due to the `OP_CHECKMULTISIG`, but this cannot be proven either.

Then at then end we have two different transactions, which as we saw above the downloader ignores:

* a standard P2PKH `OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG` with minimal value 0.00000001 BTC and unspent. TODO why? This corresponds to the part of the code:

  ```
  # dest output
  out_value = Decimal(sys.argv[3])
  change -= out_value
  txouts.append((out_value, OP_DUP + OP_HASH160 + pushdata(addr2bytes(sys.argv[2])) + OP_EQUALVERIFY + OP_CHECKSIG))

  ```

  which shows that you just pass an arbitrary target value and address from your script.
* a standard spent P2PKH change transaction with a considerable value, to recover leftovers from the input. It is sent to a new address owned by you that is created on the fly via the RPC call:

  ```
  change_addr = proxy.getnewaddress()
  txouts.append([change, OP_DUP + OP_HASH160 + pushdata(addr2bytes(change_addr)) + OP_EQUALVERIFY + OP_CHECKSIG])

  ```

Reading the source basically confirms what we see on the disassembly. Some other pointers follow.

At:

```
data = open(sys.argv[1],'rb').read()
data = struct.pack('<L', len(data)) + struct.pack('<L', crc32(data)) + data
fd = io.BytesIO(data)

```

we see the:

* payload length `struct.pack('<L', len(data))`
* crc32

being added to the start of the file.

At:

```
def checkmultisig_scriptPubKey_dump(fd):
    data = fd.read(65*3)
    if not data:
        return None

    r = pushint(1)

    n = 0
    while data:
        chunk = data[0:65]
        data = data[65:]

        if len(chunk) < 33:
            chunk += b'\x00'*(33-len(chunk))
        elif len(chunk) < 65:
            chunk += b'\x00'*(65-len(chunk))

        r += pushdata(chunk)
        n += 1

    r += pushint(n) + OP_CHECKMULTISIG
    return r

```

we see the main data encoding loop. This clarifies from the disassembly:

```
OP_1 data OP_3 OP_CHECKMULTISIG

```

* the `OP_1` is fixed at `pushint(1)`
* the `OP_3` can be less than 3, it encodes how many 65 byte chunks were encoded, with a maximum of 3 chunks present on each output due to `fd.read(65*3)`

  The last payload transaction can therefore have `OP_1`, `OP_2` or `OP_3` depending on granularity.

**What else was the uploader script used for and was it popular?**

At:

* <https://github.com/cirosantilli/bitcoin-strings-with-txids/blob/master/data/satoshi_uploader>
* <https://github.com/cirosantilli/bitcoin-strings-with-txids#satoshi_uploader>

I have indexed every single blockchain transaction that follows the above format, notably having a matching CRC32 in the first 8 bytes.

I have also managed to download and interpret every single one of the transactions as described briefly at: <https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/illegal-content-of-block-229k>

Basically, very soon after the uploader/downloader were uploaded on 229991, besides the whitepaper, several uploads were made intentionally containing what some countries might consider illegal data, although none of them eye-popping for my crazy standards, and as we've seen so far, the Bitcoin blockchain has survived them for the time being as of 2021.

Since the illegal content was uploaded soon after the uploader, I find it exceedingly likely that it was uploaded by the same person who created the uploader.

And after a short flurry of activity, the uploads stopped, and only one single upload was made much later at tx 89248ecadd51ada613cf8bdf46c174c57842e51de4f99f4bbd8b8b34d3cb7792 which caught my attention because it contains an ASCII art of a Buddha between two Yin Yang symbols as described at: <https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/ascii-art>

```
          69696969                         69696969
       6969    696969                   696969    6969
     969    69  6969696               6969  6969     696
    969        696969696             696969696969     696
   969        69696969696           6969696969696      696
   696      9696969696969           969696969696       969
    696     696969696969             969696969        969
     696     696  96969      _=_      9696969  69    696
       9696    969696      q(-_-)p      696969    6969
          96969696         '_) (_`         69696969
             96            /__/  \            69
             69          _(<_   / )_          96
            6969        (__\_\_|_/__)        9696

```

Therefore my conclusion is that the uploader and downloader scripts simply never reached considerable popularity.

They are just too over-engineered and clunkier than needed. Why would you need a CRC32 when the blockchain itself is already hashed byte-by-byte to the brim? Perhaps it was intended simply as a marker of "interesting information". But much more natural and dominant later on was to use ASCII strings, or simply magic bytes of certain file formats as markers, see e.g. my comments on more directly encoded images at:

* <https://github.com/cirosantilli/bitcoin-strings-with-txids#image-indexing>
* <https://cirosantilli.com/cool-data-embedded-in-the-bitcoin-blockchain/images>


---

