Lição 1

Pendahuluan dan Kontrak Multisig

Kontrak multisignature (multisig), juga dikenal sebagai kontrak "M-of-N", adalah mekanisme penting yang digunakan untuk meningkatkan keamanan dan fleksibilitas transaksi di lingkungan blockchain. Kontrak ini mengubah cara pengendalian dilakukan atas aset dengan memerlukan persetujuan dari banyak pihak sebelum transaksi dapat dilaksanakan. Istilah "M-of-N" mengacu pada persyaratan bahwa M dari N total pihak harus menyetujui transaksi agar sah.

Teori Kontrak Multisig

Kontrak multisig menyediakan sarana untuk menciptakan kendali bersama atas aset. Kasus penggunaan yang umum melibatkan layanan escrow, manajemen akun perusahaan, penandatanganan perjanjian keuangan bersama, dan banyak lagi. Kontrak-kontrak ini sangat bermanfaat bagi organisasi atau kelompok yang memerlukan pengambilan keputusan kolektif.

Secara desain, kontrak multisig tahan terhadap kerusakan dan mencegah satu titik kegagalan. Bahkan jika kunci salah satu pihak disusupi, penyerang tidak dapat melakukan transaksi tanpa persetujuan pihak lain. Ini menambah lapisan keamanan ekstra.

Kontrak multisig dapat dianggap setara secara digital dengan brankas yang memerlukan banyak kunci untuk dibuka. Jumlah total kunci (N) dan jumlah minimum kunci yang diperlukan untuk membuka kotak (M) disepakati saat kontrak dibuat.

Kontrak multisig dapat memiliki banyak konfigurasi berbeda bergantung pada nilai M dan N:

  • 1-of-N: Satu pihak dari total dapat menyetujui transaksi tersebut. Konfigurasi ini sama seperti transaksi biasa tanpa multisig. Ini mungkin digunakan jika ada beberapa kunci untuk kenyamanan, namun salah satu dari kunci tersebut dapat menyetujui transaksi.
  • N-of-N: Semua pihak harus menyetujui transaksi tersebut. Konfigurasi ini memberikan tingkat keamanan tertinggi namun dapat menjadi masalah jika salah satu pihak kehilangan kuncinya atau menolak menyetujui transaksi.
  • M-of-N (di mana M < N): Sebagian dari total pihak harus menyetujui transaksi tersebut. Konfigurasi ini sering digunakan dalam praktik karena menyeimbangkan keamanan dengan fleksibilitas.

Kontrak Multisig di Blockchain

Dalam konteks blockchain, kontrak multisig digunakan secara luas untuk meningkatkan keamanan transaksi, mendukung mekanisme tata kelola yang kompleks, atau mempertahankan kontrol yang fleksibel atas aset blockchain. Berikut beberapa contohnya:

  • Dompet: Dompet multisig digunakan untuk mengamankan aset. Mereka mengharuskan banyak pihak untuk menandatangani transaksi, sehingga memberikan keamanan tambahan terhadap pencurian, peretasan eksternal, dan ancaman orang dalam.
  • Organisasi Otonomi Terdesentralisasi (DAO): DAO sering kali menggunakan kontrak multisig untuk menegakkan aturan tata kelola mereka. Pemungutan suara pada proposal diimplementasikan sebagai transaksi multisig, dengan anggota DAO bertindak sebagai penandatangan. Proposal tersebut dilaksanakan hanya jika mendapat suara yang cukup.
  • Operasi lintas rantai: Dalam operasi lintas rantai, kontrak multisig dapat bertindak sebagai penjaga aset. Ketika aset dipindahkan dari satu blockchain ke blockchain lainnya, kontrak multisig pada rantai asal dapat memastikan bahwa aset tersebut terkunci dengan aman hingga operasi pada rantai lainnya dikonfirmasi.
    Meskipun penerapan kontrak multisig dapat bervariasi dari satu blockchain ke blockchain lainnya, konsep intinya tetap sama – perlunya banyak pihak untuk menyetujui transaksi sebelum dieksekusi. Lapisan keamanan tambahan ini menjadikan kontrak multisig sebagai alat penting dalam ruang blockchain dan kripto.

Contoh Pengkodean: Menulis dan Menerapkan Kontrak Multisig dengan SmartPy

Untuk contoh kode, kita akan melihat tiga implementasi kontrak multisignature yang berbeda:

Kontrak Lambda

Ini cukup serbaguna dan memungkinkan berbagai kegunaan. Dibutuhkan banyak tanda tangan untuk menjalankan fungsi lambda sewenang-wenang.

