Belajar Blockchain

Sumber: Build a Blockchain from Scratch in Go (2021) - By Lukas

Mengapa Memulai Pengembangan Blockchain?

Dua tahun lalu, saya berada di persimpangan jalan dalam karir saya. Saya sedang mencari pekerjaan pemrograman saya berikutnya setelah lima tahun berkecimpung dalam pengembangan PHPPHP di trivago.

Saya sedang memilih antara posisi JavaJava di NewRelic atau posisi GoLangGoLang di sebuah startup blockchainblockchain baru. Yap. Saya memilih GoGo dan blockchainblockchain. Mengapa? Karena itu lebih baik untuk karir jangka panjang saya. Dan saya merekomendasikan Anda untuk melakukan hal yang sama.

Jika saya melanjutkan pengembangan JavaJava, saya akan terus memprogram monolithsmonoliths, sesekali mempelajari sesuatu yang baru. Sebagian besar waktu, saya akan memperbaiki bugbug, dan mungkin mengimplementasikan fitur bagus setiap dua sprintsprint sekali. Paling bagus, itu akan menjadi pilihan yang baik, aman, dan menarik, tetapi sedikit lambat untuk selera pribadi saya.

Paling buruk, saya akan mengerjakan microservicesmicro−services yang rusak :) (Hanya bercanda, microservicesmicro−services sangat bagus untuk melakukan scalingscaling pada bagian-bagian individual dari proyek dan membagi kepemilikan di antara tim-tim yang independen.)

Selain peluang yang menarik, ada juga aspek finansial dalam setiap pekerjaan. Kita semua punya tagihan yang harus dibayar. Di dunia Java/PHP/JavascriptJava/PHP/Javascript, saya akan bersaing untuk kenaikan gaji dengan 10 juta developerdeveloper JavaJava lainnya, yang masing-masing memiliki pengalaman 20 tahun. Tentu saja bisa dilakukan, tetapi ini adalah perjuangan berat yang semakin sulit dari hari ke hari.

WebWeb 3.0 akan datang. Dalam sepuluh tahun terakhir, kita telah membantu membangun platform besar untuk komunikasi massa. Ingin update kehidupan? FacebookFacebook. Punya cerita? MediumMedium. Mau berbagi foto? InstagramInstagram. Sayangnya, platform-platform ini memiliki sisi gelap. Dan sisi gelap inilah yang menjadi kekuatan dari seluruh model bisnis mereka. Pengguna telah menyerahkan kedaulatan data mereka dan kehilangan perhatian karena iklan.

Tetapi tidak harus seperti ini.

Apakah Anda ingat ketika RSSRSS menjadi raja, dan semua orang punya blog? Web3.0Web3.0 bertujuan untuk membangun kembali dan menyempurnakan visi ini. Bergabunglah dengan saya dan jelajahi ekonomi berbasis tokentoken (tokenized economies) baru yang mengoptimalkan pertukaran nilai antar partisipan dengan sepenuhnya menghilangkan perantara. Pelajari sifat kekal blockchainblockchain (immutability), gabungkan dengan kriptografi asimetris (asymmetric cryptography), dan kembangkan sistem baru yang transparan dan open-source yang dapat dipercaya oleh pengguna sambil mencegah pembobolan data yang tersentralisasi.

Buku ini akan menunjukkan kepada Anda persis tentang hal itu.


Temui karakter utama buku ini. Andrej.

Andrej adalah pemilik bar di malam hari dan seorang pengembang perangkat lunak di siang hari di sebuah kota kecil Slovakia bernama Bardejov.

Andrej lelah dengan:

  • Memprogram aplikasi PHP/JavaPHP/Java yang solid dan kuno.

  • Lupa berapa banyak uang yang dihutangi oleh teman dan kliennya untuk semua minuman vodka di malam Jumat yang belum dibayar.

  • Menghabiskan waktu mengumpulkan dan menghitung koin, mengembalikan uang kembalian, dan secara umum menyentuh uang kertas yang mungkin terkontaminasi COVID-19.

  • Memelihara berbagai kepingan plastik dan tokentoken untuk sepak bola meja, dartsdarts, biliar, dan pokerpoker.

Andrej ingin sekali:

  • Memiliki riwayat aktivitas dan penjualan bar yang dapat diaudit dengan sempurna agar barnya sesuai dengan peraturan pajak.

  • Mengubah barnya menjadi lingkungan yang otonom, efisien dalam pembayaran, dan aman yang dapat dipercaya oleh pelanggannya.

"Ini akan menjadi mimpi dalam pemrograman!" katanya pada dirinya sendiri. "Saya akan menulis program sederhana dan menyimpan semua saldo klien saya dalam bentuk virtual."

"Setiap pelanggan baru akan memberi saya uang tunai, dan saya akan memberi mereka kredit tokentoken digital saya dalam jumlah yang setara. TokenToken tersebut akan mewakili unit moneter di dalam dan di luar bar."

"Para pengguna akan menggunakan tokentoken tersebut untuk semua fungsionalitas bar, mulai dari membayar minuman, meminjamkan dan meminjamnya kepada teman-teman mereka, hingga bermain tenis meja, pokerpoker, dan kickerkicker (sepak bola meja)."

"Saya akan menamainya: TokenToken The Blockchain Bar, TBBTBB!"


Memulai

Buku ini ditulis dalam bahasa GoGo, tetapi jangan khawatir - Anda tidak perlu memiliki pengalaman GoGo sebelumnya untuk mulai membaca buku ini. Ini adalah bahasa yang sangat kuat dan ramah bagi pemula, dan Anda akan cepat menguasainya.

Persyaratan

Saya merekomendasikan Anda memiliki pengalaman pemrograman 2+ tahun di JavaJava / PHPPHP / JavascriptJavascript, atau bahasa lain yang mirip dengan GoGo.

Mengapa Go?

Karena seperti blockchainblockchain, ini adalah teknologi yang fantastis untuk karir pemrograman Anda secara keseluruhan. GoGo adalah bahasa yang sedang tren dan dibayar lebih baik daripada posisi Java/PHPJava/PHP pada umumnya.

GoGo dioptimalkan untuk arsitektur CPUCPU multicoremulti−core. Anda dapat menjalankan (spawn) ribuan threadthread ringan (GoroutinesGo−routines) tanpa masalah. Ini sangat praktis untuk perangkat lunak yang sangat paralel dan konkuren seperti jaringan blockchainblockchain. Dengan menulis perangkat lunak Anda dalam GoGo, Anda mencapai performa yang mendekati level C++C++ secara langsung tanpa harus bersusah payah karena lupa membebaskan memori (free-up memory).

GoGo juga mengkompilasi menjadi binarybinary, yang membuatnya sangat portabel.

Tentu, ini adalah kelanjutan terjemahannya.


Pengguna 1, Andrej

Senin, 18 Maret.

Andrej mengenerate 1 juta utilityutility tokentoken.

Di dunia blockchainblockchain, tokentoken adalah unit di dalam databasedatabase blockchainblockchain. Nilai riilnya dalam dolar atau euro berfluktuasi berdasarkan permintaan dan popularitasnya.

Setiap blockchainblockchain memiliki sebuah file “Genesis”. File Genesis digunakan untuk mendistribusikan tokentoken pertama kepada para partisipan awal blockchainblockchain.

Semuanya dimulai dengan file genesis.jsongenesis.json yang sederhana sebagai contoh.

Andrej membuat file ./database/genesis.json di mana ia mendefinisikan bahwa databasedatabase The Blockchain Bar akan memiliki 1 juta tokentoken dan semuanya akan menjadi milik Andrej:

