Oke, halo teman-teman! Selamat datang kembali. Saya di sini, dan kita akan bedah tuntas materi dari "ngobar" (ngoding bareng) kita kemarin.
Di video itu, kita langsung praktik membuat smart contract e-Voting sederhana. Tapi sekarang, saya ingin kamu tidak hanya copy-paste kodenya. Saya ingin kamu memahami filosofi di balik setiap baris kode, mengapa kita melakukannya, dan bagaimana kamu bisa menerapkannya di proyek lain.
Ini bukan sekadar tutorial, ini adalah blueprint cara berpikir seorang smart contract developer. Mari kita bongkar.
🚀 Modul 1: Arsitektur & Peran (Siapa Melakukan Apa)
Setiap smart contract yang baik dimulai bukan dari kode, tapi dari desain arsitektur. Kita harus tentukan siapa saja "aktor" dalam sistem kita dan apa "hak" mereka.
Dalam kasus e-Voting kita, kita membaginya menjadi dua:
- Owner (Pengurus): Ini adalah "admin" atau "Tuhan" dari kontrak kita. Dia punya otoritas terpusat untuk mengelola sistem yang terdesentralisasi.
- Mendeploy kontrak.
- Mendaftarkan kandidat.
- Mengatur status voting (Mulai / Selesai).
- User (Pemilih/Kandidat): Ini adalah publik. Siapapun yang berinteraksi dengan kontrak kita.
- Memilih kandidat (hanya satu kali).
- Melihat daftar kandidat.
- Melihat siapa pemenangnya.
🧠 Sudut Pandang yang Jarang Dilihat: Paradoks Sentralisasi
Banyak orang berpikir smart contract = 100% desentralisasi. Itu salah.
Hampir semua dApps (Aplikasi Terdesentralisasi) yang useful memiliki "titik sentralisasi administratif". Dalam kontrak kita, owner adalah titik itu. Kita mempercayai owner untuk mendaftarkan kandidat yang benar dan memulai/mengakhiri voting secara jujur.
Desain smart contract adalah tentang menyeimbangkan kepercayaan. Kita meminimalkan kepercayaan (kamu tidak perlu percaya pemilih lain tidak curang), tapi kita seringkali masih butuh satu "admin" tepercaya untuk mengelola logic bisnisnya.
👉 Apa yang bisa kamu lakukan sekarang:
Ambil pulpen dan kertas. Sebelum menulis kode untuk proyekmu berikutnya, gambar dua kolom: "Admin/Owner" dan "User/Publik". Tulis semua hak dan larangan untuk masing-masing peran. Ini adalah blueprint arsitekturmu.
🛠️ Modul 2: Fondasi Kontrak (State Variables & Constructor)
Setelah arsitektur jelas, kita siapkan "otak" dari kontrak kita. Kita mulai dengan mendefinisikan state variables (variabel yang menyimpan data di blockchain selamanya) dan constructor.
1. Menentukan "Siapa" Owner-nya
Solidity
address public owner;
Kita membuat variabel owner dengan tipe address dan visibilitas public. Ini otomatis membuat getter function (fungsi untuk melihat) siapa owner-nya.
2. Mengunci Owner Saat Lahir (Constructor)
Solidity
constructor() {
owner = msg.sender;
}
Ini adalah salah satu bagian paling krusial.
- constructor adalah fungsi spesial yang hanya berjalan satu kali, yaitu saat kontrak pertama kali di-deploy.
- msg.sender adalah variabel global yang berisi alamat wallet yang memanggil fungsi.
- Saat deploy, msg.sender-nya adalah kamu (si deployer).
- Dengan kode ini, kita "mengunci" alamat deployer sebagai owner selamanya. Tidak ada yang bisa mengubah ini (kecuali kita tambahkan fungsi untuk transfer ownership, tapi itu cerita lain).
🧠 Sudut Pandang yang Jarang Dilihat: Kelahiran Kontrak
Pikirkan constructor sebagai "akta kelahiran" kontrak. Data yang kamu tetapkan di sini adalah data yang paling fundamental dan paling aman. Saat constructor dieksekusi, msg.sender adalah satu-satunya entitas yang pasti dan tidak bisa dibantah sebagai pencipta kontrak. Inilah momen di mana kamu "mengklaim" kontrak tersebut sebagai milikmu.
👉 Apa yang bisa kamu lakukan sekarang:
Buka Remix. Buat file baru. Tulis address public owner; dan constructor() { owner = msg.sender; }. Deploy kontraknya. Setelah deploy, klik getter function owner di bagian deployed contracts. Kamu akan lihat alamat wallet-mu sendiri. Rasakan "kekuatan" dari constructor itu.
📦 Modul 3: Mendefinisikan Data (Structs & Arrays)
Bagaimana cara kita menyimpan data yang kompleks? Seorang "kandidat" bukan cuma alamat, dia juga punya "jumlah suara". Kita butuh "paket" data.
1. Membungkus Data dengan struct
Solidity
struct Candidate {
address alamatKandidat;
uint256 jumlahSuara;
}
struct adalah template atau "cetakan" data. Kita bilang ke Solidity: "Hei, nanti kalau aku bilang Candidate, maksudku adalah sebuah 'bungkusan' yang isinya ada alamatKandidat dan jumlahSuara."
2. Menyimpan "Bungkusan" dalam Daftar (array)
Solidity
Candidate[] public candidates;
Ini adalah array (daftar) dinamis yang akan menyimpan semua "bungkusan" Candidate kita. Visibilitas public lagi-lagi memberi kita getter function gratis. Ini memungkinkan siapapun "membaca" data kandidat di indeks tertentu (misal, candidates(0) untuk kandidat pertama).
🧠 Sudut Pandang yang Jarang Dilihat: Struct vs. Mapping
Kenapa pakai struct dan array? Kenapa tidak pakai mapping(address => uint256) public voteCounts;?
Bisa! Tapi ada konsekuensinya.
- Jika pakai mapping, kita bisa tahu jumlah suara (value) berdasarkan alamat (key). Tapi kita tidak bisa mendapatkan daftar semua kandidat dengan mudah. mapping tidak bisa di-looping.
- Dengan array dari struct, kita bisa looping. Kita bisa panggil candidates.length untuk tahu total kandidat. Kita bisa ambil candidates[0], candidates[1], dst.
Pilihan struktur data adalah keputusan desain terpenting. Kita memilih array dari struct karena kita butuh bisa di-looping (untuk menentukan pemenang) dan bisa dihitung (untuk totalKandidat).
👉 Apa yang bisa kamu lakukan sekarang:
Pikirkan satu data kompleks lain. Misalnya, "Mobil" (string merek, uint tahun, address pemilik). Coba tuliskan struct Mobil dan buat array Mobil[] public garasi; di Remix.
🛡️ Modul 4: Gerbang Kontrol (Require & Error Handling)
Sekarang kita buat fungsinya. Tapi fungsi tanpa keamanan adalah bencana. Kita butuh "bouncer" di depan pintu setiap fungsi yang sensitif.
1. Bouncer "Hanya Owner"
Solidity
function tambahKandidat(address _adr) public {
// Bouncer: Cek dulu dia owner bukan?
require(msg.sender == owner, "Anda bukan owner!");
// Lolos? Baru jalankan logika
// ... (logika tambah kandidat)
}
require(kondisi, "pesan error") adalah penjaga gerbangmu.
- Dia akan mengecek kondisi.
- Jika true, fungsi lanjut berjalan.
- Jika false, fungsi langsung berhenti, semua perubahan dibatalkan (transaksi di-revert), dan "pesan error" dikembalikan.
Ini adalah defense mechanism utama kita untuk memastikan hanya owner yang bisa menambah kandidat.
2. Bouncer "Belum Pernah Voting"
Kita akan butuh ini nanti di fungsi vote:
Solidity
mapping(address => bool) public userSudahMemilih;
function vote(uint _index) public {
// ... (cek lain)
// Bouncer 2: Cek dia sudah pernah milih belum?
require(userSudahMemilih[msg.sender] == false, "Anda sudah voting!");
// ... (logika voting)
}
🧠 Sudut Pandang yang Jarang Dilihat: Fail Fast, Fail Loud
Prinsip error handling di Solidity adalah "Fail Fast, Fail Loud" (Cepat Gagal, Gagal dengan Keras).
- Fail Fast: Letakkan require di baris paling atas dari fungsimu. Jangan biarkan kode lain berjalan jika kondisinya tidak terpenuhi. Ini menghemat gas dan mencegah bug yang tidak terduga.
- Fail Loud: Pesan error ("Anda bukan owner!") sangat penting untuk debugging dan memberi tahu pengguna apa yang salah.
Jangan pernah membuat fungsi publik tanpa require. Itu seperti meninggalkan pintu rumahmu terbuka lebar.
Catatan: Di video kita pakai require berulang. Cara yang lebih "advance" dan bersih adalah menggunakan modifier, yang akan kita pelajari nanti. Tapi require adalah dasarnya.
👉 Apa yang bisa kamu lakukan sekarang:
Lihat fungsi tambahKandidat yang kamu buat. Coba panggil fungsi itu dari wallet yang bukan owner di Remix. Lihat transaksinya gagal dan pesan error "Anda bukan owner!" muncul di konsol.
📈 Modul 5: Logika Bisnis (Function & State Management)
Ini adalah "daging"-nya. Bagaimana kita mengubah data di blockchain?
1. Menambah Kandidat (Mengubah Array)
Solidity
function tambahKandidat(address _adr) public {
require(msg.sender == owner, "Anda bukan owner!");
// ... (cek kandidat terdaftar)
// Logika Inti:
// 1. Buat "bungkusan" baru
Candidate memory kandidatBaru = Candidate(_adr, 0);
// 2. Dorong ke daftar
candidates.push(kandidatBaru);
}
- Kita membuat Candidate baru di memory (penyimpanan sementara) dengan alamat _adr dan 0 suara.
- candidates.push() adalah cara kita menambahkan item baru ke array candidates. Ini adalah state change (perubahan data) yang akan dicatat di blockchain.
2. Melakukan Voting (Mengubah Struct di dalam Array)
Solidity
function vote(uint _index) public {
// ... (require checks)
// Logika Inti:
// 1. Tambah jumlah suara
candidates[_index].jumlahSuara++; // atau += 1
// 2. Tandai bahwa dia sudah memilih
userSudahMemilih[msg.sender] = true;
}
- candidates[_index] memberi kita akses ke struct kandidat di indeks yang dipilih.
- .jumlahSuara++ adalah "jantung" dari voting. Kita mengubah data di dalam struct itu.
- userSudahMemilih[msg.sender] = true; adalah state change kedua. Kita "mencatat" di mapping bahwa wallet ini sudah selesai memilih.
🧠 Sudut Pandang yang Jarang Dilihat: Gas & Efisiensi
Setiap state change (seperti .push() atau ++ atau mengubah mapping) membutuhkan GAS. Ini adalah "biaya" untuk menulis di blockchain.
- Menambah kandidat (pakai .push()) itu relatif mahal.
- Menambah suara (pakai ++) itu lebih murah.
- Mengubah mapping (userSudahMemilih = true) juga relatif mahal (karena kita mengubah 0 menjadi 1 untuk pertama kalinya).
Inilah kenapa kita harus efisien. Kita tidak boleh boros gas. Desain kontrak yang baik adalah desain yang gas-efficient.
👉 Apa yang bisa kamu lakukan sekarang:
Deploy kontrakmu. Tambahkan 2 kandidat. Lalu, dari wallet User, panggil fungsi vote(0). Lihat transaksinya sukses. Coba panggil vote(0) lagi. Lihat transaksinya gagal dengan pesan "Anda sudah voting!". Kamu baru saja menguji state change dan error handling.
🔐 Modul 6: Mencegah Kecurangan (Mapping & Status Flag)
Ini adalah bagian favorit saya. Bagaimana kita memastikan fairness?
1. Mencegah Voting Ganda (mapping)
Solidity
mapping(address => bool) public userSudahMemilih;
Kenapa mapping? Seperti yang dibahas di Modul 3, mapping itu super cepat untuk lookup.
Saat vote dipanggil, kita cek userSudahMemilih[msg.sender]. Ini seperti bertanya ke database raksasa: "Hei, alamat 0x... ini sudah ada di daftar 'Sudah Memilih' belum?" Jawabannya instan (true or false).
Jika kita pakai array untuk menyimpan siapa saja yang sudah memilih, kita harus looping seluruh array setiap kali ada yang mau vote. Itu akan sangat lambat dan mahal!
2. Mencegah Kandidat Ganda (mapping)
Solidity
mapping(address => bool) public kandidatTerdaftar;
Ini kita gunakan di tambahKandidat untuk memastikan owner tidak mendaftarkan alamat yang sama dua kali. Logikanya sama: require(kandidatTerdaftar[_adr] == false, "Kandidat sudah terdaftar!");
3. Mengatur Waktu Voting (bool flag)
Solidity
bool public sudahBisaVoting; // Default-nya false
function aturStatusVoting(bool _status) public {
require(msg.sender == owner, "Anda bukan owner!");
sudahBisaVoting = _status;
}
function vote(uint _index) public {
require(sudahBisaVoting == true, "Belum waktunya voting!");
// ...
}
Ini adalah "saklar" utama. owner bisa "menyalakan" (true) voting saat waktunya tiba, dan "mematikan" (false) saat voting selesai. Fungsi vote hanya akan bekerja jika saklarnya "ON".
🧠 Sudut Pandang yang Jarang Dilihat: State Machine
Selamat! Kamu baru saja membuat sebuah "State Machine" (Mesin Status).
Kontrakmu memiliki beberapa "status":
- Preparation (sudahBisaVoting == false): Owner bisa tambahKandidat. User tidak bisa vote.
- Voting Active (sudahBisaVoting == true): Owner tidak boleh tambahKandidat (harusnya kita tambahkan require ini!). User bisa vote.
- Voting Ended (sudahBisaVoting == false lagi): Tidak ada yang bisa vote. Saatnya siapaPemenang dipanggil.
Mendesain smart contract adalah mendesain transisi antar status ini dengan aman.
👉 Apa yang bisa kamu lakukan sekarang:
Implementasikan saklar sudahBisaVoting.
- Deploy kontrak.
- Langsung coba vote(0) dari wallet User. Pastikan gagal (karena status masih false).
- Dari wallet owner, panggil aturStatusVoting(true).
- Dari wallet User, coba vote(0) lagi. Pastikan berhasil.
- Dari wallet owner, panggil aturStatusVoting(false).
- Dari wallet User lain, coba vote(0). Pastikan gagal lagi.
Kamu baru saja mengendalikan "State Machine"!
🏆 Modul 7: Menentukan Pemenang (Looping & View Function)
Setelah semua selesai, bagaimana kita tahu siapa yang menang?
1. Fungsi view (Hanya Membaca)
Solidity
function siapaPemenang() public view returns (Candidate memory) {
// ... (logika mencari pemenang)
}
Perhatikan kata kunci view. Ini berarti fungsi ini hanya membaca data dari blockchain, tidak mengubah apapun. Karena tidak mengubah, memanggil fungsi view itu GRATIS (tidak butuh gas).
2. Logika Looping
Solidity
function siapaPemenang() public view returns (Candidate memory) {
// ... (require cek)
uint suaraTertinggi = 0;
uint indexPemenang = 0;
for (uint i = 0; i < candidates.length; i++) {
if (candidates[i].jumlahSuara > suaraTertinggi) {
suaraTertinggi = candidates[i].jumlahSuara;
indexPemenang = i;
}
}
return candidates[indexPemenang];
}
Ini adalah algoritma sederhana:
- Buat variabel sementara suaraTertinggi (mulai dari 0) dan indexPemenang (mulai dari 0).
- Looping dari kandidat pertama (i = 0) sampai terakhir.
- Di setiap kandidat, tanya: "Apakah jumlahSuara kandidat ini lebih besar dari suaraTertinggi saya saat ini?"
- Jika YA: Update suaraTertinggi dengan suara baru ini, dan catat indexPemenang-nya adalah i.
- Jika TIDAK: Lanjut ke kandidat berikutnya.
- Setelah loop selesai, indexPemenang akan berisi index dari kandidat dengan suara terbanyak.
- Kembalikan (return) data kandidat di index tersebut.
(Catatan: Di video, kita me-return address dan uint. Me-return seluruh struct seperti ini lebih rapi).
🧠 Sudut Pandang yang Jarang Dilihat: Bahaya Looping
Looping di Solidity itu berbahaya dan mahal.
Kenapa? Setiap iterasi loop memakan gas.
Di fungsi siapaPemenang, ini aman karena dia view, jadi gratis. Tapi bayangkan jika ini fungsi yang mengubah data (bukan view). Jika array candidates kita punya 10.000 kandidat, looping sebanyak itu bisa menghabiskan gas lebih banyak dari batas block (transaksi akan selalu gagal).
Aturan Emas: Hindari looping pada array yang ukurannya bisa bertambah tanpa batas di dalam fungsi yang mengubah state. Untuk fungsi view, masih oke, tapi off-chain script (seperti JavaScript) yang memanggil getter satu per satu seringkali lebih baik.
👉 Apa yang bisa kamu lakukan sekarang:
Setelah beberapa wallet melakukan vote, panggil fungsi siangPemenang. Lihat hasilnya. Pastikan alamat dan jumlah suara yang muncul sesuai dengan kandidat yang seharusnya menang.
✅ CHECKLIST: Waktunya Praktik!
Kamu sudah dapat insight-nya, sekarang ini actionable steps-nya. Jangan cuma dibaca!
- [ ] Buka Remix: Buka remix.ethereum.org.
- [ ] Buat File: Buat file baru Evoting.sol.
- [ ] Tulis Fondasi: Tulis pragma, contract Evoting, address public owner, dan constructor.
- [ ] Definisikan Data: Tulis struct Candidate dan Candidate[] public candidates.
- [ ] Buat Gerbang Status: Tambahkan bool public sudahBisaVoting dan mapping(address => bool) public userSudahMemilih.
- [ ] Kode Fungsi Owner: Buat fungsi tambahKandidat(address) dan aturStatusVoting(bool). Jangan lupa require(msg.sender == owner, ...) di keduanya.
- [ ] Kode Fungsi User: Buat fungsi vote(uint _index).
- [ ] Tambahkan SEMUA Bouncer:
- Di vote:
- require(sudahBisaVoting == true, ...)
- require(_index < candidates.length, "Kandidat tidak ditemukan!")
- require(userSudahMemilih[msg.sender] == false, "Anda sudah voting!")
- require(msg.sender != owner, "Owner tidak bisa voting!") (Ini bagus untuk ditambahkan!)
- Di tambahKandidat:
- require(sudahBisaVoting == false, "Voting sudah/sedang berjalan!")
- (Opsional) Tambahkan mapping kandidatTerdaftar dan require-nya.
- Di vote:
- [ ] Kode Fungsi Pemenang: Buat fungsi siapaPemenang() public view ... lengkap dengan logikanya.
- [ ] Uji Skenario (PENTING!):
- Deploy kontrak (kamu jadi owner).
- Tes 1 (Owner): Panggil tambahKandidat dengan alamat wallet ke-2. Panggil tambahKandidat dengan alamat wallet ke-3.
- Tes 2 (User Gagal): Ganti wallet ke-2. Coba vote(0). Pastikan GAGAL ("Belum waktunya voting!").
- Tes 3 (Owner): Ganti wallet ke owner. Panggil aturStatusVoting(true).
- Tes 4 (User Sukses): Ganti wallet ke-2. Panggil vote(0). Pastikan SUKSES.
- Tes 5 (User Gagal 2): Masih di wallet ke-2. Coba vote(1). Pastikan GAGAL ("Anda sudah voting!").
- Tes 6 (User Lain): Ganti wallet ke-3. Panggil vote(0). Pastikan SUKSES. (Sekarang kandidat 0 punya 2 suara).
- Tes 7 (Owner): Ganti wallet ke owner. Panggil aturStatusVoting(false).
- Tes 8 (Cek Hasil): Panggil siapaPemenang(). Pastikan hasilnya adalah alamat wallet ke-2 dengan 2 suara.
Jika kamu berhasil menyelesaikan 10 langkah ini, kamu tidak hanya tahu cara membuat kontrak e-Voting. Kamu paham cara membuatnya.
Semangat terus, builder! Ini baru permulaan. Kalau ada pertanyaan, DM saya di Instagram atau komen di video. Saya tunggu story Instagram kamu yang tag saya, menunjukkan kamu berhasil deploy kontrak ini.
Mari kita terus ngobar dan bangun masa depan Web3!
🧠 Glosarium Cerdas: Dari Awam Menjadi Ahli
Bagian 1: Fondasi & Lingkungan
1. Smart Contract (Kontrak Cerdas)
- Penjelasan Teknis: Sebuah program komputer yang berjalan di atas blockchain, yang otomatis mengeksekusi, mengontrol, atau mendokumentasikan peristiwa dan tindakan sesuai dengan ketentuan kontrak atau perjanjian.
- Analogi Mudah: Pikirkan Smart Contract bukan sebagai "kontrak" (dokumen), tapi sebagai Mesin Penjual Otomatis (Vending Machine) yang Super Canggih.
- Studi Kasus (e-Voting):
- Aturannya sudah jelas tertulis di depan mesin (kodenya publik).
- Jika kamu (User) memasukkan koin yang tepat (memanggil fungsi vote dengan benar), mesin pasti akan mencatat pilihanmu (jumlahSuara++).
- Tidak ada "penjaga mesin" yang bisa kamu sogok untuk memberimu 2 suara. Mesin itu berjalan sendiri berdasarkan aturan.
- Mesin ini transparan, semua orang bisa lihat logic-nya.
2. Remix IDE
- Penjelasan Teknis: Integrated Development Environment (Lingkungan Pengembangan Terpadu) berbasis web untuk menulis, meng-compile, men-deploy, dan men-debug smart contract Solidity.
- Analogi Mudah: Ini adalah Dapur Uji Coba (Test Kitchen) All-in-One milikmu.
- Studi Kasus (e-Voting):
- Kamu tidak perlu membangun dapur dari nol. Remix sudah menyediakan "kompor" (compiler), "panci" (editor kode), dan "bahan makanan palsu" (akun-akun dummy) untuk menguji resep e-Voting kamu sebelum disajikan ke publik (di-deploy).
3. Deploy (Menyebarkan)
- Penjelasan Teknis: Proses mengambil kode smart contract yang sudah di-compile dan menanamkannya secara permanen ke blockchain sehingga memiliki alamat (Address) yang unik dan bisa diajak berinteraksi.
- Analogi Mudah: Ini adalah momen "Grand Opening" Toko Barumu.
- Studi Kasus (e-Voting):
- Selama di Remix (dapur), kontrakmu hanyalah "resep".
- Saat kamu klik "Deploy", kamu "membangun toko" (kontrak) di "jalan raya" (blockchain). Toko itu kini resmi dibuka, punya "alamat" (Address) unik, dan siapa pun bisa mengunjunginya.
Bagian 2: Aktor & Identitas
4. Address (Alamat)
- Penjelasan Teknis: Serangkaian karakter unik (diawali 0x...) yang mengidentifikasi sebuah akun (wallet) atau smart contract di blockchain.
- Analogi Mudah: Ini adalah Nomor Rekening Bank Digital-mu.
- Studi Kasus (e-Voting):
- Setiap pemilih punya "nomor rekening" (Address) unik.
- Kontrak e-Voting kita yang sudah di-deploy juga punya "nomor rekening" (Address) sendiri.
- Kamu "mentransfer" suaramu ke alamat kontrak tersebut.
5. msg.sender (Message Sender)
- Penjelasan Teknis: Sebuah variabel global dalam Solidity yang berisi address dari akun atau kontrak yang saat ini sedang memanggil (mengirim "pesan") ke fungsi tersebut.
- Analogi Mudah: Ini adalah "Caller ID" (ID Pemanggil) di Telepon Genggam.
- Studi Kasus (e-Voting):
- Saat kamu memanggil fungsi vote, kontrak langsung melihat "Caller ID"-nya. "Oh, yang menelepon adalah 0x123...."
- Kita lalu menggunakan ID ini untuk mengecek: require(userSudahMemilih[msg.sender] == false, ...) yang artinya "Cek di buku catatan, apakah ID pemanggil ini sudah pernah menelepon (voting) sebelumnya?"
6. Constructor
- Penjelasan Teknis: Sebuah fungsi opsional yang spesial, yang hanya berjalan satu kali dan tidak bisa dipanggil lagi, yaitu pada saat kontrak di-deploy.
- Analogi Mudah: Ini adalah "Akta Kelahiran" atau "Upacara Peresmian" kontrak.
- Studi Kasus (e-Voting):
- Saat grand opening (deploy), ada "upacara potong pita".
- constructor() { owner = msg.sender; }
- Artinya: "Siapapun (msg.sender) yang memotong pita (men-deploy kontrak), otomatis DITETAPKAN sebagai owner selamanya." Ini hanya terjadi satu kali saat kelahiran kontrak.
Bagian 3: Penyimpanan Data (Si "Otak")
7. Struct (Struktur)
- Penjelasan Teknis: Tipe data kustom yang kompleks, yang memungkinkan kita untuk mengelompokkan beberapa variabel (dari tipe yang berbeda) di bawah satu nama.
- Analogi Mudah: Ini adalah "Formulir Data Diri" atau "Kartu Nama".
- Studi Kasus (e-Voting):
- Daripada datanya berceceran (misal: 10 variabel alamat, 10 variabel suara), kita buat "formulir" struct Candidate.
- Setiap Candidate adalah satu "formulir" utuh yang berisi: alamatKandidat dan jumlahSuara. Jauh lebih rapi.
8. Array
- Penjelasan Teknis: Sebuah koleksi data dinamis (atau berukuran tetap) yang menyimpan data-data dengan tipe yang sama secara berurutan.
- Analogi Mudah: Ini adalah "Daftar Absen" atau "Rak Buku".
- Studi Kasus (e-Voting):
- Candidate[] public candidates; adalah "rak buku" kita.
- Setiap struct Candidate (formulir) adalah "buku"-nya.
- candidates.push(...) artinya kita "menambahkan buku baru ke rak".
- candidates[0] artinya kita "mengambil buku di urutan pertama".
- candidates.length artinya kita "menghitung jumlah buku di rak".
9. Mapping
- Penjelasan Teknis: Struktur data key-value (kunci-nilai). Kamu memberikan "kunci" dan ia akan langsung memberikan "nilai"-nya. Sangat efisien untuk lookup.
- Analogi Mudah: Ini adalah "Buku Telepon" atau "Indeks di Belakang Buku Teks".
- Studi Kasus (e-Voting):
- mapping(address => bool) public userSudahMemilih;
- Berbeda dengan "Daftar Absen" (Array) di mana kamu harus mencari nama satu per satu dari atas ke bawah.
- "Buku Telepon" (Mapping) ini memungkinkan kita:
- Kunci (Key): Masukkan address (nomor rekening) pemilih.
- Nilai (Value): Langsung dapat jawaban true (sudah memilih) atau false (belum).
- Ini super cepat dan efisien.
Bagian 4: Aturan & Eksekusi
10. Require
- Penjelasan Teknis: Sebuah fungsi error-handling yang mengecek sebuah kondisi. Jika kondisi false, ia akan menghentikan eksekusi fungsi, membatalkan semua perubahan (revert), dan mengembalikan pesan error.
- Analogi Mudah: Ini adalah "Satpam" atau "Bouncer" di Pintu Klub.
- Studi Kasus (e-Voting):
- Saat kamu panggil fungsi vote, kamu akan dicegat "Satpam" require ini.
- Satpam 1: require(sudahBisaVoting, ...) -> "Maaf, klub belum buka." (Gagal, diusir).
- Satpam 2: require(!userSudahMemilih[msg.sender], ...) -> "Hei, kamu tadi sudah masuk! Tidak boleh masuk lagi." (Gagal, diusir).
- Satpam 3: require(msg.sender != owner, ...) -> "Maaf, panitia (owner) dilarang ikut joget (voting)." (Gagal, diusir).
- Jika lolos semua, kamu baru boleh masuk (menjalankan sisa kode).
11. Function (public vs. view)
- Penjelasan Teknis: public adalah fungsi yang bisa dipanggil oleh siapa saja (user lain atau kontrak lain) dan bisa mengubah data (state). view adalah fungsi yang hanya "melihat" data, tidak bisa mengubah, dan bisa dipanggil gratis.
- Analogi Mudah: Bandingkan "Tombol" dengan "Jendela Kaca" di Vending Machine.
- Studi Kasus (e-Voting):
- function vote() (public): Ini adalah "Tombol" untuk memilih. Menekannya akan mengubah data (jumlah suara bertambah) dan ini butuh biaya (Gas).
- function siapaPemenang() (view): Ini adalah "Jendela Kaca". Kamu bisa melihat siapa yang menang (atau berapa total kandidat) kapan saja. Karena cuma "melihat" dan tidak mengubah isi mesin, ini GRATIS.
12. Gas
- Penjelasan Teknis: Biaya komputasi yang diperlukan untuk melakukan transaksi atau mengeksekusi smart contract di blockchain Ethereum (atau yang kompatibel).
- Analogi Mudah: Ini adalah "Bensin" untuk Transaksimu.
- Studi Kasus (e-Voting):
- Memanggil fungsi vote (mengubah data) itu seperti menjalankan mobil -> Butuh Bensin (Gas).
- Memanggil fungsi siapaPemenang (hanya melihat data) itu seperti melihat mobil di parkiran -> Tidak butuh Bensin (Gratis).
- Jika kodemu boros (misalnya looping tidak efisien), kamu butuh "bensin" lebih banyak.
👨🏫 Cara Mengajarkan Ini: Teknik "Feynman" untuk Mengunci Ilmu
Cara terbaik untuk memastikan kamu paham adalah dengan mencoba mengajarkannya. Ini adalah template yang bisa kamu gunakan untuk menjelaskan materi e-Voting ini ke teman, rekan kerja, atau bahkan ke "bebek karet" di mejamu.
Judul Sesi Ajar: "Bongkar Logika Vending Machine: Cara Kerja Smart Contract Pemilu"
Tujuanmu: Jangan buat mereka hafal kodenya. Buat mereka paham logika, keamanan, dan trade-off-nya.
Langkah 1: Mulai dengan "Mengapa", Bukan "Apa"
"Pernah kepikiran nggak, kenapa pemilu konvensional itu ribet dan rawan curang? Kita harus percaya sama KPU, percaya sama penghitung suara, dll. Nah, gimana kalau kita bisa bikin sistem pemilu di mana 'aturannya' itu adalah 'hukum' yang tidak bisa dilanggar, transparan, dan berjalan otomatis? Itulah yang kita buat dengan Smart Contract."
Langkah 2: Kenalkan Para Aktor (Arsitektur)
"Di sistem kita, cuma ada 2 'aktor':
- Panitia (Owner): Tugasnya cuma 3: Membuka pendaftaran (deploy), mendaftarkan kandidat, dan mengatur 'saklar' kapan voting mulai/selesai.
- Pemilih (User): Tugasnya 3 juga: Melihat kandidat, memilih (hanya 1x), dan melihat siapa pemenangnya.
Kerennya, 'Panitia' pun tidak bisa curang. Dia tidak bisa menambah suara kandidat A. Dia tidak bisa memilih. Dia cuma 'administrator'."
Langkah 3: Fokus pada 2 Konsep Kunci: Mapping vs. Require (Si Pencatat & Si Satpam)
"Ini bagian paling jeniusnya.
- Gimana caranya sistem tahu kita belum pernah memilih? Dia tidak pakai 'Daftar Absen' (Array) yang lama. Dia pakai 'Buku Telepon' (Mapping). Dia tinggal masukkan 'nomor rekening' (Address) kita, dan langsung dapat jawaban: true (sudah) atau false (belum). Super cepat.
- Terus, siapa yang jaga sistem ini? Namanya 'Satpam' (Require). Setiap kamu mau vote, kamu dicegat dulu sama Satpam ini. Dia bakal cek: 'Voting sudah dibuka?', 'Kamu sudah pernah milih?', 'Kamu bukan panitia kan?'. Kalau satu aja gagal, kamu diusir. Transaksi batal. Ini yang bikin sistemnya aman."
Langkah 4: Tunjukkan Skenario "Gagal" (Ini Paling Penting!)
Buka Remix. Jangan tunjukkan skenario sukses dulu.
- Deploy kontrak sebagai owner.
- Ganti akun ke user.
- Coba vote(0). TUNJUKKAN ERRORNYA. "Lihat, gagal. Kenapa? 'Belum waktunya voting'. Sistemnya bekerja!"
- Ganti ke owner. Daftarkan 1 kandidat.
- Ganti ke user. Coba vote(0). TUNJUKKAN ERRORNYA LAGI. "Masih gagal. Kan 'saklarnya' (status voting) belum dinyalakan sama owner."
- Ganti ke owner. Nyalakan saklar aturStatusVoting(true).
- Ganti ke user. Coba vote(0). "Nah, BARU berhasil."
- Coba vote(0) lagi. TUNJUKKAN ERRORNYA LAGI. "Gagal! 'Anda sudah voting'. Sistemnya mencatat kita!"
Dengan menunjukkan kegagalan terlebih dahulu, orang akan "ngeh" bahwa default dari sistem ini adalah aman dan terkunci.
Langkah 5: Penutupan dengan "What If"
Tutup dengan pertanyaan untuk memancing pikiran mereka:
- "Gimana ya caranya biar owner bisa mendelegasikan tugas tambahKandidat ke 'admin' lain?" (Jawab: Butuh role-based access control).
- "Gimana kalau kita mau pemilihnya harus 'terdaftar' dulu, nggak semua orang boleh milih?" (Jawab: Butuh whitelist, pakai mapping lagi!).
- "Apa kelemahan sistem ini?" (Jawab: Transparansi. Semua orang bisa lihat si A memilih siapa. Itu masalah untuk pemilu nyata. Berarti kita butuh privacy layer).
Dengan melakukan ini, kamu tidak hanya mengajar, kamu memimpin sebuah diskusi desain sistem. Dan itulah cara ilmu ini benar-benar menempel di kepalamu.