IPFS (InterPlanetary File System) adalah sistem file terdistribusi peer-to-peer yang memakai content addressing: file dicari berdasarkan sidik jari isinya, bukan berdasarkan server tempat ia disimpan. Bayangin minta buku ke teman-teman: kamu gak bilang "buku di rak 3 rumah Raka", tapi "buku yang sidik jarinya X". Siapa pun yang punya salinannya boleh memberikan, dan kamu bisa menghitung ulang sidik jarinya untuk memastikan bukunya gak ditukar. Tapi kalau gak ada satu pun yang menyimpan salinan, bukunya hilang.
🧪 Tebakan cepat: metadata KOPI #1 sudah di IPFS dan
tokenURI(1)menunjuk ke sana. Raka mengubah ukuran di metadata itu dari L jadi XL lalu meng-upload ulang. ApakahtokenURI(1)sekarang menunjuk ke isi yang baru? Tebak dulu, jawabannya ada di bagian "Mengganti isi berarti CID baru".
Alamat lokasi vs alamat isi
Di lesson ERC-721, tokenURI(1) adalah https://toko-kaos-raka.test/kopi/1: alamat lokasi. Server itu bisa mati, atau isinya diganti diam-diam, dan pemilik NFT gak bisa tahu. Di IPFS alamatnya adalah CID (Content Identifier), hasil hash dari isi file. Isi sama, CID sama, di komputer mana pun. Ubah satu byte, CID-nya berubah total.
CID: sidik jari isi file
Gambar kaos untuk sertifikat KOPI adalah file SVG 234 byte, kaos-kopi.svg. Raka menghitung CID-nya dengan kubo 0.43.1 (implementasi IPFS dalam Go) secara offline (--offline: gak terhubung ke jaringan, gak ada yang ter-upload):
$ ipfs add --offline --cid-version 1 kaos-kopi.svg
added bafkreiccib257y3ea6vz6yjtteeiftjj6wzohtemafb5w5nccfb4f44n3i kaos-kopi.svg
$ ipfs add --offline kaos-kopi.svg
added QmYpJh9o16QHqf8jaoQRxcfMoTgYJVbPZrSnkHaRn33ZkZ kaos-kopi.svg
Yang pertama CIDv1, yang kedua CIDv0 (masih bawaan kubo kalau gak diberi --cid-version 1). Isinya sama, tapi teksnya beda, jadi pilih satu versi dan konsisten. CIDv1 di atas bisa dibongkar:
| Bagian | Nilai | Artinya |
|---|---|---|
| multibase | b |
sisanya ditulis base32 huruf kecil |
| versi | 0x01 |
CIDv1 |
| codec | 0x55 |
raw: isi file apa adanya |
| multihash | 0x12 0x20 + 32 byte |
SHA-256, panjang 32 byte: 424075df...c2f38dda |
Jadi CID = b + base32(01 55 12 20 + sha256(isi file)). Library multiformats menghitung hal yang sama: CID.create(1, raw.code, await sha256.digest(bytes)). Hasilnya cocok persis dengan kubo.
Metadata KOPI di IPFS
Metadata #1 sekarang menunjuk gambarnya dengan ipfs://, bukan URL server. File kopi/1 (393 byte) berisi:
{
"name": "Kaos Edisi Kopi #1",
"description": "Sertifikat bernomor Kaos Sablon Kopi edisi terbatas Toko Kaos Raka. Satu dari 100.",
"image": "ipfs://bafkreiccib257y3ea6vz6yjtteeiftjj6wzohtemafb5w5nccfb4f44n3i",
"attributes": [
{
"trait_type": "Ukuran",
"value": "L"
},
{
"trait_type": "Nomor",
"value": 1,
"display_type": "number"
}
]
}
Raka menaruh metadata #1 dan #2 di satu folder kopi/, lalu menambahkan folder itu:
$ ipfs add --offline --cid-version 1 -r kopi
added bafkreie2qgfr2dgxygy3jgx3mvcz6bz66sdri6efobmiqwudwrm6b7daeq kopi/1
added bafkreifeyj4c7uw6zdoh3lqnuwcou4q3zmvkdqmvfyo3k5uizp6iaggype kopi/2
added bafybeifuwwkzdzittkxf5ghlmsq2k6jqasyheos6mb3vgz4l73qcmsgfku kopi
Folder juga punya CID (bafybei..., codec dag-pb), dihitung dari nama dan CID file-file di dalamnya. KaosEdisiKopi cukup mengganti satu baris:
function _baseURI() internal pure override returns (string memory) {
return "ipfs://bafybeifuwwkzdzittkxf5ghlmsq2k6jqasyheos6mb3vgz4l73qcmsgfku/";
}
Di chain lokal Hardhat, tokenURI(1) mengembalikan ipfs://bafybeifuwwkzdzittkxf5ghlmsq2k6jqasyheos6mb3vgz4l73qcmsgfku/1. Kenapa gak simpan JSON-nya langsung di contract? Menyimpan 393 byte itu di mapping(uint256 => string) menghabiskan 339.798 gas per token, karena tiap 32 byte storage disimpan oleh semua node. Dengan _baseURI, biaya tambahan per token nol, dan CID tetap menjamin isinya gak bisa diganti.
Mengganti isi berarti CID baru
Ubah "L" jadi "XL", CID kopi/1 berubah total, dan CID folder ikut berubah karena folder menyimpan CID anak-anaknya. _baseURI KaosEdisiKopi adalah teks tetap di bytecode, jadi tokenURI(1) tetap menunjuk ke folder lama dengan ukuran L. Itu jawaban tebakannya. Ini justru jaminannya: pembeli tahu metadata yang ia beli gak bisa diubah diam-diam. Kalau Raka ingin bisa mengubahnya, ia harus menulis fungsi setBaseURI (onlyOwner), dan jaminan itu hilang.
Pinning: tanpa pin, file bisa hilang
Node IPFS cuma wajib menyimpan file yang ia pin. Sisanya boleh dibuang oleh garbage collection. Raka mencoba di node lokalnya:
$ ipfs pin rm bafkreiccib257y3ea6vz6yjtteeiftjj6wzohtemafb5w5nccfb4f44n3i
unpinned bafkreiccib257y3ea6vz6yjtteeiftjj6wzohtemafb5w5nccfb4f44n3i
$ ipfs repo gc
removed bafkreiccib257y3ea6vz6yjtteeiftjj6wzohtemafb5w5nccfb4f44n3i
...
$ ipfs cat --offline bafkreiccib257y3ea6vz6yjtteeiftjj6wzohtemafb5w5nccfb4f44n3i
Error: block was not found locally (offline): ipld: could not find bafkreiccib257y3ea6vz6yjtteeiftjj6wzohtemafb5w5nccfb4f44n3i
Folder kopi masih ter-pin dan ipfs cat .../1 masih jalan, tapi gambarnya hilang. Pin folder cuma menahan file di dalam folder; gambar yang disebut lewat teks "image" gak ikut. Jadi pin gambar dan metadata, di lebih dari satu tempat: node sendiri dan layanan pinning.
Gateway dan ipfs://
Browser biasa belum bisa membuka ipfs://. Gateway menerjemahkannya ke HTTP: https://ipfs.io/ipfs/<CID>/1 atau https://<CID>.ipfs.dweb.link/1. Wallet dan marketplace melakukan itu otomatis. Yang ditulis di contract tetap ipfs://<CID>, bukan URL gateway, supaya NFT-nya gak terikat ke satu perusahaan. Gateway publik juga harus dipercaya; klien seperti @helia/verified-fetch mengecek hash sendiri.
Apa yang dijamin "desentralisasi"
- Dijamin: isi yang kamu terima cocok dengan CID-nya, dan siapa pun yang punya salinan boleh melayaninya.
- Gak dijamin: file tetap ada (harus ada yang pin), file rahasia (siapa pun yang tahu CID bisa membacanya), dan isinya bisa diubah (yang ada cuma CID baru).
Arweave adalah alternatif dengan model lain: jaringan blockchain sendiri tempat kamu membayar sekali di depan untuk penyimpanan jangka panjang, dibiayai dana abadi (endowment) jaringan. Datanya publik dan gak bisa dihapus, alamatnya ID transaksi (ar://...). Cocok untuk metadata yang memang harus permanen, tapi biayanya per byte di muka dan kamu bergantung pada ekonomi jaringan itu.
⚠️ Jebakan yang sering ketemu
- Upload tanpa pin. CID-nya valid, tapi kalau gak ada node yang menyimpan, gambar NFT jadi kosong.
- Pin metadata, lupa gambarnya.
"image": "ipfs://..."cuma teks; pin juga CID gambarnya. - Menulis URL gateway di contract.
https://gateway-x/ipfs/...mati kalau gateway itu tutup; tulisipfs://. - Mengira JSON yang "sama" pasti CID sama. Spasi, urutan key dan baris baru ikut di-hash. Simpan file persis yang kamu upload.
TL;DR: IPFS adalah sistem file terdistribusi peer-to-peer dengan content addressing: CID = b + base32(versi 01, codec, multihash SHA-256 isi file). kubo 0.43.1 masih memberi CIDv0 (Qm...) kecuali --cid-version 1 (bafkrei... untuk file, bafybei... untuk folder). Metadata KOPI ada di folder kopi/ dengan CID bafybeifuwwk..., gambar di ipfs://bafkreiccib..., dan _baseURI mengembalikan ipfs://<CID folder>/, jadi tokenURI(1) = ipfs://.../1. Menyimpan JSON 393 byte di contract butuh 339.798 gas per token; IPFS murah dan CID menjamin isinya gak diganti, tapi cuma selama ada yang pin. Ubah isi berarti CID baru. Tulis ipfs:// di contract dan pakai gateway cuma untuk menampilkan. Arweave menukar biaya di muka dengan penyimpanan jangka panjang.