Index: 3
TX: 0x5b042e1eb0c556951f50ced40c83e138bc06b916c887f05ef5cdd323e8641a90
Creator: 0x174B3C5f95c9F27Da6758C8Ca941b8FFbD01d330
Create Date: Jun 13 07:00:00 2010
Image Size: 145 x 220
Metadata IPFS Hash : QmReM5QJJBeY7buMfwtLAURhBuHeJ4wneLuRHdJaMk18af
SHA256 Hash :
a370d156a559ec0d751dff48ff99e4568740275ebf42b9044449a2ab9420ac65
This is experimental project based on Ethereum ERC721 token Smart Contract that stores digital information and licensed images’ fingerprint data in blockchain and IPFS. The title refers to ‘artwork’, meaning a digital and licensed image or an image’s identity; this is the translated identity of digital artwork.
Yes, in the digital world, almost everything can be copied from here to there. This may not make sense to many people, even traditional artists (yes, I mean the artist who actually paints or draws on a physical surface). However, to artists who use a computer to create their artwork, keeping their original digital file is not a new idea.
I am one of the artists who always keeps the original digital files and always want to claim ownership of my creation when I deliver the artwork. (Delivery of artwork means transferring an electronic copy of original files to someone). Once a digital file is copied and transferred from one storage (let’s say A) to another (let’s say B), there is no clear methodology to prove which one is the original file, since visually the two set of images that are saved in A and B are same, as long as they were saved by a lossless algorithm (as long as the saved data is in the form of an image, i.e. the well-known image file formats GIF, PNG and TIFF). PNG is one of the most popular among these formats with the generation of portable media. (https://en.wikipedia.org/wiki/Portable_Network_Graphics)
In terms of digital images, within the visual representation there are some possible areas that can be used to identify the original image (or artwork):
Creation and modification timestamp:
Every single digital file has a timestamp of creation and modification. These timestamps could be easily modifiable with simple computer skills.
File size:
As long as the duplicated artworks are being saved in different image file extension than original artworks’ extension, the meaning of file size is just nothing. Even with a different image compression algorithm, the size of two images is totally different. Even with same compression algorithm, repeated decompressing and re-compressing of image decreases size and quality of the image.
Beyond those basic digital file information, some of computational data can possibly be fingerprints of artwork. To use those computational data, there are a few pre-determined rules which we can set by ourselves as the creator of digital artwork. In the art world, those are something like a pre-assigned environmental limitations to evaluate the creativity of artists.
Rule of compression choice:
This can be said in different ways, like “choice of image extension” or “choice of lossless|lossy algorithm format”. In the digital world, especially regarding visualized data perspective, there is a computer algorithm - Lossless Comparison (https://en.wikipedia.org/wiki/Lossless_compression). Lossless compression is used in cases where it is important that the original and the decompressed data be identical.
My artworks which are named as CryptoPixel are based on the minimum unit of visual interface - pixel (well, at least, back in 1990). GIF (https://en.wikipedia.org/wiki/GIF) was one of the most popular bitmap image formats, since its algorithm gives the most optimized color composition with lightweight file size. Here are the biggest limitations or challenges to artists of the GIF format:
“The format supports up to 8 bits per pixel for each image, allowing a single image to reference its own palette of up to 256 different colors chosen from the 24-bit RGB color space.
In other words, an artist is only able to use 256 colors for his/her artwork. If the artist uses fewer colors or fewer pixels, the size (not dimensional size) of the image will be dramatically reduced. Another artistic benefit or limitation of GIF is an animation. Not many pixel artists can make GIF animation, especially on a small canvas when frames are limited.
So by using GIF’s limitations and possibilities, I have enough reason to keep my own original artwork. The artwork is created by using only the colors that GIF allows - fewer than 256 colors - and also it is animating. The limitation of GIF is also closely tied with encoding and hash-based values, which I use as part of the fingerprint of the artwork.
This is one of the most common hashing algorithms in various areas of the digital industry. Hash is a one-way data mapping. It is non-reversible. The most common area that is using SHA256 among other hash algorithms like MD5 is a web registration. The passwords as human recognizable data are being hashed into machine readable values and stored in DB. For my artwork, I hashed my artwork into string data using SHA256, and stored it in IPFS as one of the metadata. With the limitation of GIF, hashing an image works perfectly. In the artwork, if there is one pixel value change, the hash value will be different.
This is an old-school front-end technique to reduce the size of the image to load on web clients. Basically, with Base64, any binary (like image) can be encoded to ASCII text. This encoded data can be added in HTML image embedding, JS embedding, CSS embedding and XML image embedding to display images with data definition. For instance:
<img src="data:image/gif;base64, R0lGODlhkQ...EAADs=" alt="" />
Base64 encoded data of artwork is added to the metadata set as part of the data that proves the original identity of artwork. Base64 is not enough to give on its own to secure the identity of an artwork or to be used as a source statistical model of image comparison, but it can be used as backup of image url.
So, I think I have described the components of digital images, and now is the time to actually build the data. Let’s take a look one example artwork of mine.
As I mentioned above, for one of the metadata, I created a SHA256 hash of the artwork and used it as part of the file name of artwork.
First, I named the above artwork in this structure.
[index]_[artworkname]_[SHA256 of actual image].gif # or [0-9]{2}_[a-z]+_[a-fA-F0-9]{64}.gif # or 00_newbiegirl_2005f2f28daae6902e58a1d0a7aac30c7e83713e5f5fbd7dff1391c452614524.gif
The index, artwork name and sha256 are in the metadata too. Also, I created another SHA256 hash based on combination of:
Human readable image name(see above example) + Size of image({width px} x {height px}) + Size of image(byte) + SHA256 of image
For instance:
00_newbiegirl_2005f2f28daae6902e58a1d0a7aac30c7e83713e5f5fbd7dff1391c452614524.gif|w145h220|4463|2005f2f28daae6902e58a1d0a7aac30c7e83713e5f5fbd7dff1391c452614524
So, final form of above data is 36f2fe3bcc9bc940c4ac3ba049f577764789ee394e9af2450968a58ab392e787
.
Finally, those metadata are collected in JSON format, so they can be used any computational parsing task.
{ "id": 1, "name": "Newbie Girl", "image": "01_newbiegirl_2005f2f28daae6902e58a1d0a7aac30c7e83713e5f5fbd7dff1391c452614524.gif", "width": "145", "height": "220", "creator": "Chris Youm", "createdate": "Jun 13 07:00:00 2010", "modifydate": "Jun 13 07:00:00 2010", "metadata": { "sha256": "2005f2f28daae6902e58a1d0a7aac30c7e83713e5f5fbd7dff1391c452614524", "base64": "data:image/gif;base64, R0lGODlhkQ...EAADs=,", "datasha256": "36f2fe3bcc9bc940c4ac3ba049f577764789ee394e9af2450968a58ab392e787" } }
Now we have the image, all necessary metadata, and JSON file of the image data. The last thing we need is Ethereum blockchain, Smart Contract and IPFS. Without blockchain and IPFS, granting some sort of metadata to image does not mean anything.
I don’t think I need to explain what the blockchain, Ethereum, Smart Contract, Solidity, decentralization, dApp, ERC-721(Ethereum Request Comments) token or non-fungible token are. Instead, I will explain to you my approach to storing data in Ethereum blockchain via Smart Contract written by Solidity.
Some of you must already know CryptoKitty or CryptoPunks. Yes, CryptoPixel was inspired by those dApps, but yet CryptoPixel is not a level of being called a dApp. I had just completed taking a Solidity online class of CryptoZombie from Loom Network, and I just wanted to build my own.
In the beginning, when I had no clear idea how data is being stored in blockchain, I had many questions, and they bothered me while taking a shower ;)
Should I hardcode all the artworks’ metadata in contract? Can small GIF image be stored in Ethereum blockchain? Should artworks’ metadata be editable? If the artworks’ metadata is same as name of token and symbol of token, then should it be good idea to hardcode in contract? How many tokens should I pre-mine? Should I hardcode amount of token in Contract? Are the metadata of artwork stored dynamically? By whom? Then do I need some sort of admin page? How I can effectively decentralize all the assets? Do the artwork tokens need to be owned by Smart Contracts?
I had been using Truffle and Ganache for Smart Contract, and had been re-writing my Smart Contract a couple of times. Even with various articles and tutorials, I was not clear to answer my own questions. However, through my first Smart Contract, I started to realize what could be the possible answers.
It is a simple dApp and has basic architecture that can be applied to any size of dApp.
I personally think this project with Ethereum Smart Contract can’t even be called as dApp. Since the ERC-721 Smart Contract is already there, I have been using limited functions to create tokens with metadata and pull the stored data from Ethereum blockchain. Transferring a token from wallet to wallet or burning it or any real Eth involved functions and features that are not necessary for this simple project. Maybe later, like CryptoPunks, I will want to create auction-based real dApp.
There are already many tutorials for ERC-20 and ERC-721 and also a standardization project for Smart Contract - https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/token/ERC721
For my project, I created only two Contracts - Cryptopixel.sol and ERC721.sol. The ERC721.sol is already well standardized Contact in Ethereum community, but I had to remove and modify some of variables, modifiers and methods for my purpose.
The ERC721 Contract is to create NFT. I had to modify the ERC721 Contract for minting tokens with the expected type of metadata. In the beginning, I also imported the Ownable Contract, which is for managing ownership of token, but I realized that I should not have any critical features or methods in my first Smart Contract. ;) This project at this stage does not have any functionality of transforming a token’s ownership from my wallet to other wallet.
First comes first: for any kind of token, we need a name, symbol and total amount of token.
// Name of token string constant public name = "CryptoPixel"; // Symbol of Cryptopixel token string constant public symbol = "CPX"; // Number of total characters uint constant internal limitChrt = 52;
In the case of NFT, especially for the token that gives unique identity of existence, hardcoded limited value should be natural. For CryptoPixel, I have about 100 artworks, but decided to use 52 artworks for this project. The 52nd artwork is purely for myself, since it is my face. ;)
Regarding limitChrt, it is constant variable and the value is 52. In ERC-20 and ERC-721 Smart Contract there is a method - totalSupply
function totalSupply() public view returns (uint256 _totalSupply) { return totalSupply; }
This method gets and returns the total supply of tokens held by this Contract. Every time I store my artwork in Ethereum Blockchain, the amount of totalSupply gets increased. I have hardcode the limit of totally supply(limitChrt) to 52; this means once the total supply hits 52, this Smart Contract won’t let executors add more tokens. Here is Solidity Test:
// Testing max token minting function testMintLimit() public { //address addr = cPixel.creatorAddr(); string memory metadata = "[{index:0}]"; // Frist token have minted at testMint, so i start from 1. for (uint i = 1; i <= 51; i++) { cPixel.mintWithMetadata(addr, i, metadata); } // At this point, totalSupply is 52(0~51) uint256 total = cPixel.totalSupply(); Assert.equal(total, 52, "52 token have minted."); // Now, try to mint 53rd token, which has to be impossible. Assert.isFalse(execute('mintOverLimit()'), "Should fail over limit."); }
I am a developer who tries to have a unit test (https://en.wikipedia.org/wiki/Unit_testing) at first before I complete the specific function or method. With Truffle, we have two options for writing pre-automated tests - using pure Solidity and using Mocha/Chai from Node.js. As far as I see, those two have different benefits. In short, Solidity is preferable for unit test and Mocha/Chai for integration test. Check this for further explanation (https://ethereum.stackexchange.com/questions/23565/what-is-the-reason-for-writing-truffle-tests-in-sol-and-js-files)
For the above total Supply test, I tried Mocha/Chai, which has amazing Promise, Async, Await concept from a highly modified JavaScript framework. It was too amazing to complete the unit test code to me, so I gave up and completed unit test in Solidity.
This is my first Smart Contract, and the NFT Smart Contract is already well optimized and reasonably standardized, but still modifying the standardized code is my decision, based on concept of this project. I am a selfish artist and developer who wants to keep reference of my existence somewhere forever. So in the Smart Contract, I hardcoded my Ethereum wallet address and kept all my artworks’ identities in my wallet address.
// This is address of artwork creator address constant public creatorAddr = 0x174B3C5f95c9F27Da6758C8Ca941b8FFbD01d330;
I do not want the creator can be editable (hackable) via Smart Contract by others. This is the same argument as why the ownership of specific crypto should not be changeable by any party except owner. To protect the asset as created and added in the CryptoPixel, we can simply use “require” in mint function like:
require(creatorAddr == _owner);
Again, testing is really important, so here is unit test code:
// Testing mint with wrong address other then creators wallet address function testMintWithWrongAddr() public { Assert.isFalse(execute('mintWithWrongAddr()'), "Should fail minting other than creator."); // Check actual mint happens uint256 expacted = 1; uint256 total = cPixel.totalSupply(); Assert.equal(total, expacted, "1 token have minted."); }
CryptoPixel Smart Contract does not have any functions that need to grant or edit ownership of the asset, since this is truly only for storing artworks’ digital metadata in Blockchain. Later, for CryptoPixel version 2, I may want to add functionalities of auction or simple transfer function.
Every developer is familiar with code review. In work, the code review is somewhat a necessary process that cannot be skipped. In the world of Solidity Smart Contract (in the world of open source), code review or audit is a promise and reputation (at least in my opinion). Even my simple Smart Contract needs an audit. There is no skipping this. There are already many paid Smart Contract audit services, and Concensys also provides a lot of resources that developers can run on their local machine. For my CryptoPixel, I used Smartdec (https://tool.smartdec.net).
https://tool.smartdec.net/scan/be91e68d63d54cb0b3c2a1d5c2c6903a
By doing audit, you will have more chance to experience the standards of Solidity programming. Also, to me it was good chance to go through entire development cycle, even though my Smart Contract is simple.
With Ethereum blockchain, I was able to make what I have been planning for a while a reality, and I have added something meaningful to me in Ethereum blockchain. I hope this project also inspires someone like me as I was inspired by Ethereum community.