Gim “Serang 51%”: menulis gim mandiri sederhana di platform Obyte



Dalam artikel terakhir "Agen Otonomi" atau kami mengeksekusi kode di platform cryptopl terbuka Obyte, kami berbicara tentang apa itu Agen Otonom dan membandingkannya dengan kontrak pintar Ethereum. Sekarang mari kita menulis Agen Otonomi pertama kami (AA) menggunakan contoh Serangan 51%. Dan di akhir artikel, kami akan menganalisis cara untuk memperbaikinya: bagaimana melindungi pemain dari kehilangan / kehilangan dana dan bagaimana meningkatkan algoritma untuk mengurangi efek "paus" dengan deposit besar pada hasil permainan.

Sumber asli dari kedua agen yang berdiri sendiri selalu tersedia di editor kode Oscript online dalam bentuk template, cukup pilih mereka dari menu drop-down: “51% game serangan” dan “Proksi Penggalangan Dana”.

Sebelum Anda mulai menulis AA dalam Oscript, saya sangat merekomendasikan membaca Panduan Memulai (ind) dalam dokumentasi kami untuk segera menjadi akrab dengan prinsip dasar penulisan AA.

Inti dari permainan


Beberapa tim secara bersamaan bersaing satu sama lain untuk mendapatkan hak mengumpulkan seluruh kumpulan dana yang terkumpul. Pemain tim melakukan deposit, sehingga meningkatkan jumlah total pool. Tim yang telah melakukan setoran setidaknya 51% dari semua dana yang dikumpulkan dan bertahan sebagai pemimpin untuk kemenangan setidaknya satu hari. Kelompok ini didistribusikan di antara para pemain tim pemenang sesuai dengan kontribusi yang dibuat, sehingga setiap peserta berpotensi menggandakan investasi mereka.

Segera setelah salah satu tim mengumpulkan> = 51% dari semua dana, sebelumnya dinyatakan sebagai pemenang dan pesertanya tidak dapat lagi melakukan setoran. Tetapi semua tim lain memiliki 24 jam untuk menyalip tim yang menang. Segera setelah ini terjadi, tim penyalip sekarang menjadi pemenang dan penghitung waktu memulai lagi.

Implementasi kami akan menerbitkan "saham" untuk setiap deposan dengan imbalan byte, dalam rasio 1 banding 1, yang, jika tim menang, peserta dapat menukar dengan saham di pool yang dimenangkan. Saham adalah aset pada platform Obyte, dirilis khusus untuk setiap tim. Mereka, seperti halnya aset apa pun, dapat ditransfer / diperdagangkan, menjual potensi kemenangan mereka kepada orang lain. Harga saham akan tergantung pada pasar yang mengevaluasi peluang tim untuk menang.

Siapa pun dapat membuat tim. Pembuat tim dapat menetapkan komisi untuk menang, yang akan dibebankan dari setiap anggota tim jika menang.

Kami juga akan menerapkan AA kedua dalam permainan kami, atas nama pencipta tim, yang akan "mengumpulkan dana" jumlah yang diperlukan, dan hanya jika itu tercapai (dalam contoh kami, ketika mencapai> 51% dari kumpulan permainan) akan mengirim semua dana yang terkumpul ke alamat AA permainan, jika tidak uang dapat diambil kembali dengan bebas.

Menulis Kode Naskah


Biarkan saya mengingatkan Anda bahwa kode AA dipanggil setiap kali transaksi tiba di alamat AA ini, yang disebut memicu transaksi. Sebenarnya, AA adalah kode yang, sebagai respons terhadap input (data dalam transaksi pemicu dan status AA saat itu sendiri, disimpan dalam variabel keadaan) menghasilkan output (transaksi "respons" lain), atau mengubah statusnya. Tugas kita adalah memprogram aturan respons AA terhadap input data. Setiap transaksi dalam Obyte adalah serangkaian pesan, paling sering berupa pesan "pembayaran", atau pesan "data", dll.

Inisialisasi


Pertama, kita menginisialisasi AA kita. Blok init disebut setiap kali AA dimulai, di bagian paling awal. Di dalamnya kita akan menetapkan konstanta lokal untuk akses yang lebih mudah ke nilai-nilai.

{     init: `{         $team_creation_fee = 5000;         $challenging_period = 24*3600;         $bFinished = var['finished'];     }`,     messages: { cases: [ ]     } } 

String $ bFinished = var ['selesai']; membaca variabel jadi dari negara AA.
Array dari pesan yang dihasilkan akan dibingkai oleh case: blok {} , yang mirip dengan blok switch / case / default yang biasa , berdasarkan pada kondisi di jika child block, hanya satu dari pesan yang akan dipilih, atau jika tidak ada jika blok kembali benar , itu akan menjadi pesan terakhir dipilih.

