Belajar menulis kontrak pintar pada RIDE dan RIDE4DAPPS. Bagian 2 (DAO - Organisasi Otonomi Terdesentralisasi)


Halo semuanya!


Pada bagian pertama , kami memeriksa secara rinci cara membuat dan bekerja dengan dApp (aplikasi terdesentralisasi) di Waves RIDE IDE .


Mari kita coba sedikit contoh sekarang .


Tahap 3. Menguji akun dApp



Masalah apa yang langsung terlihat dengan Akun Alice dApp ?
Pertama:
Boob dan Cooper secara tidak sengaja dapat mengirim dana ke dApp menggunakan transaksi transfer normal, dan dengan demikian tidak akan dapat mengaksesnya kembali.

Kedua:
Kami tidak membatasi Alice untuk menarik dana tanpa persetujuan Boob dan / atau Cooper. Karena, perhatikan untuk memverifikasi, semua transaksi dari Alice akan dieksekusi.

Ketiga:
Siapa pun dapat melakukan operasi apa pun dari akun Alice hanya dengan mengganti publicKey - nya ke dalam transaksi:
const unsignedTransferTx = transfer({ amount: 1, recipient: '3P6fVra21KmTfWHBdib45iYV6aFduh4WwC2', //senderPublicKey is required if you omit seed senderPublicKey: '6nR7CXVV7Zmt9ew11BsNzSvVmuyM5PF6VPbWHW9BHgPq' }) 

Sayangnya, kontrak pintar Waves belum memungkinkan Anda untuk memblokir transaksi masuk di akun Anda, jadi Boob dan Cooper harus mengendalikan sendiri transaksi keluar mereka.


Mari kita perbaiki ke-2 dan ke-3 dengan menonaktifkan Alice untuk semua transaksi kecuali SetScriptTransaction , menonaktifkan sisanya dengan menentukan PublicKey-nya di @ Verifier. Artinya, kami hanya akan mengizinkan Alice, sebagai pengembang dApp, untuk memperbarui / memperbaiki kontrak pintar untuk sementara waktu.


Ya, Alice selalu dapat memperbarui skrip untuk mendapatkan lebih banyak hak dan mengelola cara "pengguna", tetapi hanya dia yang bisa melakukan ini dan semua pengguna akan melihat saat perubahan yang tidak sah pada kontrak dan akan dapat mengambil tindakan. Tetapi selama transaksi selain dari invokeScript tidak diblokir, klien harus dipercaya oleh Alice.

Sebarkan skrip yang diperbaiki:


 @Verifier(tx) func verify() = { match tx { case d: SetScriptTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], base58'x51ySWMyhE8G2AqJqmDpe3qoQM2aBwmieiJzZLK33JW') case _ => true } 

Kami mencoba untuk menarik koin dengan dApp Alice dan tanda tangannya. Kami mendapatkan kesalahan:



Kami mencoba menarik melalui penarikan:


 broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []})) 

Scriptnya bekerja dan dengan poin ke-2 kita sudah tahu!


Tahap 4. Kami membuat DAO dengan memilih


Sayangnya, bahasa RIDE belum menyediakan untuk bekerja dengan koleksi (kamus, kamus, iterator, reduksi, dll.). Namun, untuk operasi apa pun dengan koleksi nilai kunci datar, kami dapat merancang sistem untuk bekerja dengan string, masing-masing, dengan kunci dan dekripsi mereka.
String sangat mudah digabungkan, string dapat dibagi berdasarkan indeks.


Kami memiliki semua yang Anda butuhkan untuk menulis logika DAO dApp yang rumit!


Transaksi data

Transaksi Data:
β€œUkuran maksimum untuk sebuah kunci adalah 100 karakter, dan sebuah kunci dapat berisi titik kode Unicode yang sewenang-wenang termasuk spasi dan simbol yang tidak dapat dicetak lainnya. Nilai string memiliki batas 32.768 byte dan jumlah maksimum entri yang mungkin dalam transaksi data adalah 100. Secara keseluruhan, ukuran maksimum transaksi data adalah sekitar 140kb - untuk referensi, hampir persis panjang drama Shakespeare 'Romeo and Juliet'. ”


Buat DAO dengan ketentuan sebagai berikut:
Agar startup mendapatkan pendanaan, memanggil getFunds () memerlukan dukungan minimal 2 peserta - investor DAO. Dimungkinkan untuk menarik tepat jumlah total yang ditunjukkan oleh pemilik DAO.


Mari kita membuat 3 jenis kunci dan menambahkan logika untuk bekerja dengan saldo di 2 fungsi, pilih dan getFunds:
xx ... xx _ia = investor, saldo yang tersedia (suara, setoran, penarikan)
xx ... xx _sv = startup, jumlah suara (suara, getFunds)
xx ... xx _sf = startup, jumlah suara (suara, getFunds)
xx ... xx = alamat publik (35 karakter)

Catatan di Vote kami perlu memperbarui beberapa bidang sekaligus:


 WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]), 

WriteSet memungkinkan kita untuk membuat beberapa catatan sekaligus dalam satu transaksi invokeScript .


Ini adalah tampilannya di penyimpanan nilai kunci DAO dApp, setelah Bob dan Cooper mengisi ulang depositnya :



Fungsi setoran sedikit berubah:



Sekarang tiba momen terpenting dalam kegiatan DAO - memilih proyek untuk pendanaan.


Bob Memilih Proyek 500.000 Wavelet Neli:


 broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []})) 