{
  "genesis_time": "2019-03-18T00:00:00.000000000Z",
  "chain_id": "the-blockchain-bar-ledger",
  "balances": {
    "andrej": 1000000
  }
}

TokenToken tersebut harus memiliki “utilitas” nyata, yaitu, sebuah kasus penggunaan (use case). Pengguna harus bisa membayar dengan tokentoken tersebut sejak hari pertama! Andrej harus mematuhi regulator hukum (seperti SEC di Amerika Serikat). Adalah ilegal untuk menerbitkan sekuritas (security) yang tidak terdaftar. Di sisi lain, utilityutility tokentoken diperbolehkan, jadi ia segera mencetak dan menempelkan poster daftar harga baru di pintu barnya.

Andrej menetapkan nilai moneter awal untuk tokentoken-nya agar ia bisa menukarkannya dengan euro, dolar, atau mata uang fiatfiat lainnya.

1 TBB token = 1€

Item
Harga

Vodka shot

1 TBB

Orange juice

5 TBB

Burger

2 TBB

Crystal Head Vodka Bottle

950 TBB

Andrej juga memutuskan, ia harus mendapatkan 100 tokentoken per hari karena telah memelihara databasedatabase dan memiliki ide disruptif yang cemerlang.


01 | Fakta Menarik Database MVP

Genesis EtherEther (ETHETH) pertama di blockchainblockchain EthereumEthereum dibuat dan didistribusikan kepada investor awal dan developerdeveloper dengan cara yang sama seperti utilityutility tokentoken milik Andrej.

Pada tahun 2017, selama ledakan ICOICO (initial coin offerings) di jaringan blockchainblockchain EthereumEthereum, para pendiri proyek menulis dan mempresentasikan whitepaperwhitepaper kepada investor. Sebuah whitepaperwhitepaper adalah dokumen teknis yang menguraikan masalah kompleks dan solusi yang memungkinkan, dimaksudkan untuk mengedukasi dan menjelaskan suatu hal tertentu. Di dunia blockchainblockchain, whitepaperwhitepaper berfungsi untuk menguraikan spesifikasi tentang bagaimana blockchainblockchain tersebut akan terlihat dan berperilaku setelah dikembangkan.

Proyek-proyek blockchainblockchain berhasil mengumpulkan antara €10 juta hingga €300 juta per ide whitepaperwhitepaper.

Sebagai ganti uang (pendanaan ICOICO), nama-nama investor akan dimasukkan ke dalam “saldo genesis” awal, mirip seperti yang dilakukan Andrej. Harapan investor melalui ICOICO adalah nilai koin genesis akan naik dan tim pengembang akan mewujudkan blockchainblockchain yang telah diuraikan.

Tentu saja, tidak semua ide whitepaperwhitepaper membuahkan hasil. Investasi besar yang hilang karena ide-ide yang tidak jelas atau tidak lengkap adalah alasan mengapa blockchainblockchain menerima liputan negatif di media selama masa ICOICO tersebut, dan mengapa beberapa orang masih menganggapnya sebagai hype. Tetapi teknologi blockchainblockchain yang mendasarinya sangat fantastis dan berguna, seperti yang akan Anda pelajari lebih lanjut di buku ini. Hanya saja teknologi ini telah disalahgunakan oleh beberapa oknum yang tidak bertanggung jawab.


01 | Ringkasan Database MVP

BlockchainBlockchain adalah sebuah databasedatabase.

Jumlah pasokan tokentoken, saldo awal pengguna, dan pengaturan global blockchainblockchain Anda definisikan dalam sebuah file Genesis.


Tentu, ini lanjutannya.


02 | Mengubah State DB Global

Pesta yang Sepi

Senin, 25 Maret.

Setelah seminggu bekerja, fasilitas bar siap untuk menerima tokentoken. Sayangnya, tidak ada yang datang, jadi Andrej memesan tiga sloki vodka untuk dirinya sendiri dan menulis perubahan databasedatabase di secarik kertas:

andrej-3; // 3 shots of vodka
andrej+3; // technically purchasing from his own bar
andrej+700; // Reward for a week of work (7x100 per day)

Untuk menghindari penghitungan ulang statestate terbaru dari saldo setiap pelanggan, Andrej membuat file ./database/state.json yang menyimpan saldo dalam format yang teragregasi.

StateState DB yang baru:json

{
  "balances": {
    "andrej": 1000700
  }
}

Bonus untuk BabaYaga

Selasa, 26 Maret.

Untuk mendatangkan pengunjung ke barnya, Andrej mengumumkan bonus eksklusif 100% bagi siapa saja yang membeli tokentoken TBB dalam 24 jam ke depan.

Ting! Dia mendapatkan pelanggan pertamanya yang bernama BabaYaga. BabaYaga membeli tokentoken senilai 1000€, dan untuk merayakannya, dia langsung membelanjakan 1 TBB untuk satu sloki vodka. Dia punya masalah kecanduan alkohol.

Transaksi DB yang ditulis di secarik kertas:

andrej-2000;     // transfer ke BabaYaga (2 kali lipat, 2 * 1000, krna bonus 100%)
babayaga+2000;   // pembelian di muka dengan bonus 100%
babayaga-1;      // membayar 1 TBB untuk vodka
andrej+1;        // pendapatan bar dari penjualan
andrej+100;      // hadiah harian

StateState DB yang baru:

JSON

{
  "balances": {
    "andrej": 998801,
    "babayaga": 1999
  }
}

Fakta Menarik

Proyek ICOICO (initial coin offerings berbasis whitepaperwhitepaper) BlockchainBlockchain sering kali mendistribusikan tokentoken genesis dengan bonus yang berbeda, tergantung pada berapa banyak yang Anda beli dan seberapa awal Anda melakukannya. Tim pengembang menawarkan, rata-rata, bonus 10-40% kepada “partisipan” awal.

Kata “investor” dihindari agar regulator hukum tidak menganggap tokentoken tersebut sebagai sekuritas (security). Proyek-proyek tersebut akan berargumen bahwa produk utama mereka, yaitu tokentoken blockchainblockchain, berfungsi seperti “poin loyalitas”.

Para “partisipan” ini kemudian bahkan menghasilkan keuntungan 1000% (empat angka nol!) dari investasi mereka dengan menjualnya ke publik melalui bursa (exchange) beberapa bulan kemudian.


Ringkasan

BlockchainBlockchain adalah sebuah databasedatabase. Jumlah pasokan tokentoken, saldo awal pengguna, dan pengaturan global blockchainblockchain Anda definisikan dalam sebuah file Genesis. Saldo Genesis menunjukkan statestate asli dari blockchainblockchain dan tidak akan pernah diperbarui setelahnya.

Perubahan pada statestate databasedatabase disebut Transaksi (TransactionsTransactions atau TXTX).

Tentu, ini adalah terjemahan dari bagian selanjutnya.


03 | Event Monolitik vs Transaksi

Para pengembang yang terbiasa dengan arsitektur event-sourcing pasti langsung mengenali prinsip-prinsip yang familiar di balik transaksi. Mereka benar. Transaksi BlockchainBlockchain merepresentasikan serangkaian event, dan databasedatabase adalah state akhir yang teragregasi dan terkalkulasi setelah memutar ulang (replaying) semua transaksi dalam urutan tertentu.


Andrej Mulai Pemrograman

Selasa malam, 26 Maret.