Kami membuat tim


Jadi, blok pertama akan memproses transaksi untuk membuat perintah baru:

 // create a new team; any excess amount is sent back if: `{trigger.data.create_team AND !$bFinished}`, init: `{ if (var['team_' || trigger.address || '_asset']) bounce('you already have a team'); if (trigger.output[[asset=base]] < $team_creation_fee) bounce('not enough to pay for team creation'); }`, messages: [ { app: 'asset', payload: { is_private: false, is_transferrable: true, auto_destroy: false, fixed_denominations: false, issued_by_definer_only: true, cosigned_by_definer: false, spender_attested: false } }, { app: 'payment', if: `{trigger.output[[asset=base]] > $team_creation_fee}`, payload: { asset: 'base', outputs: [ {address: "{trigger.address}", amount: "{trigger.output[[asset=base]] - $team_creation_fee}"} ] } }, { app: 'state', state: `{ var['team_' || trigger.address || '_founder_tax'] = trigger.data.founder_tax otherwise 0; var['team_' || trigger.address || '_asset'] = response_unit; response['team_asset'] = response_unit; }` } ] 

Seperti yang kita lihat, kondisi untuk pelaksanaan blok ini adalah yang pertama di blok if dan merupakan cek bahwa transaksi pemicu berisi pesan dengan tipe data ( trigger.data.create_team ), yang memiliki kunci bernama create_team dan permainan belum berakhir ( ! $ bFinished ). Konstanta lokal $ bFinished dapat diakses dari mana saja dalam kode jika telah diinisialisasi dalam blok init. Jika setidaknya salah satu dari kondisi ini tidak terpenuhi, maka blok kasus induk hanya akan terus mengeksekusi dan memeriksa kondisi untuk pesan berikut, melewatkan yang ini.

Di blok init berikutnya, kami tidak menginisialisasi apa pun, tetapi kami memeriksa kondisi yang diperlukan, yang tanpanya transaksi pemicu dianggap salah:

 if (var['team_' || trigger.address || '_asset']) bounce('you already have a team'); if (trigger.output[[asset=base]] < $team_creation_fee) bounce('not enough to pay for team creation'); 

Di sini kita menggabungkan (menggunakan ||) string dengan variabel dari pemicu transaksi dan mencoba mencari tahu apakah ada variabel bernama 'team_' || trigger.address || '_asset' dalam cerita AA kami.

Panggilan bouncing () mengembalikan semua perubahan yang dibuat pada saat ini dan mengembalikan kesalahan ke pemanggil.

Perhatikan juga bagaimana pencarian di dalam transaksi pemicu dilakukan : trigger.output [[aset = basis]] mencari output dengan aset == basis , yang akan mengembalikan jumlah dalam byte (aset dasar = byte) yang ditentukan dalam transaksi pemicu. Dan jika jumlah ini tidak cukup untuk membuat tim baru, kami menyebutnya bounce (), dengan diam-diam memakan semua byte yang masuk dikurangi bounce_fee, yang secara default adalah 10.000 byte.

Selanjutnya, sebagian besar kode pembangunan tim dimulai. Secara singkat, algoritma ini adalah sebagai berikut:

  1. Pesan pertama merilis aset baru ( aplikasi: 'aset' )
  2. Pesan kedua mengembalikan semua yang lebih dari jumlah byte yang diperlukan untuk membuat perintah ( aplikasi: 'pembayaran' ). Lihat blok if di sini. Jika kondisi ini salah (pencipta mengirim persis jumlah byte yang diperlukan), maka pesan ini tidak akan dimasukkan dalam transaksi yang dihasilkan, tetapi hanya akan dibuang.
  3. Pesan ketiga mengubah status AA kami ( aplikasi: 'status' ): tulis founder_tax yang diteruskan sebagai argumen, atau atur ke 0 jika tidak diteruskan ke transaksi pemicu. Konstruksi var1 sebaliknya var2 mengembalikan var1 jika dilemparkan ke true, jika tidak maka mengembalikan var2. Di sini kita bertemu variabel response_unit, yang selalu berisi hash dari unit yang dihasilkan. Dalam hal ini, karena unit yang dihasilkan akan membuat aset baru yang disebut aset-a dan akan menjadi hash dari unit pembuat. Respons string ['team_asset'] = response_unit cukup menulis hash yang sama (atau aset untuk perintah yang diberikan) ke array responseVars di unit akhir. Array respons juga dapat dibacakan kepada orang yang melakukan transaksi pemicu, serta pada saat pendengar berlangganan acara dengan AA ini.