Python 
 impor smartpy sebagai sp 


 @sp.module 
 def main(): 
 operasi_lambda: type = sp.lambda_(sp.unit, sp.unit, with_operations=True) 

 class MultisigLambda(sp.Contract): 
 """Beberapa anggota memilih untuk mengeksekusi lambda.

        Kontrak ini dapat dibuat dengan daftar alamat dan jumlah 
 suara yang diperlukan. Setiap anggota dapat mengirimkan lambda sebanyak yang dia inginkan dan memilih 
 untuk proposal aktif. Ketika lambda mencapai suara yang diperlukan, kodenya 
 dipanggil dan operasi keluaran dijalankan. Hal ini memungkinkan kontrak ini untuk 
 melakukan apa pun yang dapat dilakukan kontrak: mentransfer token, mengelola aset, 
 mengelola kontrak lain...

        Ketika lambda diterapkan, semua lambda yang dikirimkan hingga saat ini akan dinonaktifkan.
        Anggota masih dapat mengirimkan lambda baru.
        """ 

 def __init__(mandiri, anggota, diperlukan_votes): 
 """Konstruktor 

 Args: 
 anggota (sp.set dari sp.address): orang yang dapat mengirimkan dan memilih 
 untuk lambda.
                diperlukan_suara (sp.nat): jumlah suara yang diperlukan 
 """ 
 menegaskan suara_yang diperlukan <= sp.len( 
 anggota 
 ), "suara_yang diperlukan harus <= len(anggota)" 
 self.data.lambdas = sp.cast (
                sp.big_map(), sp.big_map[sp.nat, operasi_lambda] 
 ) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 
 self.data.nextId = 0 
 self.data.inactiveBefore = 0 
 self.data.anggota = sp.cast(anggota, sp.set[sp.alamat])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def submit_lambda(self, lambda_): 
 """Kirim lambda baru ke pemungutan suara.

            Mengajukan proposal tidak berarti memberikan suara mendukung proposal tersebut.

            Args: 
 lambda_(sp.lambda dengan operasi): lambda mengusulkan untuk memilih.
            Menimbulkan: 
 `Anda bukan anggota` 
 """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 self.data.lambdas[self.data.nextId] = lambda_ 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote_lambda(self, id): 
 """Pilih lambda.

            Argumen: 
 id(sp.nat): id lambda yang akan dipilih.
            Menimbulkan: 
 `Anda bukan anggota`, `Lambda tidak aktif`, `Lambda tidak ditemukan` 

 Tidak ada suara yang menentang atau menyetujui. Jika seseorang tidak setuju dengan lambda 
 mereka dapat menghindari untuk memilih.
            """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 tegaskan id >= self.data.inactiveBefore, "Lambda tidak aktif" 
 tegaskan self.data.lambdas.contains(id), "Lambda tidak ditemukan" 
 self.data.votes[id].add(sp.sender)
            if sp.len(self.data.votes[id]) >= self.data.required_votes:
                mandiri.data.lambdas[id]()
                self.data.inactiveBefore = self.data.nextId 

 @sp.onchain_view() 
 def get_lambda(self, id): 
 """Kembalikan lambda yang sesuai.

            Args: 
 id (sp.nat): id lambda yang akan didapat.

            Return: 
 pasang lambda dan boolean yang menunjukkan apakah lambda aktif.
            """ 
 pengembalian (self.data.lambdas[id], id >= self.data.inactiveBefore)


# jika "templat" tidak ada di __name__: 


 @sp.module 
 def test(): 
 class Administrated(sp.Contract): 
 def __init__(mandiri, admin): 
 self.data.admin = admin 
 diri.data.nilai = sp.int(0)

        @sp.entrypoint 
 def set_value(self, value): 
 menegaskan sp.sender == self.data.admin 
 self.data.value = nilai 


 @sp.add_test(name="Skenario dasar MultisigLambda", is_default=True ) 
 def basic_scenario(): 
 """Gunakan multisigLambda sebagai administrator contoh kontrak.

    Pengujian: 
 - Asal 
 - Pengiriman Lambda 
 - Suara Lambda 
 """ 
 sc = sp.test_scenario([main, tes]) 
 sc.h1("Skenario dasar.")

    anggota1 = sp.test_account("anggota1")
    anggota2 = sp.test_account("anggota2")
    anggota3 = sp.test_account("anggota3")
    anggota = sp.set([alamat anggota1, anggota2.alamat, anggota3.alamat])

    sc.h2("MultisigLambda: origination") 
 c1 = main.MultisigLambda(anggota, 2) 
 sc += c1 

 sc.h2("Administrated: origination") 
 c2 = test.Administrated(c1.address)
    sc += c2 

 sc.h2("MultisigLambda: submit_lambda") 

 def set_42(params): 
 diadministrasikan = sp.contract(sp.TInt, c2.address, entrypoint="set_value") 
 sp.transfer(sp. ke dalam(42), sp.tez(0), diadministrasikan.open_some())

    lambda_ = sp.build_lambda(set_42, with_operations=True) 
 c1.submit_lambda(lambda_).run(sender=member1) 

 sc.h2("MultisigLambda: vote_lambda") 
 c1.vote_lambda(0).run(sender=member1)
    c1.vote_lambda(0).run(pengirim=anggota2)

    # Kami dapat memeriksa apakah kontrak yang diadministrasikan menerima transfer.
    sc.verifikasi(c2.data.nilai == 42)

Kontrak MultisigAction

