Symbol Blog and the Perplexing PNG puzzle

THIS EXAMPLE IS NO LONGER WORKING – PLEASE SCROLL TO THE BOTTOM OF THE ARTICLE FOR AN UPDATED EXAMPLE

Gimre posted this tweet a while back now and I wanted to give it a go but kind of thought that someone else would solve it in no time and I would spend hours working on it and get nowhere. Anyway, I decided to have a go last night and failed miserably but I had no idea why. It turned out that it just required 5 minutes of reading on PNG file format and it was a super easy fix.

In this hastily written article I am going to take you through my hastily written code, apologies it is messy and probably clunky and inefficient. I basically know zero Python syntax so it will look like a 10 year old wrote it. I am amazed that I managed to get it to work (even if the problem was simple!) 😁

import urllib.request
import json
from binascii import unhexlify
NODEURL = "http://symbolblog-testnet.com:3000"

Spoiler: this was the killer for me. I did all of the rest really quickly but there was one missing element that I needed to add.. The PNG header (89 50 4E 47 0D 0A 1A 0A) once I added this to the beginning of the file that I output the image magically appeared – it was a eureka moment for me. Nothing more satisfying than solving a problem 😁

unhexlify("89504E470D0A1A0A")
b'\x89PNG\r\n\x1a\n'

Next it was just a case of taking the first transaction http://symbolblog-testnet.com:3000/transactions/confirmed/5B1A42E157411311B5F0302E6A1F9150C0E21BFBBE4741662F9922279A44570B and following the trail.

There were two messages “0000000D49484452000005000000050008030000002F3A0FC5”:

b'\x00\x00\x00\rIHDR\x00\x00\x05\x00\x00\x00\x05\x00\x08\x03\x00\x00\x00/:\x0f\xc5'

This was part of the file that we would assemble. And the second longer message:

0000032B69545874416767726567617465730000000000686173686573364634304235353133443431323337334133383531443539444242374244463637424642323743323437434646354145363444393837423742384243323337432C20383443333036373144324241394645314139363946373732414446334438423441334135313834434333453144413632344537383145354631363837424344312C20373542463837453837363242413133423841343844464545453131424238364132343131454644454341343533324332304242414544314431303945383432432C20394535414345344441383732354445384633314646423730453538423835434235423936313730343841413234323346434545454633434333394537324335352C20383442364433373942323337434537434135353231333343454644464245323635343945414333373236314546413935353842453639364430364542444230382C20323246363744394642374139453231433544433136394341443439423642454134314544304141463444423336313134433341303846393639384541313535322C20413241323934423135313137323444314544414541363937343733384633393145373838374334424644334231373039323130414446353743413137323231372C20314239304331373738414437433230383641313345323142453046453839434446324444444335443935383344364632454442314534304234443036303241312C20333538373937364233463836394341434136364346373934334538383335394345303843423945353544323341454338324630393230313135443939383235322C20433846334634303933383945364132413938433737314631323244313836343743414141323544343237433237443130343035444542343638413936383939412C20393742414536333941443035414545303537363037413039333842304245423438383131423943373334314632354345453830333746374439454144304431302C2039343332424332373731343730413943463937423244343045393643354346333438414431363730303842464344343130394346393545374241394643303032D1951748

Contained information on the other aggregate transactions needed to assemble the file:

b'\x00\x00\x03+iTXtAggregates\x00\x00\x00\x00\x00hashes6F40B5513D412373A3851D59DBB7BDF67BFB27C247CFF5AE64D987B7B8BC237C, 84C30671D2BA9FE1A969F772ADF3D8B4A3A5184CC3E1DA624E781E5F1687BCD1, 75BF87E8762BA13B8A48DFEEE11BB86A2411EFDECA4532C20BBAED1D109E842C, 9E5ACE4DA8725DE8F31FFB70E58B85CB5B9617048AA2423FCEEEF3CC39E72C55, 84B6D379B237CE7CA552133CEFDFBE26549EAC37261EFA9558BE696D06EBDB08, 22F67D9FB7A9E21C5DC169CAD49B6BEA41ED0AAF4DB36114C3A08F9698EA1552, A2A294B1511724D1EDAEA6974738F391E7887C4BFD3B1709210ADF57CA172217, 1B90C1778AD7C2086A13E21BE0FE89CDF2DDDC5D9583D6F2EDB1E40B4D0602A1, 3587976B3F869CACA66CF7943E88359CE08CB9E55D23AEC82F0920115D998252, C8F3F409389E6A2A98C771F122D18647CAAA25D427C27D10405DEB468A96899A, 97BAE639AD05AEE057607A0938B0BEB48811B9C7341F25CEE8037F7D9EAD0D10, 9432BC2771470A9CF97B2D40E96C5CF348AD167008BFCD4109CF95E7BA9FC002\xd1\x95\x17H'

So I just stuck all of the transactions into an array and sequentially pulled all messages from each transaction (I think that there were 306 of them?) and “unhexlified” the results.

#Loop through all messages in each of the transactions from txarray

