Hyperledger Fabric (HLF) adalah platform sumber terbuka yang menggunakan teknologi leded ledger (DLT), yang dirancang untuk mengembangkan aplikasi yang bekerja di lingkungan jaringan bisnis yang dibuat dan dikendalikan oleh konsorsium organisasi menggunakan aturan izin.
Platform ini mendukung kontrak pintar, dalam istilah HLF - kode rantai yang dibuat dalam bahasa umum seperti Golang, JavaScript, Java, tidak seperti, misalnya, Ethereum, yang menggunakan fungsionalitas terbatas yang berorientasi kontrak dan fungsionalitas Bahasa soliditas (LLL, Viper, dll.).

Pengembangan dan pengujian kode rantai, karena kebutuhan untuk menggunakan sejumlah besar komponen jaringan blockchain, dapat menjadi proses yang agak panjang dengan waktu yang dihabiskan untuk menguji perubahan. Artikel ini membahas pendekatan pengembangan cepat dan pengujian kontrak pintar HLF Golang menggunakan perpustakaan CCKit .
Aplikasi berbasis HLF
Dari sudut pandang pengembang, aplikasi blockchain terdiri dari dua bagian utama:
- On-chain - kontrak pintar (program) yang beroperasi di lingkungan terisolasi dari jaringan blockchain yang menentukan aturan untuk membuat dan komposisi atribut transaksi. Dalam kontrak pintar, tindakan utama adalah membaca, memperbarui, dan menghapus data dari keadaan jaringan blockchain. Harus ditekankan bahwa menghapus data dari keadaan meninggalkan informasi bahwa data ini ada.
- Off-chain adalah aplikasi (misalnya, API) yang berinteraksi dengan lingkungan blockchain melalui SDK. Interaksi dipahami sebagai panggilan fungsi kontrak pintar dan pemantauan peristiwa kontrak pintar - peristiwa eksternal dapat menyebabkan perubahan data dalam kontrak pintar, sementara peristiwa dalam kontrak pintar dapat memicu tindakan dalam sistem eksternal.
Data biasanya dibaca melalui simpul jaringan blockchain "home". Untuk merekam data, aplikasi mengirimkan permintaan ke node organisasi yang berpartisipasi dalam "kebijakan persetujuan" dari kontrak pintar tertentu.
Untuk mengembangkan kode off-chain (API, dll.), SDK khusus digunakan yang merangkum interaksi dengan node blockchain, mengumpulkan tanggapan, dll. Untuk HLF, ada implementasi SDK untuk Go ( 1 , 2 ), Node.Js dan Java
Komponen Kain Hyperledger
Saluran
Saluran adalah subnet node yang terpisah yang mendukung rantai blok terisolasi (buku besar), serta status saat ini (nilai kunci) rantai blok ( negara dunia ) yang digunakan untuk mengoperasikan kontrak pintar. Sebuah host dapat memiliki akses ke sejumlah saluran yang berubah-ubah.
Transaksi
Transaksi di Hyperledger Fabric adalah pembaruan atom dari keadaan rantai blok, hasil dari eksekusi metode chaincode. Suatu transaksi terdiri dari permintaan untuk memanggil metode chaincode dengan beberapa argumen (Proposal Transaksi) yang ditandatangani oleh node panggilan dan serangkaian respons (Respon Proposal Transaksi) dari node di mana transaksi itu โdikonfirmasiโ (Endorsement). Respons berisi informasi tentang pasangan nilai kunci yang berubah dari status rantai blok Baca-Tulis dan informasi layanan (tanda tangan dan sertifikat node yang mengkonfirmasikan transaksi). Karena rantai blok saluran individu dipisahkan secara fisik, transaksi hanya dapat dilakukan dalam konteks satu saluran.
Platform blockchain "klasik", seperti Bitcoin dan Ethereum , menggunakan siklus transaksi Pemesanan-Eksekusi yang dilakukan oleh semua node, yang membatasi skalabilitas jaringan blockchain.