Ini adalah Selasa malam yang santai bagi Andrej. Merayakan klien pertamanya, ia memutuskan untuk bermain Starcraft⁸ dan membersihkan mesin pengembangan lokalnya dengan menghapus beberapa foto lama. Sayangnya, ia terlalu cepat menekan enter saat mengetik path perintah penghapusan di terminal sudo rm -rf /. Ups.

Semua filenya, termasuk genesis.json dan state.json milik bar, hilang.

Andrej, sebagai seorang pengembang senior, berulang kali meneriakkan beberapa kata-kata kotor dengan sangat keras selama beberapa detik, tetapi dia tidak panik! Meskipun dia tidak punya cadangan (backup), dia punya sesuatu yang lebih baik — secarik kertas berisi semua transaksi databasedatabase. Satu-satunya hal yang perlu dia lakukan adalah memutar ulang semua transaksi satu per satu, dan state databasedatabase-nya akan pulih kembali.

Terkesan dengan keunggulan arsitektur berbasis event, ia memutuskan untuk memperluas solusi databasedatabase MVP-nya. Setiap aktivitas bar, seperti pembelian minuman individual, HARUS dicatat di dalam databasedatabase blockchainblockchain.

https://www.youtube.com/watch?v=Ff4VIghrTMg&feature=youtu.be&t=51603

Setiap pelanggan akan direpresentasikan dalam DB menggunakan sebuah Account Struct:

type Account string

Setiap Transaction (TX - perubahan databasedatabase) akan memiliki empat atribut berikut: from, to, value, dan data.

Atribut data dengan satu nilai yang mungkin (reward) mencatat bonus Andrej karena telah menciptakan blockchainblockchain dan secara artifisial meningkatkan total pasokan awal tokentoken TBB (inflasi).

type Tx struct {
    From  Account `json:"from"`
    To    Account `json:"to"`
    Value uint    `json:"value"`
    Data  string  `json:"data"`
}

func (t Tx) IsReward() bool {
    return t.Data == "reward"
}

DB Genesis akan tetap berupa file JSON:

{
  "genesis_time": "2019-03-18T00:00:00.000000000Z",
  "chain_id": "the-blockchain-bar-ledger",
  "balances": {
    "andrej": 1000000
  }
}

Semua transaksi, yang sebelumnya ditulis di secarik kertas, akan disimpan dalam databasedatabase file-teks lokal bernama tx.db, diserialisasi dalam format JSON dan dipisahkan oleh karakter pemisah baris (line-break):

{"from":"andrej","to":"andrej","value":3,"data":""}
{"from":"andrej","to":"andrej","value":700,"data":"reward"}
{"from":"andrej","to":"babayaga","value":2000,"data":""}
{"from":"andrej","to":"andrej","value":100,"data":"reward"}
{"from":"babayaga","to":"andrej","value":1,"data":""}

Komponen databasedatabase paling krusial yang merangkum semua logika bisnis adalah State:

type State struct {
    Balances   map[Account]uint
    txMempool  []Tx
    dbFile     *os.File
}

Struct State akan mengetahui semua saldo pengguna dan siapa yang mentransfer tokentoken TBB kepada siapa, dan berapa banyak yang ditransfer.

State ini dibangun dengan membaca saldo awal pengguna dari file genesis.json:

func NewStateFromDisk() (*State, error) {
    // dapatkan direktori kerja saat ini
    cwd, err := os.Getwd()
    if err != nil {
        return nil, err
    }

    genFilePath := filepath.Join(cwd, "database", "genesis.json")
    gen, err := loadGenesis(genFilePath)
    if err != nil {
        return nil, err
    }

    balances := make(map[Account]uint)
    for account, balance := range gen.Balances {
        balances[account] = balance
    }

Setelah itu, saldo State dari genesis diperbarui dengan memutar ulang secara berurutan semua event databasedatabase dari tx.db:

    txDbFilePath := filepath.Join(cwd, "database", "tx.db")
    f, err := os.OpenFile(txDbFilePath, os.O_APPEND|os.O_RDWR, 0600)
    if err != nil {
        return nil, err
    }

    scanner := bufio.NewScanner(f)
    state := &State{balances, make([]Tx, 0), f}

    // Iterasi melalui setiap baris file tx.db
    for scanner.Scan() {
        if err := scanner.Err(); err != nil {
            return nil, err
        }

        // Ubah TX yang di-encode JSON menjadi sebuah objek (struct)
        var tx Tx
        json.Unmarshal(scanner.Bytes(), &tx)

        // Bangun kembali state (saldo pengguna),
        // sebagai serangkaian event
        if err := state.apply(tx); err != nil {
            return nil, err
        }
    }
    return state, nil
}

Komponen State bertanggung jawab untuk:

  • Menambahkan transaksi baru ke Mempool

  • Memvalidasi transaksi terhadap State saat ini (saldo pengirim mencukupi)

  • Mengubah state

  • Menyimpan (persisting) transaksi ke disk

  • Menghitung saldo akun dengan memutar ulang semua transaksi sejak Genesis secara berurutan

Menambahkan transaksi baru ke Mempool:

func (s *State) Add(tx Tx) error {
    if err := s.apply(tx); err != nil {
        return err
    }

    s.txMempool = append(s.txMempool, tx)
    return nil
}

Menyimpan (persisting) transaksi ke disk:

func (s *State) Persist() error {
    // Buat salinan dari mempool karena s.txMempool akan dimodifikasi
    // di dalam loop di bawah ini
    mempool := make([]Tx, len(s.txMempool))
    copy(mempool, s.txMempool)

    for i := 0; i < len(mempool); i++ {
        txJson, err := json.Marshal(mempool[i])
        if err != nil {
            return err
        }

        if _, err = s.dbFile.Write(append(txJson, '\n')); err != nil {
            return err
        }

        // Hapus TX yang telah ditulis ke file dari mempool
        s.txMempool = s.txMempool[1:]
    }

    return nil
}

Mengubah dan Memvalidasi state:

func (s *State) apply(tx Tx) error {
    if tx.IsReward() {
        s.Balances[tx.To] += tx.Value
        return nil
    }

    if tx.Value > s.Balances[tx.From] {
        return fmt.Errorf("insufficient balance")
    }

    s.Balances[tx.From] -= tx.Value
    s.Balances[tx.To] += tx.Value

    return nil
}

Tentu, ini adalah kelanjutan dari terjemahan buku tersebut.


Membangun Command-Line-Interface (CLI)

Selasa malam, 26 Maret.

Andrej ingin cara yang mudah untuk menambahkan transaksi baru ke DB-nya dan melihat daftar saldo terbaru pelanggannya. Karena program GoGo dikompilasi menjadi binarybinary, ia membangun sebuah CLI untuk programnya.

Cara termudah untuk mengembangkan program berbasis CLI di GoGo adalah dengan menggunakan pustaka pihak ketiga github.com/spf13/cobra.

Andrej menginisialisasi manajer dependensi bawaan GoGo untuk proyeknya, yang disebut gogo modulesmodules:

go mod init github.com/web3coach/the-blockchain-bar

Perintah GoGo modulesmodules akan secara otomatis mengambil pustaka apa pun yang Anda referensikan di dalam file GoGo Anda.

Andrej membuat direktori baru bernama cmd dengan subdirektori tbb:

mkdir -p ./cmd/tbb

Di dalamnya ia membuat file main.go, yang berfungsi sebagai titik masuk (entry point) CLI program:

package main

import (
    "os"
    "github.com/spf13/cobra"
    "fmt"
)