for tx in txarray:
    url = NODEURL + '/transactions/confirmed/' + tx
    req = urllib.request.Request(url)
    with urllib.request.urlopen(req) as res:
        data = json.load(res)
        i=0
        for d in data['transaction']['transactions']:
            msg = unhexlify(data['transaction']['transactions'][i]['transaction']['message'])
            xs=xs+msg
            i=i+1

Then I just wrote everything to a binary file called image.png opened up the folder I saved it to and I saw a thumbnail of the png – problem solved 😁

Here is the image:

I have added all my code below. If you run this through you should get the same image file!

import urllib.request
import json
from binascii import unhexlify

NODEURL = "http://symbolblog-testnet.com:3000"

#Pesky PNG header
xs= unhexlify("89504E470D0A1A0A")

#Looks like this:
unhexlify("89504E470D0A1A0A")

f = open("output.png", "wb")

#First message in initial transaction
xs= xs+unhexlify("0000000D49484452000005000000050008030000002F3A0FC5")

#Looks like this:
unhexlify("0000000D49484452000005000000050008030000002F3A0FC5")

#Second message in initial transaction contains the other aggregate transactions
unhexlify("0000032B69545874416767726567617465730000000000686173686573364634304235353133443431323337334133383531443539444242374244463637424642323743323437434646354145363444393837423742384243323337432C20383443333036373144324241394645314139363946373732414446334438423441334135313834434333453144413632344537383145354631363837424344312C20373542463837453837363242413133423841343844464545453131424238364132343131454644454341343533324332304242414544314431303945383432432C20394535414345344441383732354445384633314646423730453538423835434235423936313730343841413234323346434545454633434333394537324335352C20383442364433373942323337434537434135353231333343454644464245323635343945414333373236314546413935353842453639364430364542444230382C20323246363744394642374139453231433544433136394341443439423642454134314544304141463444423336313134433341303846393639384541313535322C20413241323934423135313137323444314544414541363937343733384633393145373838374334424644334231373039323130414446353743413137323231372C20314239304331373738414437433230383641313345323142453046453839434446324444444335443935383344364632454442314534304234443036303241312C20333538373937364233463836394341434136364346373934334538383335394345303843423945353544323341454338324630393230313135443939383235322C20433846334634303933383945364132413938433737314631323244313836343743414141323544343237433237443130343035444542343638413936383939412C20393742414536333941443035414545303537363037413039333842304245423438383131423943373334314632354345453830333746374439454144304431302C2039343332424332373731343730413943463937423244343045393643354346333438414431363730303842464344343130394346393545374241394643303032D1951748")

#Array of transactions above..
txarray = ['6F40B5513D412373A3851D59DBB7BDF67BFB27C247CFF5AE64D987B7B8BC237C', '84C30671D2BA9FE1A969F772ADF3D8B4A3A5184CC3E1DA624E781E5F1687BCD1', '75BF87E8762BA13B8A48DFEEE11BB86A2411EFDECA4532C20BBAED1D109E842C', '9E5ACE4DA8725DE8F31FFB70E58B85CB5B9617048AA2423FCEEEF3CC39E72C55', '84B6D379B237CE7CA552133CEFDFBE26549EAC37261EFA9558BE696D06EBDB08', '22F67D9FB7A9E21C5DC169CAD49B6BEA41ED0AAF4DB36114C3A08F9698EA1552', 'A2A294B1511724D1EDAEA6974738F391E7887C4BFD3B1709210ADF57CA172217', '1B90C1778AD7C2086A13E21BE0FE89CDF2DDDC5D9583D6F2EDB1E40B4D0602A1', '3587976B3F869CACA66CF7943E88359CE08CB9E55D23AEC82F0920115D998252', 'C8F3F409389E6A2A98C771F122D18647CAAA25D427C27D10405DEB468A96899A', '97BAE639AD05AEE057607A0938B0BEB48811B9C7341F25CEE8037F7D9EAD0D10', '9432BC2771470A9CF97B2D40E96C5CF348AD167008BFCD4109CF95E7BA9FC002']

#Loop through all messages in each of the transactions from txarray

for tx in txarray:
    url = NODEURL + '/transactions/confirmed/' + tx
    req = urllib.request.Request(url)
    with urllib.request.urlopen(req) as res:
        data = json.load(res)
        i=0
        for d in data['transaction']['transactions']:
            msg = unhexlify(data['transaction']['transactions'][i]['transaction']['message'])
            xs=xs+msg
          #  print(msg)
            i=i+1

# Write data to the png file
f.write(xs)

Run it yourself

You can download my Jupyter Notebook file here and run the code yourself (hopefully it will work for you too!) 😁 You can even run it on Google Colab and see the output:

Update 04/10/22

The example above is no longer working as there was a testnet reset. However, I have included an update using an image stored on the Symbol mainnet. You can download the Jupyter Notebook here.

There’s also something special about this image – hint: look at the PLTE data 😊

Tags:
Avatar photo
NineLives
admin@symbolblog.com

I'm a Symbol and NEM enthusiast and run this blog to try to grow awareness of the platform in the English-speaking world. If you have any Symbol news you would like me to report on or you have an article that you would like to publish then please let me know!

No Comments

Sorry, the comment form is closed at this time.