Hyperledger Fabric menggunakan eksekusi transaksi dan arsitektur distribusi yang memiliki 3 operasi utama:
Eksekusi ( eksekusi ) - penciptaan oleh kontrak pintar yang berjalan pada satu atau beberapa node jaringan, transaksi - perubahan atom dalam keadaan registri terdistribusi ( dukungan )
Memesan - memesan dan mengelompokkan transaksi ke dalam blok oleh layanan pemesan khusus menggunakan algoritma konsensus pluggable.
Validasi - verifikasi oleh node jaringan transaksi yang berasal dari pemesan sebelum menempatkan informasi dari mereka dalam salinan registri terdistribusi

Pendekatan ini memungkinkan Anda untuk melakukan fase eksekusi transaksi sebelum memasuki jaringan blockchain, serta secara horizontal skala operasi node jaringan.
Kode rantai
Chaincode, yang juga bisa disebut kontrak pintar, adalah program yang ditulis dalam Golang, JavaScript (HLF 1.1+) atau Java (HLF 1.3+), yang mendefinisikan aturan untuk membuat transaksi yang mengubah keadaan rantai blok. Program ini dijalankan secara bersamaan pada beberapa node independen dari jaringan terdistribusi dari blockchain node, yang menciptakan lingkungan netral untuk pelaksanaan kontrak pintar dengan merekonsiliasi hasil program pada semua node yang diperlukan untuk "konfirmasi" transaksi.
Kode harus mengimplementasikan antarmuka yang terdiri dari metode:
type Chaincode interface {
- Metode Init dipanggil berdasarkan instantiasi atau peningkatan kode. Metode ini melakukan inisialisasi yang diperlukan dari keadaan kode kode. Penting untuk membedakan dalam kode metode apakah panggilan itu merupakan instantiasi atau upgrade, sehingga secara tidak sengaja Anda tidak menginisialisasi (mereset) data yang telah menerima keadaan tidak nol selama pengoperasian kode kode.
- Metode Invoke dipanggil ketika fungsi kode apa pun diakses. Metode ini berfungsi dengan status kontrak pintar.
Chaincode diinstal pada rekan-rekan jaringan blockchain. Pada tingkat sistem, setiap instance kode sesuai dengan wadah buruh pelabuhan yang terpasang pada node jaringan tertentu, yang melakukan pengiriman panggilan ke eksekusi kode.
Tidak seperti kontrak pintar Ethereum, logika rantai dapat diperbarui, tetapi ini mengharuskan semua node yang meng-host kode kode menginstal versi yang diperbarui.
Menanggapi panggilan ke fungsi chaincode dari luar melalui SDK, chaincode menciptakan perubahan dalam status rantai blok ( Set Baca-Tulis ), serta acara. Chaincode mengacu pada saluran tertentu dan dapat mengubah data hanya dalam satu saluran. Pada saat yang sama, jika host tempat kode diinstal juga memiliki akses ke saluran lain, dalam logika kode dapat membaca data dari saluran ini.
Kode rantai khusus untuk mengelola berbagai aspek pengoperasian jaringan blockchain disebut kode rantai sistem.
Kebijakan Pengesahan
Kebijakan persetujuan mendefinisikan aturan konsensus di tingkat transaksi yang dihasilkan oleh kode rantai tertentu. Kebijakan menetapkan aturan yang menentukan node saluran mana yang harus melakukan transaksi. Untuk melakukan ini, setiap node yang ditentukan dalam kebijakan persetujuan harus menjalankan metode chaining (langkah "Execute"), melakukan "simulasi", setelah itu hasil yang ditandatangani akan dikumpulkan dan diverifikasi oleh SDK yang memulai transaksi (semua hasil simulasi harus identik, tanda tangan semua node yang diperlukan oleh kebijakan harus ada). Selanjutnya, SDK mengirimkan transaksi ke pemesan , setelah semua node yang memiliki akses ke saluran akan menerima transaksi melalui pemesan dan melakukan langkah "Validasi". Penting untuk menekankan bahwa tidak semua node saluran harus berpartisipasi dalam langkah "Jalankan".
Kebijakan persetujuan ditentukan pada saat instantiate atau peningkatan kode. Dalam versi 1.3, menjadi mungkin untuk menetapkan kebijakan tidak hanya pada tingkat kode rantai, tetapi juga pada tingkat kunci dukungan individu berbasis negara . Contoh kebijakan persetujuan:
- Node A, B, C, D
- Sebagian besar saluran node
- Setidaknya 3 node dari A, B, C, D, E, F
Acara
Suatu peristiwa adalah kumpulan data bernama yang memungkinkan Anda untuk mempublikasikan "feed pembaruan" dari keadaan rantai blockchain. Set atribut event menentukan chaincode.
Infrastruktur jaringan
Tuan rumah (Peer)
Host terhubung ke sejumlah saluran yang sewenang-wenang yang memiliki hak akses. Tuan rumah memelihara versinya dari rantai blok dan keadaan rantai blok, dan juga menyediakan lingkungan untuk menjalankan kode rantai. Jika host bukan bagian dari kebijakan persetujuan, maka itu tidak harus diatur dengan kode rantai.
Pada tingkat perangkat lunak host, status rantai blok saat ini (negara dunia) dapat disimpan di LevelDB atau di CouchDB. Keuntungan CouchDB adalah dukungannya untuk kueri kaya menggunakan sintaksis MongoDB.
Pemesan
Layanan manajemen transaksi menerima transaksi yang ditandatangani sebagai input dan memastikan bahwa transaksi didistribusikan di seluruh node jaringan dalam urutan yang benar.
Pemesan tidak menjalankan kontrak pintar dan tidak mengandung rantai blok dan status rantai blok. Saat ini (1.3) ada dua implementasi dari orderer - solo pengembangan dan versi berdasarkan Kafka yang memberikan toleransi kesalahan kerusakan. Implementasi orderer yang mendukung resistensi terhadap perilaku yang salah dari sebagian kecil peserta (toleransi kesalahan Bizantium) diharapkan pada akhir 2018.
Layanan Identitas
Dalam jaringan Hyperledger Fabric, semua anggota memiliki identitas yang diketahui anggota lain (identitas). Untuk identifikasi, infrastruktur kunci publik (PKI) digunakan, di mana sertifikat X.509 dibuat untuk organisasi, elemen infrastruktur (simpul, pemesan), aplikasi, dan pengguna akhir. Akibatnya, akses untuk membaca dan memodifikasi data dapat dikendalikan oleh aturan akses di tingkat jaringan, pada satu saluran, atau dalam logika kontrak pintar. Dalam jaringan blockchain yang sama, beberapa layanan identifikasi dari berbagai jenis dapat bekerja secara bersamaan.
Implementasi kode kode
Chaincode dapat dianggap sebagai objek yang memiliki metode yang menerapkan logika bisnis tertentu. Tidak seperti OOP klasik, chaincode tidak dapat memiliki bidang atribut. Untuk bekerja dengan keadaan, penyimpanan yang disediakan oleh platform blockchain HLF, lapisan ChaincodeStubInterface digunakan , yang diteruskan ketika metode Init dan Invoke dipanggil. Ini memberikan kemampuan untuk menerima argumen panggilan fungsi dan membuat perubahan pada status rantai blok:
type ChaincodeStubInterface interface {
Dalam kontrak pintar Ethereum yang dikembangkan pada Solidity, setiap metode memiliki fungsi publik. Dalam rantai kode Fabric Hyperledger di metode Init dan Invoke menggunakan fungsi ChaincodeStubInterface . GetArgs () bisa mendapatkan argumen pemanggilan fungsi dalam bentuk array byte array, sedangkan elemen pertama array saat memanggil Invoke berisi nama fungsi kode kode. Karena Meminta metode chaincode apa pun melewati metode Invoke, kita dapat mengatakan bahwa ini adalah implementasi dari pola pengontrol depan.
Sebagai contoh, jika kita mempertimbangkan implementasi antarmuka Ethereum standar untuk token ERC-20 , kontrak pintar harus menerapkan metode:
- totalSuplai ()
- balanceOf (address _owner)
- transfer (alamat _to, uint256 _ nilai)
dan lain-lain. Dalam kasus implementasi HLF, kode fungsi Invoke harus mampu menangani kasus-kasus di mana argumen pertama untuk memanggil Panggilan berisi nama metode yang diharapkan (misalnya, "totalSuplai" atau "balanceOf"). Contoh penerapan standar ERC-20 dapat dilihat di sini .
Contoh Chaincode
Selain dokumentasi Hyperledger Fabric , ada beberapa contoh kode rantai:
Implementasi kode rantai dalam contoh-contoh ini agak bertele-tele dan mengandung banyak logika berulang untuk memilih fungsi routing yang disebut), memeriksa jumlah argumen, dan:
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() fmt.Println("invoke is running " + function)
Pengorganisasian kode semacam itu menyebabkan kemunduran dalam keterbacaan kode dan kemungkinan kesalahan, seperti ini , ketika Anda hanya lupa untuk menguraikan data input. Presentasi tentang rencana pengembangan HLF menyebutkan revisi pendekatan untuk pengembangan kode rantai, khususnya pengenalan anotasi dalam kode rantai Java, dll., Namun, rencana tersebut berkaitan dengan versi yang diharapkan hanya pada 2019. Pengalaman mengembangkan kontrak pintar telah mengarah pada kesimpulan bahwa mengembangkan dan menguji kode rantai akan lebih mudah jika Anda memilih fungsi dasar di perpustakaan yang terpisah.
CCKit - perpustakaan untuk mengembangkan dan menguji kode rantai
Perpustakaan CCKit merangkum praktik mengembangkan dan menguji kode rantai. Sebagai bagian dari pengembangan ekstensi chaincode , pustaka OpenZeppelin ekstensi untuk kontrak pintar Ethereum digunakan sebagai contoh. CCKit menggunakan solusi arsitektur berikut:
Mengalihkan panggilan ke fungsi kontrak pintar
Routing mengacu pada algoritma yang digunakan aplikasi merespons permintaan klien. Pendekatan ini digunakan, misalnya, di hampir semua kerangka kerja http. Router menggunakan aturan tertentu untuk mengikat permintaan dan penangan permintaan. Sehubungan dengan chaincode, ini untuk mengasosiasikan nama fungsi chaincode dengan fungsi handler.
Dalam contoh terbaru dari kontrak pintar, misalnya, di Aplikasi Asuransi , ini menggunakan pemetaan antara nama fungsi chaincode dan fungsi dalam kode Golang dari formulir:
var bcFunctions = map[string]func(shim.ChaincodeStubInterface, []string) pb.Response{
Router CCKit menggunakan pendekatan yang mirip dengan router http, serta kemampuan untuk menggunakan konteks permintaan untuk fungsi chaincode dan fungsi middleware
Konteks panggilan ke fungsi kode
Mirip dengan konteks permintaan http, yang biasanya memiliki akses ke parameter permintaan http, router CCKit menggunakan konteks panggilan ke fungsi kontrak pintar , yang merupakan abstraksi di atas shim.ChaincodeStubInterface . Konteksnya bisa menjadi satu-satunya argumen bagi penangan fungsi chaining, melaluinya, penangan bisa mendapatkan argumen pemanggilan fungsi, serta akses ke fungsi tambahan untuk bekerja dengan keadaan kontrak pintar (Negara), menciptakan jawaban (Respons), dll.
Context interface { Stub() shim.ChaincodeStubInterface Client() (cid.ClientIdentity, error) Response() Response Logger() *shim.ChaincodeLogger Path() string State() State Time() (time.Time, error) Args() InterfaceMap Arg(string) interface{} ArgString(string) string ArgBytes(string) []byte SetArg(string, interface{}) Get(string) interface{} Set(string, interface{}) SetEvent(string, interface{}) error }
Karena Konteks adalah antarmuka, dalam kode rantai tertentu dapat diperluas.
Fungsi Middleware
Fungsi pemrosesan antara (middleware) dipanggil sebelum panggilan dari pawang metode kode, memiliki akses ke konteks panggilan ke metode kode dan ke fungsi menengah berikutnya atau langsung ke pawang metode berikutnya (berikutnya). Middleware dapat digunakan untuk:
- mengonversi data input (dalam contoh di bawah ini. String dan p.Struct adalah middleware)
- pembatasan akses ke fungsi (misalnya, pemilik. Hanya )
- penyelesaian siklus pemrosesan permintaan
- memanggil fungsi pemrosesan antara berikutnya dari stack
Konversi struktur data
Antarmuka chaincode mengasumsikan bahwa array array byte disediakan untuk input, masing-masing elemen yang merupakan atribut dari fungsi chaincode. Untuk mencegah pembuatan data manual dari array byte ke tipe data golang (int, string, struktur, array) dari argumen pemanggilan fungsi di setiap handler dari fungsi chaining, tipe data yang diharapkan ditetapkan pada saat membuat aturan routing pada router CCKit dan tipe tersebut dikonversi secara otomatis . Pada contoh berikut ini , fungsi carGet mengharapkan argumen tipe string, dan fungsi carRegister mengharapkan struktur CarPayload . Argumen ini juga dinamai, yang memungkinkan pawang untuk mendapatkan nilainya dari konteks dengan nama. Contoh dari pawang akan diberikan di bawah ini. Protobuf juga dapat digunakan untuk menggambarkan skema data chaining.
r.Group(`car`). Query(`List`, cars).
Juga, konversi otomatis (marshalling) digunakan saat menulis data ke status kontrak pintar dan saat membuat acara (tipe golang diserialisasi ke dalam array byte)
Alat untuk debugging dan mencatat kode rantai
Untuk men-debug kode, Anda dapat menggunakan ekstensi debug , yang menerapkan metode kontrak pintar yang akan memungkinkan Anda memeriksa keberadaan kunci dalam status kontrak pintar, serta membaca / mengubah / menghapus nilai dengan kunci secara langsung.
Untuk masuk dalam konteks panggilan ke fungsi chaincode, metode Log () dapat digunakan, yang mengembalikan instance dari logger yang digunakan dalam HLF.
Metode kontrak pintar metode kontrol akses
Sebagai bagian dari ekstensi pemilik , primitif dasar untuk menyimpan informasi tentang pemilik kode rantai yang dipakai dan pengubah akses (middleware) untuk metode kontrak pintar diimplementasikan.
Alat Pengujian Kontrak Cerdas
Menyebarkan jaringan blockchain, menginstal dan menginisialisasi kode rantai adalah pengaturan yang agak rumit dan prosedur yang panjang. Waktu untuk menginstal ulang / meningkatkan kode kontrak pintar dapat dikurangi dengan menggunakan mode DEV dari kontrak pintar, namun, proses memperbarui kode masih akan lambat.
Paket shim berisi implementasi MockStub , yang membungkus panggilan ke kode untuk kode, mensimulasikan operasinya di lingkungan blockchain HLF. Menggunakan MockStub memungkinkan Anda untuk mendapatkan hasil tes hampir secara instan dan memungkinkan Anda untuk mengurangi waktu pengembangan. Jika kami mempertimbangkan skema umum pengoperasian kode dalam HLF, MockStub pada dasarnya menggantikan SDK, memungkinkan Anda untuk melakukan panggilan ke fungsi kode, dan mensimulasikan lingkungan untuk memulai kode pada host.

MockStub dari pengiriman HLF berisi implementasi hampir semua metode antarmuka shim.ChaincodeStubInterface , namun, dalam versi saat ini (1.3), tidak memiliki implementasi beberapa metode penting, seperti GetCreator. Karena kode rantai dapat menggunakan metode ini untuk mendapatkan sertifikat pencipta transaksi untuk kontrol akses, untuk cakupan maksimum dalam tes, kemampuan untuk memiliki rintisan metode ini penting.
Pustaka CCKit berisi versi diperpanjang MockStub , yang berisi implementasi metode yang hilang, serta metode untuk bekerja dengan saluran acara, dll.
Contoh Chaincode
Misalnya, mari kita buat kode rantai sederhana untuk menyimpan informasi tentang mobil yang terdaftar
Model data
Keadaan kode kode adalah penyimpanan nilai kunci, di mana kuncinya adalah string, nilainya adalah array byte. Praktik dasarnya adalah menyimpan instance struktur data golang yang diononisasi sebagai nilai. Dengan demikian, untuk bekerja dengan data dalam chaincode, setelah membaca dari negara bagian, Anda perlu menghapus array byte.
Untuk merekam tentang mobil, kami akan menggunakan set atribut berikut:
- Identifier (nomor mobil)
- Model mobil
- Informasi Pemilik Kendaraan
- Informasi waktu perubahan data
Untuk mentransfer data ke chaincode, buat struktur terpisah yang hanya berisi bidang yang berasal dari luar chaincode:
Bekerja dengan kunci
Rekam kunci dalam keadaan kontrak pintar adalah string. Ini juga mendukung kemampuan untuk membuat kunci komposit di mana bagian-bagian kunci dipisahkan oleh byte nol ( U + 0000 )
func CreateCompositeKey(objectType string, attributes []string) (string, error)
Di CCKit, fungsi bekerja dengan keadaan kontrak pintar dapat secara otomatis membuat kunci untuk catatan jika struktur yang ditransfer mendukung antarmuka Keyer
Untuk merekam mobil, fungsi pembuatan kunci adalah sebagai berikut:
const CarEntity = `CAR`
Deklarasi fungsi kontrak pintar (perutean)
Dalam metode konstruktor dari kode rantai, kita dapat mendefinisikan fungsi kode rantai dan argumennya. Akan ada 3 fungsi dalam kode registrasi mobil
- carList, mengembalikan susunan struktur Mobil
- carGet, menerima pengenal mobil dan mengembalikan struktur Mobil
- carRegister, menerima contoh serial dari struktur CarPayload dan mengembalikan hasil registrasi. Akses ke metode ini hanya dimungkinkan bagi pemilik chaincode, yang disimpan menggunakan middleware dari paket pemilik
func New() *router.Chaincode { r := router.New(`cars`)
Contoh di atas menggunakan struktur Chaincode di mana pemrosesan metode Init dan Invoke didelegasikan ke router:
package router import ( "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" )
Menggunakan router dan struktur Chaincode dasar memungkinkan penggunaan kembali fungsi handler. Misalnya, untuk menerapkan chaincode tanpa memeriksa akses ke fungsi carRegister
, carRegister
akan cukup untuk membuat metode konstruktor baru
Implementasi fungsi kontrak pintar
Fungsi Golang - penangan fungsi kontrak pintar di router CCKit dapat terdiri dari tiga jenis:
- StubHandlerFunc - antarmuka penangan standar, menerima shim.ChaincodeStubInterface , mengembalikan rekan respons standar.Response
- ContextHandlerFunc - mengambil konteks dan mengembalikan peer.Response
- HandlerFunc - mengambil konteks, mengembalikan antarmuka dan kesalahan. Array byte dapat dikembalikan atau jenis golang apa pun yang secara otomatis dikonversi ke array byte berdasarkan peer.Response dibuat. Status respons adalah shim.Ok atau shim.Error , tergantung pada kesalahan yang diteruskan.
, , ( CarPayload)
State , ( )
-
- โ , . BDD โ Behavior Driven Development, .
, , - Ethereum ganache-cli truffle . golang - Mockstub.
, . .
Ginkgo , Go, go test
. gomega expect , , .
import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" examplecert "github.com/s7techlab/cckit/examples/cert" "github.com/s7techlab/cckit/extensions/owner" "github.com/s7techlab/cckit/identity" "github.com/s7techlab/cckit/state" testcc "github.com/s7techlab/cckit/testing" expectcc "github.com/s7techlab/cckit/testing/expect" ) func TestCars(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Cars Suite") }
, CarPayload :
var Payloads = []*Car{{ Id: `A777MP77`, Title: `VAZ`, Owner: `victor`, }, { Id: `O888OO77`, Title: `YOMOBIL`, Owner: `alexander`, }, { Id: `O222OO177`, Title: `Lambo`, Owner: `hodl`, }}
MockStub Cars.
Karena cars , .
BeforeSuite Car authority Init . , Cars Init Init , .
BeforeSuite(func() {
. , CarRegister , .
It("Allow authority to add information about car", func() {
:
It("Disallow authority to add duplicate information about car", func() { expectcc.ResponseError( cc.From(actors[`authority`]).Invoke(`carRegister`, Payloads[0]), state.ErrKeyAlreadyExists)
Kesimpulan
- HLF Go, Java, JavaScript, , , - (Solidity) / -. / .
HLF , , ( .). Hypeledger Fabric , .. .