Kode fungsi suara:


 @Callable(i) func vote(amount: Int, address: String) = { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let xxxStartupFund = address + "_" + "sf" let xxxStartupVotes = address + "_" + "sv" let flagKey = address + "_" + currentKey let flag = match getInteger(this, flagKey) { case a:Int => a case _ => 0 } let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let currentVotes = match getInteger(this, xxxStartupVotes) { case a:Int => a case _ => 0 } let currentFund = match getInteger(this, xxxStartupFund) { case a:Int => a case _ => 0 } if (amount <= 0) then throw("Can't withdraw negative amount") else if (amount > currentAmount) then throw("Not enough balance!") else if (flag > 0) then throw("Only one vote per project is possible!") else WriteSet([ DataEntry(xxxInvestorBalance, currentAmount - amount), DataEntry(xxxStartupVotes, currentVotes + 1), DataEntry(flagKey, 1), DataEntry(xxxStartupFund, currentFund + amount) ]) } 

Di gudang data, kami melihat semua entri yang diperlukan untuk alamat Neli:



Cooper juga memilih proyek Neli.



Mari kita lihat kode fungsi getFunds . Neli harus mengumpulkan minimal 2 suara untuk dapat menarik dana dari DAO.



Neli akan menarik setengah dari jumlah yang dipercayakan kepadanya:


 broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []})) 


Dia berhasil, yaitu, DAO bekerja!


Kami melihat proses pembuatan DAO dalam bahasa RIDE4DAPPS .
Di bagian-bagian berikut, kami akan membahas secara lebih rinci dengan refactoring kode dan pengujian kasus.


Versi lengkap dari kode di Waves RIDE IDE:


 # In this example multiple accounts can deposit their funds to DAO and safely take them back, no one can interfere with this. # DAO participants can also vote for particular addresses and let them withdraw invested funds then quorum has reached. # An inner state is maintained as mapping `address=>waves`. # https://medium.com/waves-lab/waves-announces-funding-for-ride-for-dapps-developers-f724095fdbe1 # You can try this contract by following commands in the IDE (ide.wavesplatform.com) # Run commands as listed below # From account #0: # deploy() # From account #1: deposit funds # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"deposit",args:[]}, payment: [{amount: 100000000, asset:null }]})) # From account #2: deposit funds # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"deposit",args:[]}, payment: [{amount: 100000000, asset:null }]})) # From account #1: vote for startup # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []})) # From account #2: vote for startup # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []})) # From account #3: get invested funds # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []})) {-# STDLIB_VERSION 3 #-} {-# CONTENT_TYPE DAPP #-} {-# SCRIPT_TYPE ACCOUNT #-} @Callable(i) func deposit() = { let pmt = extract(i.payment) if (isDefined(pmt.assetId)) then throw("can hodl waves only at the moment") else { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let newAmount = currentAmount + pmt.amount WriteSet([DataEntry(xxxInvestorBalance, newAmount)]) } } @Callable(i) func withdraw(amount: Int) = { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let newAmount = currentAmount - amount if (amount < 0) then throw("Can't withdraw negative amount") else if (newAmount < 0) then throw("Not enough balance") else ScriptResult( WriteSet([DataEntry(xxxInvestorBalance, newAmount)]), TransferSet([ScriptTransfer(i.caller, amount, unit)]) ) } @Callable(i) func getFunds(amount: Int) = { let quorum = 2 let currentKey = toBase58String(i.caller.bytes) let xxxStartupFund = currentKey + "_" + "sf" let xxxStartupVotes = currentKey + "_" + "sv" let currentAmount = match getInteger(this, xxxStartupFund) { case a:Int => a case _ => 0 } let totalVotes = match getInteger(this, xxxStartupVotes) { case a:Int => a case _ => 0 } let newAmount = currentAmount - amount if (amount < 0) then throw("Can't withdraw negative amount") else if (newAmount < 0) then throw("Not enough balance") else if (totalVotes < quorum) then throw("Not enough votes. At least 2 votes required!") else ScriptResult( WriteSet([ DataEntry(xxxStartupFund, newAmount) ]), TransferSet([ScriptTransfer(i.caller, amount, unit)]) ) } @Callable(i) func vote(amount: Int, address: String) = { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let xxxStartupFund = address + "_" + "sf" let xxxStartupVotes = address + "_" + "sv" let flagKey = address + "_" + currentKey let flag = match getInteger(this, flagKey) { case a:Int => a case _ => 0 } let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let currentVotes = match getInteger(this, xxxStartupVotes) { case a:Int => a case _ => 0 } let currentFund = match getInteger(this, xxxStartupFund) { case a:Int => a case _ => 0 } if (amount <= 0) then throw("Can't withdraw negative amount") else if (amount > currentAmount) then throw("Not enough balance!") else if (flag > 0) then throw("Only one vote per project is possible!") else WriteSet([ DataEntry(xxxInvestorBalance, currentAmount - amount), DataEntry(xxxStartupVotes, currentVotes + 1), DataEntry(flagKey, 1), DataEntry(xxxStartupFund, currentFund + amount) ]) } @Verifier(tx) func verify() = { match tx { case d: SetScriptTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], base58'x51ySWMyhE8G2AqJqmDpe3qoQM2aBwmieiJzZLK33JW') case _ => false } } 



Bagian pertama
Kode github
Gelombang IDE NAIK
Pengumuman Program Hibah

Source: https://habr.com/ru/post/id447808/


All Articles