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:
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:
Untuk contoh kode, kita akan melihat tiga implementasi kontrak multisignature yang berbeda:
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)
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))
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.
Untuk mencoba kontrak multisig yang kami tulis di SmartPy, Anda dapat mengikuti langkah-langkah berikut:
Buka IDE SmartPy di https://smartpy.io/ide.
Tempelkan kode kontrak ke editor. Anda dapat mengganti kode yang ada.
Untuk melaksanakan kontrak, klik tombol “Jalankan” yang terletak di panel atas.
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.
Untuk menerapkan kontrak Anda di jaringan Tezos, Anda harus mengompilasinya terlebih dahulu. Klik tombol "Kompilasi" di panel atas.
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.
Setelah kontrak diterapkan, Anda akan diberikan alamat kontrak di blockchain. Anda dapat menggunakan alamat ini untuk berinteraksi dengan kontrak melalui transaksi.
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.
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:
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:
Untuk contoh kode, kita akan melihat tiga implementasi kontrak multisignature yang berbeda:
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)
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))
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.
Untuk mencoba kontrak multisig yang kami tulis di SmartPy, Anda dapat mengikuti langkah-langkah berikut:
Buka IDE SmartPy di https://smartpy.io/ide.
Tempelkan kode kontrak ke editor. Anda dapat mengganti kode yang ada.
Untuk melaksanakan kontrak, klik tombol “Jalankan” yang terletak di panel atas.
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.
Untuk menerapkan kontrak Anda di jaringan Tezos, Anda harus mengompilasinya terlebih dahulu. Klik tombol "Kompilasi" di panel atas.
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.
Setelah kontrak diterapkan, Anda akan diberikan alamat kontrak di blockchain. Anda dapat menggunakan alamat ini untuk berinteraksi dengan kontrak melalui transaksi.
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.