Download
Getting files from blockchain after upload on Aetrna
This feature reconstructs and decrypts the files if needed. It is available through the distinct Download page or Library page's Download button for the saved files respectively.
The Download page allows to download the file in 3 ways:
Just the file_hash (access hash), which you can get from the NFT, Library's "Get Key" button or on-chain data
Full metadata with chain_id, storage address etc from NFT or Library's "Get Key" button
The idea is this (EVM data storage):

Decryption happens on frontend inside the browser (Noble library is used), however, to avoid private_key exposure, you can also save the encrypted file and decrypt it offline or in VM/sandbox. The standalone tool is here:
Manual reconstruction
This method is only valid for Alpha 1.0 release. In next releases the system will be refactored for even cheaper storage and the method listed here might become irrelevant
As you know, Aetrna is built to serve its puporse even if it is no longer online — what it means is that the user, who has uploaded the file in the past, will still be able to get their file themselves by hand without complex tools or coding knowledge.
Here is a simple example how to reconstruct the file with just a bit of persistence. The file in question is not encrypted and is stored permanently, meaning the Blobs (EIP-4844) were not enabled (reconstructing files from Blobs would require a different approach since the data there is stored in sidecars and not in execution layer (EL) of Ethereum, but in Consensus layer (CL) for 4096 epochs).
The upload route used in this example is Metamask, for Relayer route, there is one extra step with an RPC call to find the transaction hash through eth_getLogs with inputs of block_number, and storage_contract (and optionallyevent_signature and file_hash for topics filter). Example in Python:
Reconstruction steps
Locate the Access Key — assuming the user has minted an NFT during upload like this
Find the
block numberin the Access Key which is8780579in our exampleFind the
storage_contractin the Access Key which is0x7Bcd679D0892534bdD6EE083fdB542228f4E4702in our exampleGo the block explorer like Etherscan and search for the
storage_contractwhich leads to this pageLook for the block number
8780579in the transaction list (Ctrl+F) and click on the transaction hash next to it, which leads to this pageClick on "Show more details" nd then look into the
Input datawhich will contain a hexadecimal string. In our case it is this:
Now, lets decode whats in the input data to extract the actual file content before decompression
Decoding the Input Data
The easiest way is to paste the hexadecimal string into a notepad and split it by 32-bytes (64 characters) each of which serves its own purpose. This is table that shows the structure of the Input Data, you can do the same if you add line breaks (press Enter) to the Input Data string in the notepad — first, separate the Function selection (first 8 symbols after 0x), then split the rest by 32 bytes (64 symbols):
1.
Function selection (4 bytes = 8 characters after 0x)
0x75bf82d7
2.
Offset to storage params data. 20 in hex means 32 (64 characters), so we skip this first word entirely.
0000000000000000000000000000000000000000000000000000000000000020
3.
Offset to packedChunks data in bytes. 1e0 is 480 in decimal (bytes) or 960 in hex characters.
00000000000000000000000000000000000000000000000000000000000001e0
...
...
...
18.
Length of packedChunks
00000000000000000000000000000000000000000000000000000000000000c0
19-24.
Content of packedChunks
61ea441b97130a6f7e3b689d93a136d6746573742e706e673a3a789ceb0cf073...
25.
Padding word
0000000000000000000000000000000000000000000000000000000000000000
...
...
...
After we found the offest to the packedChunks which is 1e0 = 480 bytes = 960 characters, we remove the next 960 characters after 1e0 and everything before that (1098 characters in summary).
In case if the dynamic data at the end of the struct is not zeroed and you are working with multiple blocks for concatenation, you will need to account for the 18th word (Length of packedChunks) which will tell where exactly to stop counting the data for part (or block) which is NOT last. The unpadded_length is used after the concatenation, the length of packedChunks is usefull for individual block that is not the last one.
Which leaves us with this:
Now we come back to the
Access Keyto find theunpadded_lengthline which is 172 in this example, which means we take only 172 bytes = 344 characters from thepackedChunkswith padding that we got now, which leaves us with this:
Now that we got the actual file content, time to reconstruct the actual file.
File reconstruction
The structure of the file is this:
Separator :: is always the same value of 3a3a, which leads us to this:
Convert the file name from hexadecimal to ASCII using any online tool.
746573742e706e67 yeilds test.png which is the file name we are going to use.
Now, the last step is decompression. Aetrna uses
zlibfor compression / decompression.zlibis heavily standardazed and used everywhere (PNG, Zip, FLAC formats, VPNs, Linux kernel, etc). The standards are RFC 1950 / 1951 since 1996. So even decades later, the user will be able to decompress the file by few lines of code.
Example with Python:
Running that will yeild the actual file from the blockchain. For file that were stored across multiple blocks, you will need to concatenate the file content and decompress in a same way. The structure of the files in second + N blocks will have no 16-byte salt, file_name and separator :: in it, only the part of content.
Last updated