arrow-down-to-bracketDownload

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 libraryarrow-up-right 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

circle-info

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-4844arrow-up-right) 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:

chevron-rightRPC call to find the transaction hashhashtag

Reconstruction steps

  1. Locate the Access Key — assuming the user has minted an NFT during upload like thisarrow-up-right

chevron-rightWe gonna take this Access Key as an examplehashtag

  1. Find the block number in the Access Key which is 8780579 in our example

  2. Find the storage_contract in the Access Key which is 0x7Bcd679D0892534bdD6EE083fdB542228f4E4702 in our example

  3. Go the block explorer like Etherscan and search for the storage_contract which leads to this pagearrow-up-right

  4. Look for the block number 8780579 in the transaction list (Ctrl+F) and click on the transaction hash next to it, which leads to this pagearrow-up-right

  5. Click on "Show more details" nd then look into the Input data which will contain a hexadecimal string. In our case it is this:

chevron-rightInput data in Default viewhashtag

  1. 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):

#
Description
Hex value

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).

circle-info

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:

chevron-rightpackedChunks with paddinghashtag

  1. Now we come back to the Access Key to find the unpadded_length line which is 172 in this example, which means we take only 172 bytes = 344 characters from the packedChunks with padding that we got now, which leaves us with this:

chevron-rightpackedChunks without paddinghashtag

Now that we got the actual file content, time to reconstruct the actual file.


File reconstruction

  1. The structure of the file is this:

Separator :: is always the same value of 3a3a, which leads us to this:

  1. Convert the file name from hexadecimal to ASCII using any online toolarrow-up-right.

746573742e706e67 yeilds test.png which is the file name we are going to use.

  1. Now, the last step is decompression. Aetrna uses zlib for compression / decompression. zlib is heavily standardazed and used everywhere (PNG, Zip, FLAC formats, VPNs, Linux kernel, etc). The standards are RFC 1950arrow-up-right / 1951arrow-up-right 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