func main() {
    var tbbCmd = &cobra.Command{
        Use:   "tbb",
        Short: "The Blockchain Bar CLI",
        Run: func(cmd *cobra.Command, args []string) {
        },
    }

    err := tbbCmd.Execute()
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

Program GoGo dikompilasi menggunakan perintah install:

$ go install ./cmd/tbb/...
go: finding github.com/spf13/cobra v1.0.0
go: downloading github.com/spf13/cobra v1.0.0
go: extracting github.com/spf13/cobra v1.0.0

GoGo akan mendeteksi pustaka yang hilang dan mengambilnya secara otomatis sebelum mengkompilasi program. Bergantung pada $GOPATH Anda, program yang dihasilkan akan disimpan di folder $GOPATH/bin.

$ echo $GOPATH
> /home/web3coach/go

$ which tbb
> /home/web3coach/go/bin/tbb

Anda sekarang dapat menjalankan tbb dari terminal Anda, tetapi itu tidak akan melakukan apa-apa karena fungsi Run di dalam file main.go masih kosong.

Hal pertama yang dibutuhkan Andrej adalah dukungan versioning untuk program CLI tbb-nya.

Di sebelah file main.go, ia membuat perintah version.go:

package main

import (
    "fmt"
    "github.com/spf13/cobra"
)

const Major = "0"
const Minor = "1"
const Fix = "0"
const Verbal = "TX Add && Balances List"

var versionCmd = &cobra.Command{
    Use:   "version",
    Short: "Describes version.",
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Printf("Version: %s.%s.%s-beta %s", Major, Minor, Fix, Verbal)
    },
}

Dia mengkompilasi dan menjalankannya:

$ go install ./cmd/tbb/...
$ tbb version
> Version: 0.1.0-beta TX Add && Balances List

Sempurna.

Sama seperti file version.go, ia membuat file balances.go:

func balancesCmd() *cobra.Command {
    var balancesCmd = &cobra.Command{
        Use:   "balances",
        Short: "Interact with balances (list...).",
        PreRunE: func(cmd *cobra.Command, args []string) error {
            return incorrectUsageErr()
        },
        Run: func(cmd *cobra.Command, args []string) {
        },
    }

    balancesCmd.AddCommand(balancesListCmd)
    return balancesCmd
}

Perintah balances akan bertanggung jawab untuk memuat State DB terbaru dan mencetaknya ke standard output:

var balancesListCmd = &cobra.Command{
    Use:   "list",
    Short: "Lists all balances.",
    Run: func(cmd *cobra.Command, args []string) {
        state, err := database.NewStateFromDisk()
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
        defer state.Close()

        fmt.Println("Saldo Akun:")
        fmt.Println("__________________")
        fmt.Println("")
        for account, balance := range state.Balances {
            fmt.Println(fmt.Sprintf("%s: %d", account, balance))
        }
    },
}

Andrej memverifikasi apakah perintah tersebut berfungsi seperti yang diharapkan. Seharusnya perintah ini mencetak saldo yang sama persis seperti yang didefinisikan dalam file Genesis karena file tx.db masih kosong.

$ go install ./cmd/tbb/...
$ tbb balances list
Saldo Akun:
__________________

andrej: 1000000

Berhasil! Sekarang dia hanya butuh sebuah perintah untuk mencatat aktivitas bar.

Andrej membuat perintah ./cmd/tbb/tx.go:

func txCmd() *cobra.Command {
    var txsCmd = &cobra.Command{
        Use:   "tx",
        Short: "Interact with txs (add...).",
        PreRunE: func(cmd *cobra.Command, args []string) error {
            return incorrectUsageErr()
        },
        Run: func(cmd *cobra.Command, args []string) {
        },
    }

    txsCmd.AddCommand(txAddCmd())
    return txsCmd
}

Perintah tbb tx add menggunakan fungsi State.Add(tx) untuk menyimpan event bar ke dalam sistem file:

func txAddCmd() *cobra.Command {
    var cmd = &cobra.Command{
        Use:   "add",
        Short: "Adds new TX to database.",
        Run: func(cmd *cobra.Command, args []string) {
            from, _ := cmd.Flags().GetString(flagFrom)
            to, _ := cmd.Flags().GetString(flagTo)
            value, _ := cmd.Flags().GetUint(flagValue)

            fromAcc := database.NewAccount(from)
            toAcc := database.NewAccount(to)
            tx := database.NewTx(fromAcc, toAcc, value, "")

            state, err := database.NewStateFromDisk()
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }
            // `defer` berarti, di akhir eksekusi fungsi ini,
            // jalankan statement berikut (tutup file DB dengan semua TX)
            defer state.Close()

            // Tambahkan TX ke array dalam memori (pool)
            err = state.Add(tx)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }
            
            // Salin (flush) TX dari mempool ke disk
            err = state.Persist()
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }
            
            fmt.Println("TX successfully added to the ledger.")
        },
    }

Perintah tbb tx add memiliki 3 flags wajib: --from, --to, dan --value.

    cmd.Flags().String(flagFrom, "", "Dari akun mana token akan dikirim")
    cmd.MarkFlagRequired(flagFrom)
    
    cmd.Flags().String(flagTo, "", "Ke akun mana token akan dikirim")
    cmd.MarkFlagRequired(flagTo)
    
    cmd.Flags().Uint(flagValue, 0, "Berapa banyak token yang akan dikirim")
    cmd.MarkFlagRequired(flagValue)

    return cmd
}

CLI-nya sudah selesai!

Andrej memindahkan semua transaksi dari kertas ke DB barunya:

$ tbb tx add --from=andrej --to=andrej --value=3
$ tbb tx add --from=andrej --to=andrej --value=700 --data=reward
$ tbb tx add --from=andrej --to=babayaga --value=2000
$ tbb tx add --from=andrej --to=andrej --value=100 --data=reward
$ tbb tx add --from=babayaga --to=andrej --value=1

Baca semua TX dari disk dan hitung state terbaru:

$ tbb balances list
Saldo Akun:
__________________

andrej: 998801
babayaga: 1999

Data bar berhasil dipulihkan! Fiuh, malam yang luar biasa!


Tentang Pustaka Cobra CLI

Hal yang baik tentang pustaka Cobra untuk pemrograman CLI adalah fitur-fitur tambahan yang disediakannya. Misalnya, Anda sekarang dapat menjalankan tbb help dan itu akan mencetak semua sub-perintah TBB yang terdaftar beserta instruksi cara menggunakannya.

$ tbb help
The Blockchain Bar CLI

Usage:
  tbb [flags]
  tbb [command]

Available Commands:
  balances    Interact with balances (list...).
  help        Help about any command
  tx          Interact with txs (add...).
  version     Describes version.

Flags:
  -h, --help   help for tbb

Use "tbb [command] --help" for more information about a command.

Fakta Menarik

Secara tidak sengaja kehilangan data pelanggan adalah hal yang biasa terjadi di dunia korporat saat ini. BlockchainBlockchain memperbaiki ini dengan mendesentralisasi penyimpanan data.

Trik yang Andrej tanamkan ke dalam program dengan melewatkan verifikasi saldo untuk TX yang ditandai sebagai reward. BitcoinBitcoin dan EthereumEthereum bekerja dengan cara yang sama. Saldo dari Akun yang menambang (mined) sebuah block meningkat begitu saja sebagai subjek dari inflasi total pasokan tokentoken yang mempengaruhi seluruh chain. Total pasokan bitcoinbitcoin dibatasi pada 21 juta BTC. Anda akan belajar lebih banyak tentang “mining” dan “blocks” di bab 7 dan 10.

Komponen State dan Mempool tidak unik untuk program ini. Andrej memilih nama dan desain agar sesuai dengan model go-Ethereum yang disederhanakan, sehingga Anda dapat melihat sekilas ke dalam kode sumber inti EthereumEthereum.