Ini memperkenalkan konsep pemungutan suara untuk proposal. Dalam kontrak ini, penandatangan dapat memilih tindakan tertentu yang akan diambil, dan jika kuorum tercapai, tindakan yang diusulkan akan dilaksanakan.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 # Spesifikasi jenis tindakan administrasi internal 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.daftar[sp.alamat],
        ubahKuorum=sp.nat,
        hapusSigners=sp.daftar[sp.alamat],
    ) 

 kelas MultisigAction(sp.Contract): 
 """Kontrak yang dapat digunakan oleh banyak penandatangan untuk mengatur 
 kontrak lainnya. Kontrak yang diadministrasikan menerapkan antarmuka yang memungkinkan proses administrasi 
 kepada pengguna non-ahli.

        Penandatangan memilih proposal. Proposal adalah daftar target dengan daftar 
 tindakan. Suatu tindakan adalah byte sederhana tetapi dimaksudkan sebagai nilai paket 
 varian. Pola sederhana ini memungkinkan untuk membangun antarmuka UX 
 yang menampilkan konten proposal atau pembuatannya.
        """ 

 def __init__(mandiri, kuorum, penandatangan): 
 self.data.inactiveBefore = 0 
 self.data.nextId = 0 
 self.data.proposal = sp.cast(
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.list[sp.record(target=sp.address, tindakan=sp.daftar[sp.bytes])],
                ], 
 ) 
 self.data.quorum = sp.cast(kuorum, sp.nat) 
 self.data.signers = sp.cast(signer, sp.set[sp.alamat])
            self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 

 @sp.entrypoint 
 def send_proposal(self, proposal): 
 """Hanya untuk penandatangan. Kirimkan proposal untuk pemungutan suara.

            Args: 
 proposal (sp.daftar sp.catatan alamat sasaran dan tindakan): Daftar\ 
 sasaran dan tindakan administrasi terkait.
            """ 
 menegaskan self.data.signers.contains(sp.sender), "Hanya penandatangan yang dapat mengusulkan" 
 self.data.proposals[self.data.nextId] = proposal 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Pilih satu atau lebih proposal 

 Args: 
 pId (sp.nat): Id proposal.
            """ 
 menegaskan self.data.signers.contains(sp.sender), "Hanya penandatangan yang dapat memilih" 
 tegaskan self.data.votes.contains(pId), "Proposal tidak diketahui" 
 tegaskan pId >= self.data.inactiveBefore, "Proposal tidak aktif" 
 self.data.votes[pId].add(sp.sender)

            if sp.len(self.data.votes.get(pId, default=sp.set())) >= diri.data.kuorum:
                self._onApproved(pId)

        @sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """Fungsi sebaris. Logika diterapkan ketika proposal telah disetujui."""
            proposal = self.data.proposal.get(pId, default=[]) 
 untuk p_item dalam proposal: 
 kontrak = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer( 
 p_item.tindakan,
                    sp.tez(0), 
 kontrak.unwrap_some(error="Target Tidak Valid"),
                ) 
 # Nonaktifkan semua proposal yang sudah diajukan.
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def administrate(self, action): 
 """Panggilan mandiri saja. Kelola kontrak ini.

            Titik masuk ini harus dipanggil melalui sistem proposal.

            Args: 
 tindakan (sp.list of sp.bytes): Daftar varian yang dikemas dari \ 
 `InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`).
            """ 
 menegaskan ( 
 sp.sender == sp.self_address() 
 ), "Titik masuk ini harus dipanggil melalui sistem proposal." 

 untukpacked_actions dalam tindakan: 
 action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="Format tindakan buruk" 
 ) 
 dengan sp.match(action): 
 dengan sp.case.changeQuorum sebagai kuorum: 
 self.data.quorum = kuorum 
 dengan sp.case.addSigners ditambahkan: 
 untuk penandatangan ditambahkan: 
 self.data.signers.add(signer)
                    dengan sp.case.removeSigners seperti yang dihapus: 
 untuk alamat yang dihapus: 
 self.data.signers.remove(address)
                # Pastikan bahwa kontrak tidak memerlukan kuorum lebih dari total penandatangan.
                menegaskan self.data.quorum <= sp.len( 
 self.data.signers 
 ), "Kuorum lebih banyak daripada penandatangan."


jika "templat" tidak ada di __name__: 

 @sp.add_test(name="Skenario dasar", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        penandatangan2 = sp.test_account("penandatangan2")
        penandatangan3 = sp.test_account("penandatangan3")

        s = sp.test_scenario(utama)
        s.h1("Skenario dasar") 

 s.h2("Asal") 
 c1 = main.MultisigAction( 
 kuorum=2, 
 penandatangan=sp.set([signer1.address, penandatangan2.alamat]), 
 ) 
 s += c1 

 s.h2("Usulan penambahan penandatangan baru") 
 target = sp.to_address(
            sp.kontrak(sp.TList(sp.TBytes), c1.alamat, "administrasi").open_some() 
 ) 
 tindakan = sp.pack(
            sp.set_type_expr( 
 sp.variant("addSigners", [penandatangan3.alamat]), main.InternalAdminAction 
 ) 
 ) 
 c1.send_proposal([sp.record(target=target, tindakan=[tindakan])]).run( 
 pengirim=penandatangan1 
 ) 

 s.h2("Penandatangan 1 suara untuk proposal") 
 c1.vote(0).run(pengirim=penandatangan1)
        s.h2("Penandatangan 2 suara untuk proposal") 
 c1.vote(0).run(sender=signer2)

        s.verify(c1.data.signers.contains(signer3.address))

Kontrak MultisigView

Ini juga menggunakan mekanisme pemungutan suara. Kontrak ini memungkinkan anggota untuk mengirimkan dan memilih byte yang sewenang-wenang. Setelah proposal mencapai jumlah suara yang disyaratkan, statusnya dapat dikonfirmasi melalui tampilan.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 class MultisigView(sp.Contract): 
 """Beberapa anggota memilih byte yang berubah-ubah.

        Kontrak ini dapat dibuat dengan daftar alamat dan jumlah 
 suara yang diperlukan. Setiap anggota dapat mengirimkan byte sebanyak yang mereka inginkan dan memilih 
 untuk proposal aktif.

        Setiap byte yang mencapai suara yang diperlukan dapat dikonfirmasi melalui tampilan.
        """ 

 def __init__(mandiri, anggota, diperlukan_votes): 
 """Konstruktor 

 Args: 
 anggota (sp.set dari sp.address): orang yang dapat mengirimkan dan memilih 
 lambda.
                diperlukan_suara (sp.nat): jumlah suara yang diperlukan 
 """ 
 menegaskan suara_yang diperlukan <= sp.len( 
 anggota 
 ), "suara_yang diperlukan harus <= len(anggota)" 
 self.data.proposals = sp.cast (sp.peta_besar(), sp.big_map[sp.byte, sp.bool]) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.bytes, sp.set[sp.address]] 
 ) 
 self.data.members = sp.cast(members, sp.set[sp.alamat])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def submit_proposal(self, bytes): 
 """Kirimkan proposal baru ke pemungutan suara.

            Mengajukan proposal tidak berarti memberikan suara mendukung proposal tersebut.

            Argumen: 
 byte(sp.byte): byte diusulkan untuk memilih.
            Menimbulkan : 
 `Anda bukan anggota` 
 """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 self.data.proposals[bytes] = Salah 
 self.data.votes[byte] = sp.set()

        @sp.entrypoint 
 def vote_proposal(self, bytes): 
 """Pilih proposal.

            Tidak ada suara menolak atau menyetujui. Jika seseorang tidak setuju dengan suatu usulan, 
 dapat menghindari untuk memilih. Peringatan: proposal lama yang tidak dipilih tidak akan pernah menjadi 
 .

            Argumen: 
 id(sp.byte): byte proposal.
            Menaikkan: 
 `Anda bukan anggota`, `Proposal tidak ditemukan` 
 """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 tegaskan self.data.proposals.contains(bytes), "Proposal tidak ditemukan" 
 self.data.votes[bytes].add(sp.sender)
            if sp.len(self.data.votes[byte]) >= self.data.required_votes:
                self.data.proposal[byte] = Benar 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """Mengembalikan boolean yang menunjukkan apakah proposal telah dipilih.

            Args: 
 id (sp.bytes): byte proposal 
 Return: 
 (sp.bool): Benar jika proposal telah dipilih, Salah jika sebaliknya.
            """ 
 mengembalikan self.data.proposals.get(id, error="Proposal tidak ditemukan") 


 jika "templat" tidak ada di __name__: 

 @sp.add_test(name="Skenario dasar MultisigView", is_default=True) 
 def basic_scenario(): 
 """Skenario dengan pemungutan suara pada kontrak

        Pengujian: 
 - Asal usul 
 - Pengiriman proposal 
 - Pemungutan suara proposal 
 """ 
 sc = sp.test_scenario(main)
        sc.h1("Skenario dasar.")

        anggota1 = sp.test_account("anggota1")
        anggota2 = sp.test_account("anggota2")
        anggota3 = sp.test_account("anggota3")
        anggota = sp.set([alamat anggota1, anggota2.alamat, anggota3.alamat])

        sc.h2("Asal") 
 c1 = main.MultisigView(anggota, 2) 
 sc += c1 

 sc.h2("submit_proposal") 
 c1.submit_proposal(sp.bytes("0x42")).run( pengirim=anggota1) 

 sc.h2("vote_proposal") 
 c1.vote_proposal(sp.bytes("0x42")).run(sender=member1) 
 c1.vote_proposal(sp.bytes("0x42")).run (pengirim=anggota2) 

 # Kami dapat memeriksa apakah proposal telah divalidasi.
        sc.verifikasi(c1.is_voted(sp.bytes("0x42")))

Setiap kontrak menyediakan mekanisme berbeda untuk mencapai kontrol multi-tanda tangan, menawarkan fleksibilitas tergantung pada kebutuhan spesifik kasus penggunaan blockchain Anda.

Panduan Langkah demi Langkah untuk Mencoba Kontrak Multisig di SmartPy Online

Untuk mencoba kontrak multisig yang kami tulis di SmartPy, Anda dapat mengikuti langkah-langkah berikut:

  1. Buka IDE SmartPy di https://smartpy.io/ide.

  2. Tempelkan kode kontrak ke editor. Anda dapat mengganti kode yang ada.

  3. Untuk melaksanakan kontrak, klik tombol “Jalankan” yang terletak di panel atas.

  4. Setelah menjalankan kontrak, Anda dapat melihat eksekusi skenario di panel “Output” di sebelah kanan. Di sini, Anda dapat melihat detail setiap tindakan, termasuk proposal, pemungutan suara, dan persetujuan.

  5. Untuk menerapkan kontrak Anda di jaringan Tezos, Anda harus mengompilasinya terlebih dahulu. Klik tombol "Kompilasi" di panel atas.

  6. Setelah kompilasi, Anda dapat menerapkan kontrak ke testnet dengan mengklik “Terapkan Kontrak Michelson”. Anda harus memberikan Kunci Rahasia untuk akun Tezos dengan dana yang cukup untuk membayar biaya penerapan bahan bakar.

  7. Setelah kontrak diterapkan, Anda akan diberikan alamat kontrak di blockchain. Anda dapat menggunakan alamat ini untuk berinteraksi dengan kontrak melalui transaksi.

  8. Untuk mengirimkan proposal atau memberikan suara dalam kontrak, Anda dapat menggunakan titik masuk yang ditentukan dalam kode kontrak, seperti submit_proposal atau vote_proposal. Ini bisa dipanggil langsung dari transaksi yang Anda buat.

Ingat, meskipun SmartPy IDE memungkinkan Anda menguji kontrak Anda pada simulasi blockchain, menerapkan kontrak ke jaringan Tezos yang sebenarnya akan menimbulkan biaya bahan bakar, yang harus dibayar dalam XTZ, mata uang kripto asli dari jaringan Tezos.

Isenção de responsabilidade
* O investimento em criptomoedas envolve grandes riscos. Prossiga com cautela. O curso não se destina a servir de orientação para investimentos.
* O curso foi criado pelo autor que entrou para o Gate Learn. As opiniões compartilhadas pelo autor não representam o Gate Learn.
Catálogo
Lição 1

Pendahuluan dan Kontrak Multisig

Kontrak multisignature (multisig), juga dikenal sebagai kontrak "M-of-N", adalah mekanisme penting yang digunakan untuk meningkatkan keamanan dan fleksibilitas transaksi di lingkungan blockchain. Kontrak ini mengubah cara pengendalian dilakukan atas aset dengan memerlukan persetujuan dari banyak pihak sebelum transaksi dapat dilaksanakan. Istilah "M-of-N" mengacu pada persyaratan bahwa M dari N total pihak harus menyetujui transaksi agar sah.

Teori Kontrak Multisig

Kontrak multisig menyediakan sarana untuk menciptakan kendali bersama atas aset. Kasus penggunaan yang umum melibatkan layanan escrow, manajemen akun perusahaan, penandatanganan perjanjian keuangan bersama, dan banyak lagi. Kontrak-kontrak ini sangat bermanfaat bagi organisasi atau kelompok yang memerlukan pengambilan keputusan kolektif.

Secara desain, kontrak multisig tahan terhadap kerusakan dan mencegah satu titik kegagalan. Bahkan jika kunci salah satu pihak disusupi, penyerang tidak dapat melakukan transaksi tanpa persetujuan pihak lain. Ini menambah lapisan keamanan ekstra.

Kontrak multisig dapat dianggap setara secara digital dengan brankas yang memerlukan banyak kunci untuk dibuka. Jumlah total kunci (N) dan jumlah minimum kunci yang diperlukan untuk membuka kotak (M) disepakati saat kontrak dibuat.

Kontrak multisig dapat memiliki banyak konfigurasi berbeda bergantung pada nilai M dan N:

  • 1-of-N: Satu pihak dari total dapat menyetujui transaksi tersebut. Konfigurasi ini sama seperti transaksi biasa tanpa multisig. Ini mungkin digunakan jika ada beberapa kunci untuk kenyamanan, namun salah satu dari kunci tersebut dapat menyetujui transaksi.
  • N-of-N: Semua pihak harus menyetujui transaksi tersebut. Konfigurasi ini memberikan tingkat keamanan tertinggi namun dapat menjadi masalah jika salah satu pihak kehilangan kuncinya atau menolak menyetujui transaksi.
  • M-of-N (di mana M < N): Sebagian dari total pihak harus menyetujui transaksi tersebut. Konfigurasi ini sering digunakan dalam praktik karena menyeimbangkan keamanan dengan fleksibilitas.

Kontrak Multisig di Blockchain

Dalam konteks blockchain, kontrak multisig digunakan secara luas untuk meningkatkan keamanan transaksi, mendukung mekanisme tata kelola yang kompleks, atau mempertahankan kontrol yang fleksibel atas aset blockchain. Berikut beberapa contohnya:

  • Dompet: Dompet multisig digunakan untuk mengamankan aset. Mereka mengharuskan banyak pihak untuk menandatangani transaksi, sehingga memberikan keamanan tambahan terhadap pencurian, peretasan eksternal, dan ancaman orang dalam.
  • Organisasi Otonomi Terdesentralisasi (DAO): DAO sering kali menggunakan kontrak multisig untuk menegakkan aturan tata kelola mereka. Pemungutan suara pada proposal diimplementasikan sebagai transaksi multisig, dengan anggota DAO bertindak sebagai penandatangan. Proposal tersebut dilaksanakan hanya jika mendapat suara yang cukup.
  • Operasi lintas rantai: Dalam operasi lintas rantai, kontrak multisig dapat bertindak sebagai penjaga aset. Ketika aset dipindahkan dari satu blockchain ke blockchain lainnya, kontrak multisig pada rantai asal dapat memastikan bahwa aset tersebut terkunci dengan aman hingga operasi pada rantai lainnya dikonfirmasi.
    Meskipun penerapan kontrak multisig dapat bervariasi dari satu blockchain ke blockchain lainnya, konsep intinya tetap sama – perlunya banyak pihak untuk menyetujui transaksi sebelum dieksekusi. Lapisan keamanan tambahan ini menjadikan kontrak multisig sebagai alat penting dalam ruang blockchain dan kripto.

Contoh Pengkodean: Menulis dan Menerapkan Kontrak Multisig dengan SmartPy

Untuk contoh kode, kita akan melihat tiga implementasi kontrak multisignature yang berbeda:

Kontrak Lambda

Ini cukup serbaguna dan memungkinkan berbagai kegunaan. Dibutuhkan banyak tanda tangan untuk menjalankan fungsi lambda sewenang-wenang.

Python 
 impor smartpy sebagai sp 


 @sp.module 
 def main(): 
 operasi_lambda: type = sp.lambda_(sp.unit, sp.unit, with_operations=True) 

 class MultisigLambda(sp.Contract): 
 """Beberapa anggota memilih untuk mengeksekusi lambda.

        Kontrak ini dapat dibuat dengan daftar alamat dan jumlah 
 suara yang diperlukan. Setiap anggota dapat mengirimkan lambda sebanyak yang dia inginkan dan memilih 
 untuk proposal aktif. Ketika lambda mencapai suara yang diperlukan, kodenya 
 dipanggil dan operasi keluaran dijalankan. Hal ini memungkinkan kontrak ini untuk 
 melakukan apa pun yang dapat dilakukan kontrak: mentransfer token, mengelola aset, 
 mengelola kontrak lain...

        Ketika lambda diterapkan, semua lambda yang dikirimkan hingga saat ini akan dinonaktifkan.
        Anggota masih dapat mengirimkan lambda baru.
        """ 

 def __init__(mandiri, anggota, diperlukan_votes): 
 """Konstruktor 

 Args: 
 anggota (sp.set dari sp.address): orang yang dapat mengirimkan dan memilih 
 untuk lambda.
                diperlukan_suara (sp.nat): jumlah suara yang diperlukan 
 """ 
 menegaskan suara_yang diperlukan <= sp.len( 
 anggota 
 ), "suara_yang diperlukan harus <= len(anggota)" 
 self.data.lambdas = sp.cast (
                sp.big_map(), sp.big_map[sp.nat, operasi_lambda] 
 ) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 
 self.data.nextId = 0 
 self.data.inactiveBefore = 0 
 self.data.anggota = sp.cast(anggota, sp.set[sp.alamat])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def submit_lambda(self, lambda_): 
 """Kirim lambda baru ke pemungutan suara.

            Mengajukan proposal tidak berarti memberikan suara mendukung proposal tersebut.

            Args: 
 lambda_(sp.lambda dengan operasi): lambda mengusulkan untuk memilih.
            Menimbulkan: 
 `Anda bukan anggota` 
 """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 self.data.lambdas[self.data.nextId] = lambda_ 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote_lambda(self, id): 
 """Pilih lambda.

            Argumen: 
 id(sp.nat): id lambda yang akan dipilih.
            Menimbulkan: 
 `Anda bukan anggota`, `Lambda tidak aktif`, `Lambda tidak ditemukan` 

 Tidak ada suara yang menentang atau menyetujui. Jika seseorang tidak setuju dengan lambda 
 mereka dapat menghindari untuk memilih.
            """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 tegaskan id >= self.data.inactiveBefore, "Lambda tidak aktif" 
 tegaskan self.data.lambdas.contains(id), "Lambda tidak ditemukan" 
 self.data.votes[id].add(sp.sender)
            if sp.len(self.data.votes[id]) >= self.data.required_votes:
                mandiri.data.lambdas[id]()
                self.data.inactiveBefore = self.data.nextId 

 @sp.onchain_view() 
 def get_lambda(self, id): 
 """Kembalikan lambda yang sesuai.

            Args: 
 id (sp.nat): id lambda yang akan didapat.

            Return: 
 pasang lambda dan boolean yang menunjukkan apakah lambda aktif.
            """ 
 pengembalian (self.data.lambdas[id], id >= self.data.inactiveBefore)


# jika "templat" tidak ada di __name__: 


 @sp.module 
 def test(): 
 class Administrated(sp.Contract): 
 def __init__(mandiri, admin): 
 self.data.admin = admin 
 diri.data.nilai = sp.int(0)

        @sp.entrypoint 
 def set_value(self, value): 
 menegaskan sp.sender == self.data.admin 
 self.data.value = nilai 


 @sp.add_test(name="Skenario dasar MultisigLambda", is_default=True ) 
 def basic_scenario(): 
 """Gunakan multisigLambda sebagai administrator contoh kontrak.

    Pengujian: 
 - Asal 
 - Pengiriman Lambda 
 - Suara Lambda 
 """ 
 sc = sp.test_scenario([main, tes]) 
 sc.h1("Skenario dasar.")

    anggota1 = sp.test_account("anggota1")
    anggota2 = sp.test_account("anggota2")
    anggota3 = sp.test_account("anggota3")
    anggota = sp.set([alamat anggota1, anggota2.alamat, anggota3.alamat])

    sc.h2("MultisigLambda: origination") 
 c1 = main.MultisigLambda(anggota, 2) 
 sc += c1 

 sc.h2("Administrated: origination") 
 c2 = test.Administrated(c1.address)
    sc += c2 

 sc.h2("MultisigLambda: submit_lambda") 

 def set_42(params): 
 diadministrasikan = sp.contract(sp.TInt, c2.address, entrypoint="set_value") 
 sp.transfer(sp. ke dalam(42), sp.tez(0), diadministrasikan.open_some())

    lambda_ = sp.build_lambda(set_42, with_operations=True) 
 c1.submit_lambda(lambda_).run(sender=member1) 

 sc.h2("MultisigLambda: vote_lambda") 
 c1.vote_lambda(0).run(sender=member1)
    c1.vote_lambda(0).run(pengirim=anggota2)

    # Kami dapat memeriksa apakah kontrak yang diadministrasikan menerima transfer.
    sc.verifikasi(c2.data.nilai == 42)

Kontrak MultisigAction

Ini memperkenalkan konsep pemungutan suara untuk proposal. Dalam kontrak ini, penandatangan dapat memilih tindakan tertentu yang akan diambil, dan jika kuorum tercapai, tindakan yang diusulkan akan dilaksanakan.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 # Spesifikasi jenis tindakan administrasi internal 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.daftar[sp.alamat],
        ubahKuorum=sp.nat,
        hapusSigners=sp.daftar[sp.alamat],
    ) 

 kelas MultisigAction(sp.Contract): 
 """Kontrak yang dapat digunakan oleh banyak penandatangan untuk mengatur 
 kontrak lainnya. Kontrak yang diadministrasikan menerapkan antarmuka yang memungkinkan proses administrasi 
 kepada pengguna non-ahli.

        Penandatangan memilih proposal. Proposal adalah daftar target dengan daftar 
 tindakan. Suatu tindakan adalah byte sederhana tetapi dimaksudkan sebagai nilai paket 
 varian. Pola sederhana ini memungkinkan untuk membangun antarmuka UX 
 yang menampilkan konten proposal atau pembuatannya.
        """ 

 def __init__(mandiri, kuorum, penandatangan): 
 self.data.inactiveBefore = 0 
 self.data.nextId = 0 
 self.data.proposal = sp.cast(
                sp.big_map(), 
 sp.big_map[ 
 sp.nat, 
 sp.list[sp.record(target=sp.address, tindakan=sp.daftar[sp.bytes])],
                ], 
 ) 
 self.data.quorum = sp.cast(kuorum, sp.nat) 
 self.data.signers = sp.cast(signer, sp.set[sp.alamat])
            self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 
 ) 

 @sp.entrypoint 
 def send_proposal(self, proposal): 
 """Hanya untuk penandatangan. Kirimkan proposal untuk pemungutan suara.

            Args: 
 proposal (sp.daftar sp.catatan alamat sasaran dan tindakan): Daftar\ 
 sasaran dan tindakan administrasi terkait.
            """ 
 menegaskan self.data.signers.contains(sp.sender), "Hanya penandatangan yang dapat mengusulkan" 
 self.data.proposals[self.data.nextId] = proposal 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Pilih satu atau lebih proposal 

 Args: 
 pId (sp.nat): Id proposal.
            """ 
 menegaskan self.data.signers.contains(sp.sender), "Hanya penandatangan yang dapat memilih" 
 tegaskan self.data.votes.contains(pId), "Proposal tidak diketahui" 
 tegaskan pId >= self.data.inactiveBefore, "Proposal tidak aktif" 
 self.data.votes[pId].add(sp.sender)

            if sp.len(self.data.votes.get(pId, default=sp.set())) >= diri.data.kuorum:
                self._onApproved(pId)

        @sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """Fungsi sebaris. Logika diterapkan ketika proposal telah disetujui."""
            proposal = self.data.proposal.get(pId, default=[]) 
 untuk p_item dalam proposal: 
 kontrak = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer( 
 p_item.tindakan,
                    sp.tez(0), 
 kontrak.unwrap_some(error="Target Tidak Valid"),
                ) 
 # Nonaktifkan semua proposal yang sudah diajukan.
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def administrate(self, action): 
 """Panggilan mandiri saja. Kelola kontrak ini.

            Titik masuk ini harus dipanggil melalui sistem proposal.

            Args: 
 tindakan (sp.list of sp.bytes): Daftar varian yang dikemas dari \ 
 `InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`).
            """ 
 menegaskan ( 
 sp.sender == sp.self_address() 
 ), "Titik masuk ini harus dipanggil melalui sistem proposal." 

 untukpacked_actions dalam tindakan: 
 action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="Format tindakan buruk" 
 ) 
 dengan sp.match(action): 
 dengan sp.case.changeQuorum sebagai kuorum: 
 self.data.quorum = kuorum 
 dengan sp.case.addSigners ditambahkan: 
 untuk penandatangan ditambahkan: 
 self.data.signers.add(signer)
                    dengan sp.case.removeSigners seperti yang dihapus: 
 untuk alamat yang dihapus: 
 self.data.signers.remove(address)
                # Pastikan bahwa kontrak tidak memerlukan kuorum lebih dari total penandatangan.
                menegaskan self.data.quorum <= sp.len( 
 self.data.signers 
 ), "Kuorum lebih banyak daripada penandatangan."