Kami menerima setoran


Setelah pembuatan tim selesai, pergi ke blok berikutnya - memproses setoran dari anggota tim.

 // contribute to a team if: `{trigger.data.team AND !$bFinished}`, init: `{ if (!var['team_' || trigger.data.team || '_asset']) bounce('no such team'); if (var['winner'] AND var['winner'] == trigger.data.team) bounce('contributions to candidate winner team are not allowed'); }`, messages: [ { app: 'payment', payload: { asset: `{var['team_' || trigger.data.team || '_asset']}`, outputs: [ {address: "{trigger.address}", amount: "{trigger.output[[asset=base]]}"} ] } }, { app: 'state', state: `{ var['team_' || trigger.data.team || '_amount'] += trigger.output[[asset=base]]; if (var['team_' || trigger.data.team || '_amount'] > balance[base]*0.51){ var['winner'] = trigger.data.team; var['challenging_period_start_ts'] = timestamp; } }` } ] 

Dari hal baru yang kami temui di sini adalah penerbitan token aset dari tim yang dipilih kepada pesertanya dengan imbalan setoran dalam byte:

 asset: `{var['team_' || trigger.data.team || '_asset']}`, outputs: [{address: "{trigger.address}", amount: "{trigger.output[[asset=base]]}"} 

Seperti yang kita ingat, variabel status 'team_' || trigger.data.team || Kami menyimpan '_asset' pada tahap membuat tim, dan menyimpan hash dari unit di mana kami menciptakan aset untuk tim ini, yaitu, nama aset ini-a.

Di blok yang sama, kondisi utama diperiksa 51%:

 if (var['team_' || trigger.data.team || '_amount'] > balance[base]*0.51){ var['winner'] = trigger.data.team; var['challenging_period_start_ts'] = timestamp; } 

Jika setelah transaksi pemicu ini, saldo tim yang ditentukan melebihi 51%, maka tim dinyatakan sebagai pemenang dan kami mencatat cap waktu unix saat ini (mulai penghitung waktu).

Stempel waktu ini akan diperiksa di blok ketiga ketika kami menerima transaksi pemicu dengan upaya untuk mengakhiri permainan:

 // finish the challenging period and set the winner if: `{trigger.data.finish AND !$bFinished}`, init: `{ if (!var['winner']) bounce('no candidate winner yet'); if (timestamp < var['challenging_period_start_ts'] + $challenging_period) bounce('challenging period not expired yet'); }`, messages: [ { app: 'state', state: `{ var['finished'] = 1; var['total'] = balance[base]; var['challenging_period_start_ts'] = false; response['winner'] = var['winner']; }` } ] 

Kami membayar hadiah


Dan blok terakhir, yang paling menyenangkan, adalah pembayaran seluruh deposit kepada pemenang:

 // pay out the winnings if: `{ if (!$bFinished) return false; $winner = var['winner']; $winner_asset = var['team_' || $winner || '_asset']; $asset_amount = trigger.output[[asset=$winner_asset]]; $asset_amount > 0 }`, init: `{ $share = $asset_amount / var['team_' || $winner || '_amount']; $founder_tax = var['team_' || $winner || '_founder_tax']; $amount = round(( $share * (1-$founder_tax) + (trigger.address == $winner AND !var['founder_tax_paid'] ? $founder_tax : 0) ) * var['total']); }`, messages: [ { app: 'payment', payload: { asset: "base", outputs: [ {address: "{trigger.address}", amount: "{$amount}"} ] } }, { app: 'state', state: `{ if (trigger.address == $winner) var['founder_tax_paid'] = 1; }` } ] 

Blok inisialisasi menarik di sini, di mana kami menghitung nilai yang diperlukan sebelumnya:

  $share = $asset_amount / var['team_' || $winner || '_amount']; $founder_tax = var['team_' || $winner || '_founder_tax']; $amount = round(( $share * (1-$founder_tax) + (trigger.address == $winner AND !var['founder_tax_paid'] ? $founder_tax : 0) ) * var['total']); 

Semua peserta tim, kecuali penciptanya, dibayar sejumlah yang proporsional dengan kontribusi awal mereka (1-by-1 byte sebagai ganti token aset yang dikirim dalam transaksi pemicu). Pencipta juga dibayar komisi (diverifikasi bahwa alamat orang yang mengirim transaksi pemicu sama dengan alamat pencipta tim pemenang trigger.address == $ pemenang ). Penting untuk tidak lupa bahwa komisi harus dibayar hanya sekali, dan pencipta dapat mengirim banyak transaksi pemicu, jadi kami menyimpan bendera di negara AA.

Mulai game


Jadi, kodenya sudah siap. Ini daftar lengkapnya:

kode AA lengkap
 { init: `{ $team_creation_fee = 5000; $challenging_period = 24*3600; $bFinished = var['finished']; }`, messages: { cases: [ { // create a new team; any excess amount is sent back if: `{trigger.data.create_team AND !$bFinished}`, init: `{ if (var['team_' || trigger.address || '_amount']) bounce('you already have a team'); if (trigger.output[[asset=base]] < $team_creation_fee) bounce('not enough to pay for team creation'); }`, messages: [ { app: 'asset', payload: { is_private: false, is_transferrable: true, auto_destroy: false, fixed_denominations: false, issued_by_definer_only: true, cosigned_by_definer: false, spender_attested: false } }, { app: 'payment', if: `{trigger.output[[asset=base]] > $team_creation_fee}`, payload: { asset: 'base', outputs: [ {address: "{trigger.address}", amount: "{trigger.output[[asset=base]] - $team_creation_fee}"} ] } }, { app: 'state', state: `{ var['team_' || trigger.address || '_founder_tax'] = trigger.data.founder_tax otherwise 0; var['team_' || trigger.address || '_asset'] = response_unit; response['team_asset'] = response_unit; }` } ] }, { // contribute to a team if: `{trigger.data.team AND !$bFinished}`, init: `{ if (!var['team_' || trigger.data.team || '_asset']) bounce('no such team'); if (var['winner'] AND var['winner'] == trigger.data.team) bounce('contributions to candidate winner team are not allowed'); }`, messages: [ { app: 'payment', payload: { asset: `{var['team_' || trigger.data.team || '_asset']}`, outputs: [ {address: "{trigger.address}", amount: "{trigger.output[[asset=base]]}"} ] } }, { app: 'state', state: `{ var['team_' || trigger.data.team || '_amount'] += trigger.output[[asset=base]]; if (var['team_' || trigger.data.team || '_amount'] > balance[base]*0.51){ var['winner'] = trigger.data.team; var['challenging_period_start_ts'] = timestamp; } }` } ] }, { // finish the challenging period and set the winner if: `{trigger.data.finish AND !$bFinished}`, init: `{ if (!var['winner']) bounce('no candidate winner yet'); if (timestamp < var['challenging_period_start_ts'] + $challenging_period) bounce('challenging period not expired yet'); }`, messages: [ { app: 'state', state: `{ var['finished'] = 1; var['total'] = balance[base]; var['challenging_period_start_ts'] = false; response['winner'] = var['winner']; }` } ] }, { // pay out the winnings if: `{ if (!$bFinished) return false; $winner = var['winner']; $winner_asset = var['team_' || $winner || '_asset']; $asset_amount = trigger.output[[asset=$winner_asset]]; $asset_amount > 0 }`, init: `{ $share = $asset_amount / var['team_' || $winner || '_amount']; $founder_tax = var['team_' || $winner || '_founder_tax']; $amount = round(( $share * (1-$founder_tax) + (trigger.address == $winner AND !var['founder_tax_paid'] ? $founder_tax : 0) ) * var['total']); }`, messages: [ { app: 'payment', payload: { asset: "base", outputs: [ {address: "{trigger.address}", amount: "{$amount}"} ] } }, { app: 'state', state: `{ if (trigger.address == $winner) var['founder_tax_paid'] = 1; }` } ] } ] } } 


Mari kita periksa kode untuk validitas dan coba gunakan di testnet.

  1. Buka editor online: https://testnet.oscript.org
  2. Rekatkan kode kami dan klik Validasi. Jika semuanya benar, kita akan melihat perhitungan kompleksitas kode: AA divalidasi, kompleksitas = 27, ops = 176 . Di sini ops adalah jumlah operasi dalam kode kita, kompleksitas adalah kompleksitas kode. AAs Obyte tidak memiliki siklus, tetapi bahkan ini tidak memungkinkan perlindungan 100% jaringan dari AAs jahat dengan kode buruk. Oleh karena itu, semua AA memiliki batas atas kompleksitas, kompleksitas = 100. Kompleksitas kode dihitung pada saat penyebaran, dan semua cabang kode diperhitungkan. Beberapa operasi relatif mudah, seperti ± dan lainnya, dan tidak menambah kerumitan. Lainnya, seperti akses ke database (modifikasi negara) atau perhitungan kompleks (memanggil beberapa fungsi) menambah kompleksitas. Untuk mengetahui operasi mana yang mudah dan mana yang kompleks, lihat referensi bahasa .
  3. Klik Menyebarkan. Kami melihat sesuatu yang mirip

 Check in explorer: https://testnetexplorer.obyte.org/#DiuxsmIijzkfAVgabS9chJm5Mflr74lZkTGud4PM1vI= Agent address: 6R7SF6LTCNSPLYLIJWDF57BTQVZ7HT5N 

Saya menyarankan Anda untuk mengikuti tautan di penjelajah dan memastikan bahwa unit dengan AA kami diposting ke jaringan. Explorer juga menunjukkan kode lengkap untuk AA kami, seperti semua AA di jaringan Obyte adalah open source. Anda dapat mempelajari kode agen apa pun di alamatnya.

Crowdfunding


Dan sekarang untuk optimasi yang dijanjikan. Dalam implementasi saat ini, setiap pemain segera mengirim uang ke alamat AA permainan, menunjukkan timnya. Pada saat yang sama, tim mungkin tidak pernah menjadi pemimpin, dan uang sudah dikirim. Kami dapat mengoptimalkan proses pengumpulan uang dan menghindari situasi bahwa kami mengirim uang ke tim yang tidak akan pernah menjadi pemenang. Ini dapat dilakukan dengan menggunakan AA kedua, mengatur apa yang disebut crowdfunding dana dengan menetapkan tujuan dinamis untuk jumlah dana yang dikumpulkan sama dengan 51% dari jumlah dalam permainan.

Di Oscript, kita dapat membaca status AA lainnya, sehingga kita memiliki kesempatan untuk menetapkan tujuan dinamis dalam AA crowdfunding kita.

Algoritma akan menjadi sebagai berikut: pencipta tim meminta pemain untuk mengirim uang bukan ke alamat permainan, tetapi ke alamat agen yang mengimplementasikan fungsi crowdfunding. Agen ini akan menyimpan jumlah byte yang dikumpulkan di rumah dan mengirimkannya ke game hanya jika ia mengumpulkan> = 51% dari jumlah dalam game. Dan segera tim ini menjadi pemimpin. Jika jumlah yang diperlukan tidak dikumpulkan, maka uang hanya akan dikembalikan ke para pemain. Pada tahap penggalangan dana, pemain tidak akan menerima token permainan tim, tetapi token crowdfunding, yang di masa depan dapat dikembalikan atau ditukar dengan token permainan, jika berhasil.

Pada artikel selanjutnya, kami menerapkan fungsi ini.

Kami mengumpulkan bukan byte, tetapi sertifikasi


Dalam bentuk paling sederhana dari permainan "Serang 51%" kita berbicara tentang jumlah dana yang terkumpul. Dengan demikian, "paus" dengan dompet besar dapat mengambil sebagian besar kemenangan.

Untuk membuat permainan lebih jujur ​​untuk semua peserta, kami akan mencoba untuk menghitung bukan jumlah byte yang dikirim, tetapi jumlah peserta dalam tim. Tim yang mampu menarik jumlah peserta maksimum menang. Setiap pemain akan menginvestasikan jumlah tetap, katakan 1GB, dan dihitung sebagai satu unit dalam kumpulan tim. Tetapi tidak ada yang menghalangi kita untuk membuat alamat baru dalam jumlah tak terbatas, sehingga kondisi kritisnya adalah bahwa hanya alamat yang terikat pada beberapa ID lain yang diizinkan dalam permainan, di mana aturan "satu orang - satu ID" dihormati. Ikatan semacam itu disebut sertifikasi . Contoh sertifikasi adalah jalannya prosedur KYC, setelah itu sebuah pesan dikirimkan ke DAG tentang hubungan antara alamat Obyte dan hash data pribadi (data pribadi itu sendiri disimpan di dompet pengguna, dan ia dapat mengungkapkannya kepada rekanan individu jika ia mau, tetapi untuk tugas ini mereka tidak perlu, itu penting. hanya fakta yang mengikat). Contoh lain dari sertifikasi adalah sertifikasi email pada domain di mana aturan "satu orang - satu email" dihormati, misalnya, pada domain beberapa universitas, perusahaan dan negara. Dengan demikian, satu orang sungguhan hanya akan dihitung satu kali.

Agen Otonomi dapat meminta status sertifikasi alamat di jaringan, sehingga akan ada jumlah minimum perubahan dalam kode.

Saya menyarankan pembaca di komentar menyarankan di tempat mana dan bagaimana tepatnya perlu untuk mengubah baris kode dari versi game saat ini untuk menerapkan ini. Untuk membantu, seperti biasa, Referensi Bahasa Oscript .

Perselisihan & Twitter Kami

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


All Articles