Ringkasan

BlockchainBlockchain adalah sebuah databasedatabase. Jumlah pasokan tokentoken, saldo awal pengguna, dan pengaturan global blockchainblockchain didefinisikan dalam sebuah file Genesis. Saldo Genesis menunjukkan apa state asli dari blockchainblockchain dan tidak akan pernah diperbarui setelahnya.

Perubahan pada state databasedatabase disebut Transaksi (TXTX). Transaksi pada dasarnya adalah Event kuno yang merepresentasikan tindakan di dalam sistem.


04 | Manusia Itu Serakah

Keserakahan Bisnis yang Tipikal

Rabu, 27 Maret.

BabaYaga berinvestasi sedikit terlalu banyak. Dia lupa bahwa pembayaran sewa flatnya sudah dekat, dan dia tidak punya uang. BabaYaga menelepon pemilik flatnya, Caesar.

BabaYaga: Hei Caesar, maaf, tapi aku tidak punya uang tunai untuk membayar sewa bulan ini…

Caesar: Kenapa tidak?

BabaYaga: ICOICO The Blockchain Bar menawarkan bonus besar, dan aku membeli tokentoken senilai 2000€ hanya dengan 1000€. Itu penawaran yang sangat bagus!

Caesar: Apa-apaan yang kamu bicarakan? Apa itu ICOICO? Apa pula itu tokentoken? Bisakah kamu membayarku dengan cara lain?

BabaYaga: Oh, jangan lagi. Aku bisa memberimu 1000 tokentoken TBB senilai 1000€, dan kamu bisa menggunakannya di bar untuk membayar minumanmu! Biar aku telepon pemilik bar, Andrej, untuk melakukan transfer!

Caesar: Baiklah… Aku terima.

Andrej melakukan transfer, tetapi memutuskan untuk memotong biaya tambahan sebesar 50 tokentoken TBB untuk jerih payahnya. Dia tidak mau, TAPI para pemegang saham bar yang berinvestasi padanya beberapa tahun lalu memaksanya untuk menghasilkan keuntungan sesegera mungkin.

BabaYaga kemungkinan besar tidak akan menyadari biaya yang relatif kecil ini, pikir Andrej dalam hati. Pada akhirnya, hanya dia yang punya akses ke DB.

Bash

// pembayaran sewa
$ tbb tx add --from=babayaga --to=caesar --value=1000

// pemotongan biaya tersembunyi
$ tbb tx add --from=babayaga --to=andrej --value=50

// hadiah baru untuk satu hari lagi memelihara DB
$ tbb tx add --from=andrej --to=andrej --value=100 --data=reward

Fakta Menarik 1/2

Kasus penggunaan nomor satu untuk blockchainblockchain adalah perbankan. Banyak proyek blockchainblockchain bertujuan untuk mengoptimalkan pertukaran uang domestik dan internasional di berbagai koridor mata uang (contohnya XRPXRP).

Proyek lain fokus pada kebebasan dan identitas kedaulatan diri (Self-Sovereign Identity atau SSISSI) - sebuah gerakan digital yang mengakui bahwa seorang individu harus memiliki dan mengendalikan identitas dan uang mereka sendiri tanpa campur tangan otoritas administratif atau perantara terpusat lainnya. SSISSI memungkinkan orang untuk berinteraksi di dunia digital dengan kebebasan dan kapasitas kepercayaan yang sama seperti yang mereka lakukan di dunia luring. (Contohnya BitcoinBitcoin / EthereumEthereum).

Berikut adalah beberapa fakta menarik mengapa blockchainblockchain sangat cocok untuk menggantikan infrastruktur perbankan Anda saat ini.


Hal yang baik tentang tokentoken virtual adalah fungibilitasnya - yaitu, kemampuannya untuk diperdagangkan, dengan setiap unitnya sama bergunanya dengan unit berikutnya. Melakukan transfer dari satu akun ke akun lain dapat dilakukan hanya dengan mengubah state databasedatabase. Cryptocurrency dapat diperdagangkan 24/7.

Anda tidak bisa memperdagangkan saham secara langsung. Anda perlu melalui seorang broker yang mengambil sebagian persentase dari total transaksi sebagai biaya (1-3% dari keuntungan tahunan rata-rata 7%).

Transfer bank internasional memakan waktu antara 3-10 hari kerja dan bisa memakan biaya hingga 5% dari nilai yang ditransfer! Jika Anda mengirim $10.000, Anda mungkin harus membayar hingga $500.¹¹

Teknologi di balik ini selama 40 tahun terakhir? File FTPFTP + CSVCSV.

¹¹https://www.ofx.com/en-au/faqs/how-much-does-it-cost-to-send-money-internationally/

Fakta Menarik 2/2

Apakah Anda pikir pasar saham itu adil? Bank, indeks, dan saham sangat terpusat dan dikendalikan oleh pemerintah dan kelompok swasta Wall Street. Pasar bebas? Wall Street mengontrol seberapa banyak harga bisa naik/turun dalam satu hari.

Sebagai contoh, Wall Street menghentikan perdagangan "Indeks S&P 500" setelah penurunan 7% untuk melindungi investor dan hedge funds mereka dari kerugian akibat orang-orang yang menjual saham mereka selama bulan Maret 2020 setelah berita COVID. Setelah itu, The FED (Bank Sentral AS) mencetak triliunan dolar untuk diri mereka sendiri guna menopang harga saham. Jika Anda seorang pengembang yang suka menabung dan menghindari utang, nilai tabungan Anda baru saja hilang dalam semalam dengan persentase yang tidak diketahui.

Banyak negara menuju imbal hasil negatif (negative yields), sebuah wilayah yang belum pernah dijelajahi dengan konsekuensi yang tidak diketahui. Apa artinya ini? Sebentar lagi Anda harus membayar bank untuk menyimpan tabungan Anda.

Inilah inflasi yang sesungguhnya. Anda dipaksa untuk membelanjakan uang Anda untuk mendukung sistem yang tidak Anda kontrol.


Ringkasan

Perangkat lunak tertutup dengan akses terpusat ke data dan aturan pribadi hanya menempatkan segelintir orang pada posisi berkuasa. Pengguna tidak punya pilihan, dan pemegang saham menjalankan bisnis untuk menghasilkan uang.

BlockchainBlockchain adalah sebuah databasedatabase. Jumlah pasokan tokentoken, saldo awal pengguna, dan pengaturan global blockchainblockchain Anda definisikan dalam sebuah file Genesis. Saldo Genesis menunjukkan apa state asli dari blockchainblockchain dan tidak akan pernah diperbarui setelahnya.

Perubahan pada state databasedatabase disebut Transaksi (TXTX). Transaksi pada dasarnya adalah Event kuno yang merepresentasikan tindakan di dalam sistem.

Tentu, ini adalah terjemahan bagian selanjutnya.


05 | Mengapa Kita Membutuhkan Blockchain

BabaYaga Mencari Keadilan

Kamis, 28 Maret.

BabaYaga masuk ke bar untuk merayakan hari ulang tahunnya.

BabaYaga: Hei, Andrej! Hari ini ulang tahunku! Berikan aku botolmu yang paling mahal!

Andrej: Selamat ulang tahun! Ini dia: Crystal Head Vodka. Tapi kamu perlu membeli satu tokentoken TBB tambahan. Harga botolnya 950 tokentoken, dan saldomu 949.

BabaYaga: Apa?! Seharusnya saldoku 999 TBB!