jika "templat" tidak ada di __name__: 

 @sp.add_test(name="Skenario dasar", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        penandatangan2 = sp.test_account("penandatangan2")
        penandatangan3 = sp.test_account("penandatangan3")

        s = sp.test_scenario(utama)
        s.h1("Skenario dasar") 

 s.h2("Asal") 
 c1 = main.MultisigAction( 
 kuorum=2, 
 penandatangan=sp.set([signer1.address, penandatangan2.alamat]), 
 ) 
 s += c1 

 s.h2("Usulan penambahan penandatangan baru") 
 target = sp.to_address(
            sp.kontrak(sp.TList(sp.TBytes), c1.alamat, "administrasi").open_some() 
 ) 
 tindakan = sp.pack(
            sp.set_type_expr( 
 sp.variant("addSigners", [penandatangan3.alamat]), main.InternalAdminAction 
 ) 
 ) 
 c1.send_proposal([sp.record(target=target, tindakan=[tindakan])]).run( 
 pengirim=penandatangan1 
 ) 

 s.h2("Penandatangan 1 suara untuk proposal") 
 c1.vote(0).run(pengirim=penandatangan1)
        s.h2("Penandatangan 2 suara untuk proposal") 
 c1.vote(0).run(sender=signer2)

        s.verify(c1.data.signers.contains(signer3.address))

Kontrak MultisigView

Ini juga menggunakan mekanisme pemungutan suara. Kontrak ini memungkinkan anggota untuk mengirimkan dan memilih byte yang sewenang-wenang. Setelah proposal mencapai jumlah suara yang disyaratkan, statusnya dapat dikonfirmasi melalui tampilan.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 class MultisigView(sp.Contract): 
 """Beberapa anggota memilih byte yang berubah-ubah.

        Kontrak ini dapat dibuat dengan daftar alamat dan jumlah 
 suara yang diperlukan. Setiap anggota dapat mengirimkan byte sebanyak yang mereka inginkan dan memilih 
 untuk proposal aktif.

        Setiap byte yang mencapai suara yang diperlukan dapat dikonfirmasi melalui tampilan.
        """ 

 def __init__(mandiri, anggota, diperlukan_votes): 
 """Konstruktor 

 Args: 
 anggota (sp.set dari sp.address): orang yang dapat mengirimkan dan memilih 
 lambda.
                diperlukan_suara (sp.nat): jumlah suara yang diperlukan 
 """ 
 menegaskan suara_yang diperlukan <= sp.len( 
 anggota 
 ), "suara_yang diperlukan harus <= len(anggota)" 
 self.data.proposals = sp.cast (sp.peta_besar(), sp.big_map[sp.byte, sp.bool]) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.bytes, sp.set[sp.address]] 
 ) 
 self.data.members = sp.cast(members, sp.set[sp.alamat])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def submit_proposal(self, bytes): 
 """Kirimkan proposal baru ke pemungutan suara.

            Mengajukan proposal tidak berarti memberikan suara mendukung proposal tersebut.

            Argumen: 
 byte(sp.byte): byte diusulkan untuk memilih.
            Menimbulkan : 
 `Anda bukan anggota` 
 """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 self.data.proposals[bytes] = Salah 
 self.data.votes[byte] = sp.set()

        @sp.entrypoint 
 def vote_proposal(self, bytes): 
 """Pilih proposal.

            Tidak ada suara menolak atau menyetujui. Jika seseorang tidak setuju dengan suatu usulan, 
 dapat menghindari untuk memilih. Peringatan: proposal lama yang tidak dipilih tidak akan pernah menjadi 
 .

            Argumen: 
 id(sp.byte): byte proposal.
            Menaikkan: 
 `Anda bukan anggota`, `Proposal tidak ditemukan` 
 """ 
 menegaskan diri.data.anggota.berisi(sp.pengirim), "Anda bukan anggota" 
 tegaskan self.data.proposals.contains(bytes), "Proposal tidak ditemukan" 
 self.data.votes[bytes].add(sp.sender)
            if sp.len(self.data.votes[byte]) >= self.data.required_votes:
                self.data.proposal[byte] = Benar 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """Mengembalikan boolean yang menunjukkan apakah proposal telah dipilih.

            Args: 
 id (sp.bytes): byte proposal 
 Return: 
 (sp.bool): Benar jika proposal telah dipilih, Salah jika sebaliknya.
            """ 
 mengembalikan self.data.proposals.get(id, error="Proposal tidak ditemukan") 


 jika "templat" tidak ada di __name__: 

 @sp.add_test(name="Skenario dasar MultisigView", is_default=True) 
 def basic_scenario(): 
 """Skenario dengan pemungutan suara pada kontrak

        Pengujian: 
 - Asal usul 
 - Pengiriman proposal 
 - Pemungutan suara proposal 
 """ 
 sc = sp.test_scenario(main)
        sc.h1("Skenario dasar.")

        anggota1 = sp.test_account("anggota1")
        anggota2 = sp.test_account("anggota2")
        anggota3 = sp.test_account("anggota3")
        anggota = sp.set([alamat anggota1, anggota2.alamat, anggota3.alamat])

        sc.h2("Asal") 
 c1 = main.MultisigView(anggota, 2) 
 sc += c1 

 sc.h2("submit_proposal") 
 c1.submit_proposal(sp.bytes("0x42")).run( pengirim=anggota1) 

 sc.h2("vote_proposal") 
 c1.vote_proposal(sp.bytes("0x42")).run(sender=member1) 
 c1.vote_proposal(sp.bytes("0x42")).run (pengirim=anggota2) 

 # Kami dapat memeriksa apakah proposal telah divalidasi.
        sc.verifikasi(c1.is_voted(sp.bytes("0x42")))