Andrej: Transfer dana ke Caesar yang kamu minta minggu lalu memotong biaya 50 tokentoken.

BabaYaga: Ini tidak bisa diterima! Aku tidak akan pernah setuju dengan biaya setinggi itu! Kamu tidak bisa melakukan ini, Andrej! Aku percaya pada sistemmu, tapi kamu sama tidak bisa diandalkannya dengan pemilik bisnis lainnya. Sesuatu harus berubah!

Andrej: Baiklah, begini. Kamu adalah pelanggan setiaku, dan aku tidak ingin membebankan biaya padamu, tapi para pemegang saham memaksaku. Biar aku program ulang sistemku dan membuatnya sepenuhnya transparan dan terdesentralisasi. Lagi pula, jika semua orang bisa berinteraksi dengan bar tanpa harus melaluiku, itu akan meningkatkan efisiensi bar secara signifikan dan menyeimbangkan tingkat kepercayaan!

  • Memesan minuman akan memakan waktu beberapa detik, bukan menit.

  • Pelanggan yang lupa dompetnya di rumah bisa meminjam atau meminjamkan tokentoken satu sama lain.

  • Aku tidak perlu khawatir kehilangan data klien (lagi) karena semua orang akan memiliki salinannya.

  • DatabaseDatabase-nya akan immutable (kekal/tidak bisa diubah), jadi begitu semua orang menyetujui state tertentu, tidak ada orang lain yang bisa mengubahnya atau memodifikasi riwayatnya dengan niat jahat. Sifat immutable ini juga akan membantu audit pajak tahunan!

  • Jika pemegang saham ingin memperkenalkan biaya baru atau menaikkan yang sudah ada, semua orang yang terlibat dalam sistem blockchainblockchain akan mengetahuinya dan harus menyetujuinya. Para pengguna dan pemilik bisnis bahkan mungkin harus terlibat bersama dalam suatu sistem tata kelola terdesentralisasi (decentralized governance), mungkin berdasarkan pemungutan suara. Jika terjadi ketidaksepakatan, para pengguna bisa pergi dengan membawa semua data mereka!

BabaYaga: Yah, kedengarannya bagus, tapi apa itu mungkin?

Andrej: Ya, kurasa begitu. Dengan sedikit hashing, linked lists, struktur data immutable, replikasi terdistribusi (distributed replication), dan kriptografi asimetris (asymmetric cryptography)!

BabaYaga: Aku sama sekali tidak mengerti apa yang baru saja kamu katakan, tapi lakukan saja hal geeky-mu itu, Andrej!

Satu hari lagi sistem berjalan, berarti 100 tokentoken TBB lagi untuk Andrej karena memelihara dan meningkatkan blockchainblockchain:

Bash

$ tbb tx add --from=andrej --to=andrej --value=100 --data=reward

Fakta Menarik

Miners (penambang) BitcoinBitcoin dan EthereumEthereum juga menerima hadiah setiap ~15 menit karena menjalankan server blockchainblockchain (nodes) dan memvalidasi transaksi.

Setiap 15 menit, satu miner BitcoinBitcoin menerima 12.5 BTC (senilai $100.000 saat halaman ini ditulis) untuk menutupi biaya servernya + mendapatkan keuntungan.

Jaringan BitcoinBitcoin mengonsumsi listrik sebanyak seluruh negara Austria. Ini menyumbang 0,29% dari konsumsi listrik tahunan dunia.

Setiap tahunnya, jaringan ini mengonsumsi 76.84 TWh, menghasilkan jejak karbon 36.50 Mt CO2 (setara dengan Selandia Baru). Sumber.¹³

Mengapa? Anda akan belajar lebih banyak di Bab 11, di mana Anda akan memprogram algoritma mining BitcoinBitcoin dari awal!

PS: Algoritma kita akan mengonsumsi listrik sedikit lebih sedikit :)


Ringkasan

Perangkat lunak tertutup dengan akses terpusat ke data pribadi hanya menempatkan segelintir orang pada posisi berkuasa. Pengguna tidak punya pilihan, dan pemegang saham menjalankan bisnis untuk menghasilkan uang.

Pengembang BlockchainBlockchain bertujuan untuk mengembangkan protokol di mana pengusaha aplikasi dan pengguna bersinergi dalam hubungan yang transparan dan dapat diaudit. Spesifikasi sistem blockchainblockchain harus didefinisikan dengan baik sejak awal dan hanya berubah jika penggunanya mendukung perubahan tersebut.

BlockchainBlockchain adalah sebuah databasedatabase. Jumlah pasokan tokentoken, saldo awal pengguna, dan pengaturan global blockchainblockchain didefinisikan dalam sebuah file Genesis. Saldo Genesis menunjukkan apa state asli dari blockchainblockchain dan tidak akan pernah diperbarui setelahnya.

Perubahan pada state databasedatabase disebut Transaksi (TXTX). Transaksi pada dasarnya adalah Event kuno yang merepresentasikan tindakan di dalam sistem.


06 | L’Hash de Immutable

Tingkat kesulitan teknis dimulai dari bagian ini! Konsepnya akan semakin menantang, tetapi pada saat yang sama, sangat menarik. Kencangkan sabuk pengaman Anda :)

Bagaimana Cara Memprogram Database yang Immutable?

Jumat, 29 Maret.

Jika Andrej ingin mencari tahu cara memprogram DB yang immutable (kekal/tidak dapat diubah), ia harus menyadari mengapa sistem database lain secara desain bersifat mutable (dapat diubah).

Ia memutuskan untuk menganalisis sebuah Tabel DB MySQL:

id
nama
saldo

1

Andrej

998951

2

BabaYaga

949

3

Caesar

1000

Di DB MySQL, siapa pun yang memiliki akses dan alasan yang cukup baik dapat melakukan pembaruan tabel seperti:

UPDATE user_balance SET balance = balance + 100 WHERE id > 1

Memperbarui nilai di berbagai baris dimungkinkan karena baris-baris tabel tersebut bersifat independen, mutable, dan state terbarunya tidak terlihat jelas. Apa perubahan DB terbaru? Kolom terakhir diubah? Baris terakhir disisipkan? Jika demikian, bagaimana Andrej bisa tahu baris mana yang baru saja dihapus?

Jika baris dan state tabel saling terikat erat, dependen, a.k.a, memperbarui baris 1 akan menghasilkan tabel baru yang sama sekali berbeda, maka Andrej akan mencapai tujuannya, yaitu immutability.

Bagaimana cara mengetahui jika ada satu byte pun di dalam database yang telah berubah?


Immutabilitas melalui Fungsi Hash

Sabtu, 30 Maret.

Hashing adalah proses mengambil masukan string dengan panjang sembarang dan menghasilkan string hash dengan panjang tetap. Perubahan apa pun pada masukan akan menghasilkan hash baru yang berbeda.

package main

import (
    "crypto/sha256"
    "fmt"
)

func main() {
    balancesHash := sha256.Sum256([]byte("| 1 | Andrej | 99895 |"))
    fmt.Printf("%x\n", balancesHash)
    // Keluaran: 6a04bd8e2...f70a3902374f21e089ae7cc3b200751

    // Ubah saldo dari 99895 -> 99896
    balancesHashDiff := sha256.Sum256([]byte("| 1 | Andrej | 99896 |"))
    fmt.Printf("%x\n", balancesHashDiff)
    // Keluaran: d04279207...ec6d280f6c7b3e2285758030292d5e1
}