Setiap kontrak menyediakan mekanisme berbeda untuk mencapai kontrol multi-tanda tangan, menawarkan fleksibilitas tergantung pada kebutuhan spesifik kasus penggunaan blockchain Anda.

Panduan Langkah demi Langkah untuk Mencoba Kontrak Multisig di SmartPy Online

Untuk mencoba kontrak multisig yang kami tulis di SmartPy, Anda dapat mengikuti langkah-langkah berikut:

  1. Buka IDE SmartPy di https://smartpy.io/ide.

  2. Tempelkan kode kontrak ke editor. Anda dapat mengganti kode yang ada.

  3. Untuk melaksanakan kontrak, klik tombol “Jalankan” yang terletak di panel atas.

  4. Setelah menjalankan kontrak, Anda dapat melihat eksekusi skenario di panel “Output” di sebelah kanan. Di sini, Anda dapat melihat detail setiap tindakan, termasuk proposal, pemungutan suara, dan persetujuan.

  5. Untuk menerapkan kontrak Anda di jaringan Tezos, Anda harus mengompilasinya terlebih dahulu. Klik tombol "Kompilasi" di panel atas.

  6. Setelah kompilasi, Anda dapat menerapkan kontrak ke testnet dengan mengklik “Terapkan Kontrak Michelson”. Anda harus memberikan Kunci Rahasia untuk akun Tezos dengan dana yang cukup untuk membayar biaya penerapan bahan bakar.

  7. Setelah kontrak diterapkan, Anda akan diberikan alamat kontrak di blockchain. Anda dapat menggunakan alamat ini untuk berinteraksi dengan kontrak melalui transaksi.

  8. Untuk mengirimkan proposal atau memberikan suara dalam kontrak, Anda dapat menggunakan titik masuk yang ditentukan dalam kode kontrak, seperti submit_proposal atau vote_proposal. Ini bisa dipanggil langsung dari transaksi yang Anda buat.

Ingat, meskipun SmartPy IDE memungkinkan Anda menguji kontrak Anda pada simulasi blockchain, menerapkan kontrak ke jaringan Tezos yang sebenarnya akan menimbulkan biaya bahan bakar, yang harus dibayar dalam XTZ, mata uang kripto asli dari jaringan Tezos.

Isenção de responsabilidade
* O investimento em criptomoedas envolve grandes riscos. Prossiga com cautela. O curso não se destina a servir de orientação para investimentos.
* O curso foi criado pelo autor que entrou para o Gate Learn. As opiniões compartilhadas pelo autor não representam o Gate Learn.