Andrej juga memerlukan tingkat keamanan tertentu untuk database-nya, jadi ia memutuskan untuk menggunakan Fungsi Hash Kriptografi dengan properti sebagai berikut:

  • Bersifat deterministik;¹⁶ - pesan yang sama akan selalu menghasilkan hash yang sama.

  • Cepat untuk menghitung nilai hash untuk pesan apa pun.

  • Tidak mungkin (infeasible) untuk menghasilkan pesan dari nilai hash-nya kecuali dengan mencoba semua kemungkinan pesan.

  • Perubahan kecil pada pesan akan mengubah nilai hash secara ekstensif sehingga nilai hash yang baru tampak tidak berkorelasi dengan nilai hash yang lama.

  • Tidak mungkin (infeasible)¹⁷ untuk menemukan dua pesan berbeda dengan nilai hash yang sama.


Mengimplementasikan Hashing Konten DB

Sabtu Malam, 30 Maret.

Andrej memodifikasi fungsi Persist() untuk mengembalikan hash konten baru, yang disebut Snapshot, setiap kali transaksi baru disimpan.

type Snapshot [32]byte

Snapshot ini dihasilkan oleh fungsi hashing aman sha256 yang baru ini:

func (s *State) doSnapshot() error {
    // Baca ulang seluruh file dari byte pertama
    _, err := s.dbFile.Seek(0, 0)
    if err != nil {
        return err
    }

    txsData, err := ioutil.ReadAll(s.dbFile)
    if err != nil {
        return err
    }

    s.snapshot = sha256.Sum256(txsData)

    return nil
}

Fungsi doSnapshot() dipanggil oleh fungsi Persist() yang telah dimodifikasi. Ketika sebuah transaksi baru ditulis ke dalam file tx.db, Persist() melakukan hash pada seluruh konten file dan mengembalikan "sidik jari" 32 byte-nya, yaitu hash.

(Diagram yang menggambarkan alur: Persist() -> Menulis TX ke tx.db -> Melakukan hash pada seluruh file tx.db -> Menghasilkan hash)

Mulai saat ini, semua orang dapat dengan 100% percaya diri dan aman merujuk pada state database tertentu (kumpulan data) dengan hash snapshot yang spesifik.


Waktunya Latihan.

1/4 Jalankan perintah tbb balances list dan periksa apakah saldonya cocok.

$ tbb balances list
Saldo akun pada 7d4a360f465d...
andrej: 999251
babayaga: 949
caesar: 1000

2/4 Hapus 2 baris terakhir dari ./database/tx.db dan periksa kembali saldonya.

$ tbb balances list
Saldo akun pada 841770dcd3...
andrej: 999051
babayaga: 949
caesar: 1000

3/4 Beri hadiah pada Andrej untuk 2 hari terakhir (dari 28 hingga 30 Maret):

Transaksi Hadiah 1:

$ tbb tx add --from=andrej --to=andrej --value=100 --data=reward
Menyimpan TX baru ke disk:
{"from":"andrej","to":"andrej","value":100,"data":"reward"}
Snapshot DB Baru: ff2470c7043f5a34169b5dd38921ba6825b03b3facb83e426...
TX berhasil disimpan ke dalam ledger.

Transaksi Hadiah 2:

$ tbb tx add --from=andrej --to=andrej --value=100 --data=reward
Menyimpan TX baru ke disk:
{"from":"andrej","to":"andrej","value":100,"data":"reward"}
Snapshot DB Baru: 7d4a360f468b837b662816bcdc52c1869f99327d53ab4a9ca...
TX berhasil disimpan ke dalam ledger.

4/4 Jalankan perintah tbb balances list dan pastikan saldo serta hash snapshot-nya sama seperti di awal.

$ tbb balances list
Saldo akun pada 7d4a360f465d...
andrej: 999251
babayaga: 949
caesar: 1000

Selesai!

Karena fungsi hash kriptografi sha256, dengan masukan yang sama (file tx.db saat ini dan 2x perintah tbb tx add), menghasilkan keluaran yang sama, jika Anda mengikuti langkah-langkah yang persis sama di komputer Anda, Anda akan menghasilkan state database dan hash yang sama persis!


Ringkasan

Perangkat lunak tertutup dengan akses terpusat ke data pribadi hanya menempatkan segelintir orang pada posisi berkuasa. Pengguna tidak punya pilihan, dan pemegang saham menjalankan bisnis untuk menghasilkan uang.

Pengembang BlockchainBlockchain bertujuan untuk mengembangkan protokol di mana pengusaha aplikasi dan pengguna bersinergi dalam hubungan yang transparan dan dapat diaudit. Spesifikasi sistem blockchainblockchain harus didefinisikan dengan baik sejak awal dan hanya berubah jika penggunanya mendukung perubahan tersebut.

BlockchainBlockchain adalah database yang immutable. Pasokan tokentoken, saldo awal pengguna, dan pengaturan global blockchainblockchain didefinisikan dalam sebuah file Genesis. Saldo Genesis menunjukkan apa state asli dari blockchainblockchain dan tidak akan pernah diperbarui setelahnya.

Perubahan pada state databasedatabase disebut Transaksi (TXTX). Transaksi pada dasarnya adalah Event kuno yang merepresentasikan tindakan di dalam sistem.

Konten database di-hash oleh fungsi hash kriptografi yang aman. Para partisipan blockchainblockchain menggunakan hash yang dihasilkan untuk merujuk pada state database tertentu.


07 | Model Pemrograman Blockchain

Meningkatkan Performa DB yang Immutable

Minggu, 31 Maret.

Andrej tidak hanya membangun database yang immutable, tetapi juga yang terdistribusi! Dia akan mendistribusikan dan mereplikasi semua data ke setiap komputer klien/pemangku kepentingan yang berinteraksi dengan bar untuk menghindari sentralisasi data dan satu sumber kebenaran yang mudah diserang. Ini merupakan tantangan performa.

Program TBB saat ini bekerja dengan baik dengan ukuran database kecil saat berjalan di satu komputer, tetapi ia memiliki dua kendala performa (bottleneck):

  • Mendistribusikan transaksi ke komputer lain satu per satu akan menjadi tidak efisien karena latensi jaringan dalam sistem terdistribusi.

  • Membuat hash snapshot dari database dan memvalidasinya memerlukan pembacaan dan hashing ulang seluruh DB—yang berpotensi berukuran beberapa gigabyte—dari awal untuk SETIAP TX BARU.

Untungnya, Andrej dapat menyelesaikan kedua masalah ini dengan mengimplementasikan Hashed Linked List yang dikombinasikan dengan Batch Strategy standar.

Batching adalah strategi umum saat bekerja dengan sistem database SQL/NoSQL/Lainnya. Strategi batch terdiri dari "menangani beberapa item sekaligus". Solusinya adalah dengan merangkum transaksi ke dalam "potongan-potongan" atau "block" yang saling terhubung.


Batch + Hash + Linked List ⇒ Blocks

Senin, 1 April.

Andrej tidak sedang bercanda (lihat tanggalnya). Dia merancang struktur data berikut untuk merangkum transaksi ke dalam batch:

type Hash [32]byte

type Block struct {
    Header BlockHeader
    TXs    []Tx // hanya transaksi baru (payload)
}

type BlockHeader struct {
    Parent Hash // referensi block induk
    Time   uint64
}

Dan dia mengganti nama struct Snapshot menjadi Hash.

Struct Block akan memiliki 2 atribut, Header dan Payload:

  • Payload menyimpan transaksi BARU.

  • Header menyimpan metadata block (REFERENSI HASH BLOCK INDUK, waktu).


Hashing lama yang lambat pada seluruh DB setelah setiap TX baru:

func (s *State) doSnapshot() error {
    // Baca ulang seluruh file dari byte pertama
    _, err := s.dbFile.Seek(0, 0)
    if err != nil {
        return err
    }
    txsData, err := ioutil.ReadAll(s.dbFile)
    if err != nil {
        return err
    }
    // Beri saya hash dari seluruh konten DB
    s.snapshot = sha256.Sum256(txsData)

    return nil
}

Hashing baru yang dioptimalkan - di mana Hash 32-byte dihitung dengan hanya melakukan hash pada Block terbaru yang di-encode sebagai JSON:

type Block struct {
    Header BlockHeader // metadata (hash block induk + waktu)
    TXs    []Tx        // hanya transaksi baru (payload)
}

func (b Block) Hash() (Hash, error) {
    blockJson, err := json.Marshal(b)
    if err != nil {
        return Hash{}, err
    }
    return sha256.Sum256(blockJson), nil
}

Implementasi di atas dapat direpresentasikan secara visual sebagai model pemrograman blockchain:

block saling terhubung melalui atribut ParentHash

Mengapa referensi hash dari block induk diperlukan?

ParentHash digunakan sebagai "checkpoint" yang andal, merepresentasikan dan merujuk pada konten database yang telah di-hash sebelumnya.

ParentHash meningkatkan performa. Hanya data baru + referensi ke state sebelumnya yang perlu di-hash untuk mencapai immutability.

Contohnya, jika Anda mencoba mengubah nilai TX di Block 0, itu akan menghasilkan hash Block 0 yang baru dan unik. Hash dari Block 1, yang didasarkan pada referensi Block 0 sebagai induknya, oleh karena itu akan segera berubah juga. Efek berantai ini akan mempengaruhi semua block, membuat database penyerang berniat jahat menjadi tidak valid - berbeda dari para pemangku kepentingan database lainnya yang jujur.

Oleh karena itu, database penyerang akan dikecualikan dari partisipasi dalam jaringan. Mengapa? Anda akan mengetahuinya di Bab 10 - di mana Anda akan memprogram algoritma sinkronisasi peer-to-peer.


Cara Kerja Penambahan TX ke dalam Block

Senin Malam, 1 April.

Sebuah struct Tx baru dibuat dari parameter cmd:

// ... di dalam txAddCmd()
tx := database.NewTx(
    database.NewAccount(from),
    database.NewAccount(to),
    value,
    data,
)

Tx tersebut kemudian disimpan di dalam mempool milik State (bisa satu atau beberapa Tx):

state, err := database.NewStateFromDisk()
// ...
state.AddTx(tx)

Terakhir, fungsi state.Persist() dieksekusi untuk menyalin transaksi dari mempool ke dalam file database ./database/block.db:


state.Persist()

Secara internal, fungsi Persist() akan membuat Block baru, meng-encode-nya menjadi JSON, dan menuliskannya ke DB.

func (s *State) Persist() (Hash, error) {
    // Buat Block baru HANYA dengan TX yang baru
    block := NewBlock(
        s.latestBlockHash,
        uint64(time.Now().Unix()),
        s.txMempool,
    )

    blockHash, err := block.Hash()
    if err != nil {
        return Hash{}, err
    }

    blockFs := BlockFS{blockHash, block}

    // Encode menjadi string JSON
    blockFsJson, err := json.Marshal(blockFs)
    if err != nil {
        return Hash{}, err
    }

    fmt.Printf("Menyimpan Block baru ke disk:\n")
    fmt.Printf("\t%s\n", blockFsJson)

    // Tulis ke file DB di baris baru
    err = s.dbFile.Write(append(blockFsJson, '\n'))
    if err != nil {
        return Hash{}, err
    }

    s.latestBlockHash = blockHash
    
    // Reset mempool
    s.txMempool = []Tx{}

    return s.latestBlockHash, nil
}

Migrasi dari TX.db ke BLOCKS.db

Senin Malam, 1 April.

Saat ini, semua transaksi ditulis di file ./database/tx.db. Untuk memindahkannya ke dalam block, Andrej mengembangkan sebuah perintah bantuan sederhana.

Dia menempatkan perintah tersebut di file ./cmd/tbbmigrate/main.go:

func main() {
    state, err := database.NewStateFromDisk()
    // ...
    defer state.Close()

Dia merangkum 2 TX pertama bar ke dalam Block 0 dan menyimpannya ke disk:

    block0 := database.NewBlock(
        database.Hash{},
        uint64(time.Now().Unix()),
        []database.Tx{
            database.NewTx("andrej", "andrej", 3, ""),
            database.NewTx("andrej", "andrej", 700, "reward"),
        },
    )
    state.AddBlock(block0)
    block0hash, _ := state.Persist()

    // Hash Block 0: 07536e2c559b0a4566b84802...
    // Hash Induk Block 0: 0000000...

Hasil yang ditulis ke disk:

{
  "hash": "07536e2c55ffa629a105f61c8f6c5f1c7289d139e02639436...",
  "block": {
    "header": {
      "parent": "0000000000000000000000000000000000000000000000000...",
      "time": 1556563065
    },
    "payload": [
      {"from":"andrej","to":"andrej","value":3,"data":""},
      {"from":"andrej","to":"andrej","value":700,"data":"reward"}
    ]
  }
}

Hash induknya kosong karena ini adalah Block pertama yang pernah dibuat. Andrej memasukkan sisa riwayat TX ke dalam Block 1. Block 1 merujuk pada Block 0 sebagai induknya hanya dengan menggunakan hash dari Block 0. Tidak perlu melakukan hash ulang pada seluruh konten Block 0! Jauh lebih sedikit siklus CPU yang terbuang, dengan tingkat keamanan dan immutability yang sama tercapai.

    block1 := database.NewBlock(
        block0hash,
        uint64(time.Now().Unix()),
        []database.Tx{
            // ... daftar 6 transaksi lainnya
        },
    )
    state.AddBlock(block1)
    state.Persist()

    // Hash Block 1: 2efe28463821660834cf8e3cc555...
    // Hash Induk Block 1: 07536e2c559b0a4566b84802...

Eksperimen dengan Perintah Migrasi

Waktunya Latihan.

1/3 Hapus 2 block yang ada dari ./database/block.db yang telah di-commit oleh Andrej di branch saat ini.

cat /dev/null > ./database/block.db

2/3 Kompilasi kode dan jalankan perintah migrasi yang baru.

go install ./cmd/...
tbbmigrate

3/3 Amati 2 block baru yang di-encode dalam JSON ditulis ke dalam ./database/block.db seperti yang diprogram di ./cmd/tbbmigrate/main.go. Anda akan melihat keluaran yang mirip dengan ini di terminal Anda:

Menyimpan Block baru ke disk:
{"hash":"8a05167d2f...","block":{"header":{...},"payload":[{...},{...}]}}
Menyimpan Block baru ke disk:
{"hash":"c70775ae5e...","block":{"header":{"parent":"8a05167d2f..."},"payload":[{...},{...}, ...]}}

PS: Hash block Anda akan berbeda karena atribut block.time diatur saat runtime - yaitu saat Anda menjalankan perintah tbbmigrate.


Ringkasan

Transaksi dikelompokkan ke dalam batch untuk alasan performa. Sekumpulan transaksi membentuk sebuah Block. Setiap block di-encode dan di-hash menggunakan fungsi hash kriptografi yang aman.

Block berisi Header dan Payload. Header menyimpan berbagai metadata seperti waktu dan referensi ke Parent Block (state database immutable sebelumnya). Payload membawa transaksi database yang baru.

TO BE CONTINUE...

Last updated