Hợp đồng nhiều chữ ký cung cấp một phương tiện để tạo ra quyền kiểm soát chung đối với tài sản. Các trường hợp sử dụng điển hình liên quan đến dịch vụ ký quỹ, quản lý tài khoản công ty, đồng ký kết các thỏa thuận tài chính, v.v. Những hợp đồng này đặc biệt có lợi cho các tổ chức hoặc nhóm nơi việc ra quyết định tập thể là cần thiết.
Theo thiết kế, các hợp đồng đa chữ ký có khả năng chống giả mạo và ngăn chặn các điểm lỗi đơn lẻ. Ngay cả khi khóa của một bên bị xâm phạm, kẻ tấn công không thể thực hiện giao dịch mà không có sự chấp thuận của các bên khác. Điều này bổ sung thêm một lớp bảo mật.
Hợp đồng nhiều chữ ký có thể được coi là một dạng kỹ thuật số tương đương với hộp ký gửi an toàn yêu cầu nhiều chìa khóa để mở. Tổng số chìa khóa (N) và số chìa khóa tối thiểu cần thiết để mở hộp (M) được thỏa thuận khi tạo hợp đồng.
Hợp đồng Multisig có thể có nhiều cấu hình khác nhau tùy thuộc vào giá trị của M và N:
Trong bối cảnh blockchain, hợp đồng nhiều chữ ký được sử dụng rộng rãi để tăng cường bảo mật giao dịch, hỗ trợ các cơ chế quản trị phức tạp hoặc duy trì quyền kiểm soát linh hoạt đối với tài sản blockchain. Dưới đây là một số ví dụ:
Đối với các ví dụ về mã của chúng tôi, chúng tôi sẽ xem xét ba cách triển khai hợp đồng đa chữ ký khác nhau:
Nó khá linh hoạt và cho phép sử dụng nhiều loại. Nó yêu cầu nhiều chữ ký để thực thi các hàm lambda tùy ý.
Python
nhập smartpy dưới dạng sp
@sp.module
def main():
Operation_lambda: type = sp.lambda_(sp.unit, sp.unit, with_Operations=True)
lớp MultisigLambda(sp.Contract):
"""Nhiều thành viên bỏ phiếu để thực thi lambdas.
Hợp đồng này có thể được bắt nguồn từ danh sách địa chỉ và số phiếu bầu bắt buộc là
. Bất kỳ thành viên nào cũng có thể gửi bao nhiêu lambda tùy thích và bỏ phiếu
cho các đề xuất đang hoạt động. Khi lambda đạt được số phiếu yêu cầu, mã của nó được gọi là
và các hoạt động đầu ra được thực thi. Điều này cho phép hợp đồng này
thực hiện bất kỳ điều gì mà hợp đồng có thể làm: chuyển mã thông báo, quản lý tài sản,
quản lý hợp đồng khác...
Khi áp dụng lambda, tất cả các lambda đã gửi cho đến thời điểm hiện tại đều không được kích hoạt.
Các thành viên vẫn có thể gửi lambdas mới.
"""
def __init__(tự, thành viên, require_votes):
"""Constructor
Args:
thành viên (sp.set of sp.address): những người có thể gửi và bỏ phiếu
cho lambda.
bắt buộc_votes (sp.nat): số phiếu bầu cần thiết
"""
khẳng định bắt buộc_votes <= sp.len(
thành viên
), "required_votes phải là <= len(thành viên)"
self.data.lambdas = sp.cast (
sp.big_map(), sp.big_map[sp.nat, Operation_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.members = sp.cast(thành viên, sp.set[sp.address])
self.data.required_votes = sp.cast(required_votes, sp.nat)
@sp.entrypoint
def submit_lambda(self, lambda_):
"""Gửi lambda mới để bỏ phiếu.
Việc gửi một đề xuất không có nghĩa là bỏ phiếu ủng hộ đề xuất đó.
Args:
lambda_(sp.lambda có phép toán): lambda đề xuất bỏ phiếu.
Tăng:
`Bạn không phải là thành viên`
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
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):
"""Bỏ phiếu cho lambda.
Đối số:
id(sp.nat): id của lambda để bỏ phiếu.
Tăng:
`Bạn không phải là thành viên`, `Lambda không hoạt động`, `Không tìm thấy Lambda`
Không có phiếu chống hoặc thông qua. Nếu ai đó không đồng ý với lambda
họ có thể tránh bỏ phiếu.
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
khẳng định id >= self.data.inactiveBefore, "Lambda không hoạt động"
khẳng định self.data.lambdas.contains(id), "Không tìm thấy Lambda"
self.data.votes[id].add(sp.sender)
if sp.len(self.data.votes[id]) >= self.data.required_votes:
self.data.lambdas[id]()
self.data.inactiveTrước = self.data.nextId
@sp.onchain_view()
def get_lambda(self, id):
"""Trả về lambda tương ứng.
Args:
id (sp.nat): id của lambda cần lấy.
Trả về:
cặp lambda và boolean hiển thị xem lambda có hoạt động hay không.
"""
trả về (self.data.lambdas[id], id >= self.data.inactiveBefore)
# if "templates" not in __name__:
@sp.module
def test():
class Administrated(sp.Contract):
def __init__(self, admin):
self.data.admin = admin
self.data.value = sp.int(0)
@sp.entrypoint
def set_value(self, value):
khẳng định sp.sender == self.data.admin
self.data.value = value
@sp.add_test(name="Kịch bản cơ bản MultisigLambda", is_default=True )
def basic_scenario():
"""Sử dụng multisigLambda làm quản trị viên của một hợp đồng mẫu.
Kiểm tra:
- Nguồn gốc
- Đệ trình Lambda
- Phiếu bầu Lambda
"""
sc = sp.test_scenario([main, test])
sc.h1("Kịch bản cơ bản.")
member1 = sp.test_account("member1")
member2 = sp.test_account("member2")
member3 = sp.test_account("member3")
thành viên = sp.set([member1.address, thành viên2.address, member3.address])
sc.h2("MultisigLambda: nguồn gốc")
c1 = main.MultisigLambda(thành viên, 2)
sc += c1
sc.h2("Được quản lý: nguồn gốc")
c2 = test.Administrated(c1.address)
sc += c2
sc.h2("MultisigLambda: submit_lambda")
def set_42(params):
administrated = sp.contract(sp.TINt, c2.address, entrypoint="set_value")
sp.transfer(sp. int(42), sp.tez(0), administrated.open_some())
lambda_ = sp.build_lambda(set_42, with_Operation=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(sender=member2)
# Chúng tôi có thể kiểm tra xem hợp đồng được quản lý đã nhận được chuyển khoản hay chưa.
sc.verify(c2.data.value == 42)
Nó giới thiệu khái niệm bỏ phiếu cho các đề xuất. Trong hợp đồng này, người ký có thể bỏ phiếu cho một số hành động nhất định sẽ được thực hiện và nếu đạt đến số đại biểu cần thiết, các hành động được đề xuất sẽ được thực hiện.
Python
import smartpy as sp
@sp.module
def main():
# Đặc tả loại hành động quản trị nội bộ
InternalAdminAction: type = sp.variant(
addSigners=sp.list[sp.address],
ChangeQuorum=sp.nat,
RemoveSigners=sp.list[sp.address],
)
lớp MultisigAction(sp.Contract):
"""Một hợp đồng có thể được nhiều người ký sử dụng để quản lý
hợp đồng khác. Các hợp đồng được quản trị triển khai
giao diện giúp có thể trình bày rõ ràng quy trình quản trị cho người dùng không phải là chuyên gia.
Người ký bỏ phiếu cho các đề xuất. Đề xuất là danh sách mục tiêu với danh sách
hành động. Một hành động là một byte đơn giản nhưng nó được dự định là giá trị gói bằng
biến thể. Mẫu đơn giản này giúp bạn có thể xây dựng giao diện UX
hiển thị nội dung của một đề xuất hoặc xây dựng một đề xuất.
"""
def __init__(tự, số đại biểu, người ký):
self.data.inactiveBefore = 0
self.data.nextId = 0
self.data.proposals = sp.cast(
sp.big_map(),
sp.big_map[
sp.nat,
sp.list[sp.record(target=sp.address, actions=sp.list[sp.bytes])],
],
)
self.data.quorum = sp.cast(quorum, sp.nat)
self.data.signers = sp.cast(signers, sp.set[sp.address])
self.data.votes = sp.cast(
sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]]
)
@sp.entrypoint
def send_proposal(self, đề xuất):
"""Chỉ dành cho người ký. Gửi đề xuất để bỏ phiếu.
Args:
đề xuất (sp.list of sp.record of target address and action): Danh sách\
mục tiêu và các hành động quản trị liên quan.
"""
khẳng định self.data.signers.contains(sp.sender), "Chỉ người ký mới có thể đề xuất"
self.data.proposals[self.data.nextId] = đề xuất
self.data.votes[self.data.nextId] = sp.set()
self.data.nextId += 1
@sp.entrypoint
def vote(self, pId):
"""Bỏ phiếu cho một hoặc nhiều đề xuất
Args:
pId (sp.nat): Id của đề xuất.
"""
khẳng định self.data.signers.contains(sp.sender), "Chỉ người ký mới có thể bỏ phiếu"
khẳng định self.data.votes.contains(pId), "Đề xuất không xác định"
khẳng định pId >= self.data.inactiveBefore, "Đề xuất không hoạt động"
self.data.votes[pId].add(sp.sender)
nếu sp.len(self.data.votes.get(pId, mặc định=sp.set())) >= self.data.quorum:
self._onApproved(pId)
@sp.private(with_storage="read-write", with_Operations=True)
def _onApproved(self, pId):
"""Hàm nội tuyến. Logic được áp dụng khi đề xuất đã được phê duyệt."""
đề xuất = self.data.proposals.get(pId, default=[])
cho p_item trong đề xuất:
hợp đồng = sp.contract(sp.list[sp.bytes], p_item.target)
sp.transfer(
p_item.actions,
sp.tez(0),
hợp đồng.unwrap_some(error="InvalidTarget"),
)
# Vô hiệu hóa tất cả các đề xuất đã được gửi.
self.data.inactiveTrước = self.data.nextId
@sp.entrypoint
def administrate(self, actions):
"""Chỉ tự gọi. Quản lý hợp đồng này.
Điểm vào này phải được gọi thông qua hệ thống đề xuất.
Args:
hành động (sp.list of sp.bytes): Danh sách biến thể đóng gói của \
`InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`).
"""
khẳng định (
sp.sender == sp.self_address()
), "Điểm vào này phải được gọi thông qua hệ thống đề xuất."
cho pack_actions trong hành động:
action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some(
error="Định dạng hành động xấu"
)
với sp.match(action):
với sp.case.changeQuorum với số đại biểu:
self.data.quorum = số đại biểu
với sp.case.addSigners được thêm vào:
cho người ký đã thêm:
self.data.signers.add(signer)
với sp.case.removeSigners như đã xóa:
cho địa chỉ đã xóa:
self.data.signers.remove(address)
# Đảm bảo rằng hợp đồng không bao giờ yêu cầu số đại biểu nhiều hơn tổng số người ký.
khẳng định self.data.quorum <= sp.len(
self.data.signers
), "Số đại biểu nhiều hơn người ký."
nếu "mẫu" không có trong __name__:
@sp.add_test(name="Basic script", is_default=True)
def test():
signer1 = sp.test_account("signer1")
người ký2 = sp.test_account("signer2")
người ký3 = sp.test_account("signer3")
s = sp.test_scenario(chính)
s.h1("Kịch bản cơ bản")
s.h2("Nguồn gốc")
c1 = main.MultisigAction(
quorum=2,
signers=sp.set([signer1.address, signer2.address]),
)
s += c1
s.h2("Đề xuất thêm người ký mới")
target = sp.to_address(
sp.contract(sp.TList(sp.TBytes), c1.address, "administrate").open_some()
)
action = sp.pack(
sp.set_type_expr(
sp.variant("addSigners", [signer3.address]), main.InternalAdminAction
)
)
c1.send_proposal([sp.record(target=target, actions=[action])]).run(
sender=signer1
)
s.h2("Người ký 1 phiếu cho đề xuất")
c1.vote(0).run(sender=signer1)
s.h2("Người ký 2 phiếu cho đề xuất")
c1.vote(0).run(sender=signer2)
s.verify(c1.data.signers.contains(signer3.address))
Nó cũng sử dụng một cơ chế bỏ phiếu. Hợp đồng này cho phép các thành viên gửi và bỏ phiếu cho các byte tùy ý. Khi một đề xuất đạt được số phiếu bầu cần thiết, trạng thái của nó có thể được xác nhận thông qua chế độ xem.
Python
import smartpy as sp
@sp.module
def main():
class MultisigView(sp.Contract):
"""Nhiều thành viên bỏ phiếu cho các byte tùy ý.
Hợp đồng này có thể được bắt nguồn từ danh sách địa chỉ và số phiếu bầu bắt buộc là
. Bất kỳ thành viên nào cũng có thể gửi bao nhiêu byte tùy thích và bỏ phiếu
cho các đề xuất đang hoạt động.
Bất kỳ byte nào đạt được số phiếu yêu cầu đều có thể được xác nhận thông qua chế độ xem.
"""
def __init__(tự, thành viên, bắt buộc_vote):
"""Constructor
Args:
thành viên (sp.set of sp.address): những người có thể gửi và bỏ phiếu cho
lambda.
bắt buộc_votes (sp.nat): số phiếu bầu cần thiết
"""
khẳng định bắt buộc_votes <= sp.len(
thành viên
), "required_votes phải là <= len(thành viên)"
self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.byte, sp.bool])
self.data.votes = sp.cast(
sp.big_map(), sp.big_map[sp.byte, sp.set[sp.address]]
)
self.data.members = sp.cast(thành viên, sp.set[sp.address])
self.data.required_votes = sp.cast(required_votes, sp.nat)
@sp.entrypoint
def submit_proposal(self, bytes):
"""Gửi đề xuất mới để bỏ phiếu.
Việc gửi một đề xuất không có nghĩa là bỏ phiếu ủng hộ đề xuất đó.
Đối số:
byte(sp.byte): byte được đề xuất bỏ phiếu.
Tăng:
`Bạn không phải là thành viên`
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
self.data.proposals[bytes] = Sai
self.data.votes[bytes] = sp.set()
@sp.entrypoint
def vote_proposal(self, byte):
"""Bỏ phiếu cho một đề xuất.
Không có phiếu chống hoặc thông qua. Nếu một người không đồng ý với một đề xuất,
có thể tránh bỏ phiếu. Cảnh báo: những đề xuất cũ chưa được bình chọn không bao giờ trở nên
thời.
Đối số:
id(sp.byte): byte của đề xuất.
Tăng:
`Bạn không phải là thành viên`, `Không tìm thấy đề xuất`
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
khẳng định self.data.proposals.contains(byte), "Không tìm thấy đề xuất"
self.data.votes[bytes].add(sp.sender)
if sp.len(self.data.votes[bytes]) >= self.data.required_votes:
self.data.proposals[byte] = Đúng
@sp.onchain_view()
def is_voted(self, id):
"""Trả về một boolean cho biết đề xuất đã được bỏ phiếu hay chưa.
Args:
id (sp.bytes): byte của đề xuất
Return:
(sp.bool): Đúng nếu đề xuất đã được biểu quyết, nếu không thì sai.
"""
trả về self.data.proposals.get(id, error="Không tìm thấy đề xuất")
nếu "mẫu" không có trong __name__:
@sp.add_test(name="MultisigView basic script", is_default=True)
def basic_scenario():
"""Một kịch bản có một cuộc bỏ phiếu cho hợp đồng multisigView.
Kiểm tra:
- Khởi tạo
- Nộp đề xuất
- Phiếu đề xuất
"""
sc = sp.test_scenario(main)
sc.h1("Kịch bản cơ bản.")
member1 = sp.test_account("member1")
member2 = sp.test_account("member2")
member3 = sp.test_account("member3")
thành viên = sp.set([member1.address, thành viên2.address, member3.address])
sc.h2("Gốc")
c1 = main.MultisigView(thành viên, 2)
sc += c1
sc.h2("submit_proposal")
c1.submit_proposal(sp.bytes("0x42")).run( sender=member1)
sc.h2("vote_proposal")
c1.vote_proposal(sp.bytes("0x42")).run(sender=member1)
c1.vote_proposal(sp.bytes("0x42")).run (sender=member2)
# Chúng tôi có thể kiểm tra xem đề xuất đã được xác thực chưa.
sc.verify(c1.is_voted(sp.bytes("0x42")))
Mỗi hợp đồng cung cấp một cơ chế khác nhau để đạt được khả năng kiểm soát đa chữ ký, mang lại sự linh hoạt tùy thuộc vào nhu cầu cụ thể trong trường hợp sử dụng blockchain của bạn.
Để thử các hợp đồng nhiều chữ ký mà chúng tôi đã viết trong SmartPy, bạn có thể làm theo các bước sau:
Truy cập SmartPy IDE tại https://smartpy.io/ide.
Dán mã hợp đồng vào trình chỉnh sửa. Bạn có thể thay thế mã hiện có.
Để thực hiện hợp đồng, hãy nhấp vào nút “Chạy” nằm ở bảng trên cùng.
Sau khi chạy hợp đồng, bạn có thể xem việc thực hiện kịch bản trong bảng “Đầu ra” ở bên phải. Tại đây, bạn có thể xem chi tiết về từng hành động, bao gồm đề xuất, phiếu bầu và phê duyệt.
Để triển khai hợp đồng của bạn trên mạng Tezos, trước tiên bạn cần biên dịch nó. Nhấp vào nút "Biên dịch" trên bảng trên cùng.
Sau khi biên dịch, bạn có thể triển khai hợp đồng trên mạng thử nghiệm bằng cách nhấp vào “Triển khai Hợp đồng Michelson”. Bạn sẽ cần cung cấp Khóa bí mật cho tài khoản Tezos với đủ tiền để thanh toán chi phí gas khi triển khai.
Sau khi hợp đồng được triển khai, bạn sẽ được cung cấp địa chỉ của hợp đồng trên blockchain. Bạn có thể sử dụng địa chỉ này để tương tác với hợp đồng thông qua các giao dịch.
Để gửi đề xuất hoặc bỏ phiếu trong hợp đồng, bạn có thể sử dụng các điểm nhập được xác định trong mã hợp đồng, chẳng hạn như submit_proposal
hoặc vote_proposal
. Chúng có thể được gọi trực tiếp từ các giao dịch mà bạn tạo.
Hãy nhớ rằng, mặc dù SmartPy IDE cho phép bạn kiểm tra hợp đồng của mình trên chuỗi khối mô phỏng, nhưng việc triển khai hợp đồng trên mạng Tezos thực tế sẽ phát sinh chi phí gas, khoản phí này phải được thanh toán bằng XTZ, tiền điện tử gốc của mạng Tezos.
Hợp đồng nhiều chữ ký cung cấp một phương tiện để tạo ra quyền kiểm soát chung đối với tài sản. Các trường hợp sử dụng điển hình liên quan đến dịch vụ ký quỹ, quản lý tài khoản công ty, đồng ký kết các thỏa thuận tài chính, v.v. Những hợp đồng này đặc biệt có lợi cho các tổ chức hoặc nhóm nơi việc ra quyết định tập thể là cần thiết.
Theo thiết kế, các hợp đồng đa chữ ký có khả năng chống giả mạo và ngăn chặn các điểm lỗi đơn lẻ. Ngay cả khi khóa của một bên bị xâm phạm, kẻ tấn công không thể thực hiện giao dịch mà không có sự chấp thuận của các bên khác. Điều này bổ sung thêm một lớp bảo mật.
Hợp đồng nhiều chữ ký có thể được coi là một dạng kỹ thuật số tương đương với hộp ký gửi an toàn yêu cầu nhiều chìa khóa để mở. Tổng số chìa khóa (N) và số chìa khóa tối thiểu cần thiết để mở hộp (M) được thỏa thuận khi tạo hợp đồng.
Hợp đồng Multisig có thể có nhiều cấu hình khác nhau tùy thuộc vào giá trị của M và N:
Trong bối cảnh blockchain, hợp đồng nhiều chữ ký được sử dụng rộng rãi để tăng cường bảo mật giao dịch, hỗ trợ các cơ chế quản trị phức tạp hoặc duy trì quyền kiểm soát linh hoạt đối với tài sản blockchain. Dưới đây là một số ví dụ:
Đối với các ví dụ về mã của chúng tôi, chúng tôi sẽ xem xét ba cách triển khai hợp đồng đa chữ ký khác nhau:
Nó khá linh hoạt và cho phép sử dụng nhiều loại. Nó yêu cầu nhiều chữ ký để thực thi các hàm lambda tùy ý.
Python
nhập smartpy dưới dạng sp
@sp.module
def main():
Operation_lambda: type = sp.lambda_(sp.unit, sp.unit, with_Operations=True)
lớp MultisigLambda(sp.Contract):
"""Nhiều thành viên bỏ phiếu để thực thi lambdas.
Hợp đồng này có thể được bắt nguồn từ danh sách địa chỉ và số phiếu bầu bắt buộc là
. Bất kỳ thành viên nào cũng có thể gửi bao nhiêu lambda tùy thích và bỏ phiếu
cho các đề xuất đang hoạt động. Khi lambda đạt được số phiếu yêu cầu, mã của nó được gọi là
và các hoạt động đầu ra được thực thi. Điều này cho phép hợp đồng này
thực hiện bất kỳ điều gì mà hợp đồng có thể làm: chuyển mã thông báo, quản lý tài sản,
quản lý hợp đồng khác...
Khi áp dụng lambda, tất cả các lambda đã gửi cho đến thời điểm hiện tại đều không được kích hoạt.
Các thành viên vẫn có thể gửi lambdas mới.
"""
def __init__(tự, thành viên, require_votes):
"""Constructor
Args:
thành viên (sp.set of sp.address): những người có thể gửi và bỏ phiếu
cho lambda.
bắt buộc_votes (sp.nat): số phiếu bầu cần thiết
"""
khẳng định bắt buộc_votes <= sp.len(
thành viên
), "required_votes phải là <= len(thành viên)"
self.data.lambdas = sp.cast (
sp.big_map(), sp.big_map[sp.nat, Operation_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.members = sp.cast(thành viên, sp.set[sp.address])
self.data.required_votes = sp.cast(required_votes, sp.nat)
@sp.entrypoint
def submit_lambda(self, lambda_):
"""Gửi lambda mới để bỏ phiếu.
Việc gửi một đề xuất không có nghĩa là bỏ phiếu ủng hộ đề xuất đó.
Args:
lambda_(sp.lambda có phép toán): lambda đề xuất bỏ phiếu.
Tăng:
`Bạn không phải là thành viên`
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
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):
"""Bỏ phiếu cho lambda.
Đối số:
id(sp.nat): id của lambda để bỏ phiếu.
Tăng:
`Bạn không phải là thành viên`, `Lambda không hoạt động`, `Không tìm thấy Lambda`
Không có phiếu chống hoặc thông qua. Nếu ai đó không đồng ý với lambda
họ có thể tránh bỏ phiếu.
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
khẳng định id >= self.data.inactiveBefore, "Lambda không hoạt động"
khẳng định self.data.lambdas.contains(id), "Không tìm thấy Lambda"
self.data.votes[id].add(sp.sender)
if sp.len(self.data.votes[id]) >= self.data.required_votes:
self.data.lambdas[id]()
self.data.inactiveTrước = self.data.nextId
@sp.onchain_view()
def get_lambda(self, id):
"""Trả về lambda tương ứng.
Args:
id (sp.nat): id của lambda cần lấy.
Trả về:
cặp lambda và boolean hiển thị xem lambda có hoạt động hay không.
"""
trả về (self.data.lambdas[id], id >= self.data.inactiveBefore)
# if "templates" not in __name__:
@sp.module
def test():
class Administrated(sp.Contract):
def __init__(self, admin):
self.data.admin = admin
self.data.value = sp.int(0)
@sp.entrypoint
def set_value(self, value):
khẳng định sp.sender == self.data.admin
self.data.value = value
@sp.add_test(name="Kịch bản cơ bản MultisigLambda", is_default=True )
def basic_scenario():
"""Sử dụng multisigLambda làm quản trị viên của một hợp đồng mẫu.
Kiểm tra:
- Nguồn gốc
- Đệ trình Lambda
- Phiếu bầu Lambda
"""
sc = sp.test_scenario([main, test])
sc.h1("Kịch bản cơ bản.")
member1 = sp.test_account("member1")
member2 = sp.test_account("member2")
member3 = sp.test_account("member3")
thành viên = sp.set([member1.address, thành viên2.address, member3.address])
sc.h2("MultisigLambda: nguồn gốc")
c1 = main.MultisigLambda(thành viên, 2)
sc += c1
sc.h2("Được quản lý: nguồn gốc")
c2 = test.Administrated(c1.address)
sc += c2
sc.h2("MultisigLambda: submit_lambda")
def set_42(params):
administrated = sp.contract(sp.TINt, c2.address, entrypoint="set_value")
sp.transfer(sp. int(42), sp.tez(0), administrated.open_some())
lambda_ = sp.build_lambda(set_42, with_Operation=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(sender=member2)
# Chúng tôi có thể kiểm tra xem hợp đồng được quản lý đã nhận được chuyển khoản hay chưa.
sc.verify(c2.data.value == 42)
Nó giới thiệu khái niệm bỏ phiếu cho các đề xuất. Trong hợp đồng này, người ký có thể bỏ phiếu cho một số hành động nhất định sẽ được thực hiện và nếu đạt đến số đại biểu cần thiết, các hành động được đề xuất sẽ được thực hiện.
Python
import smartpy as sp
@sp.module
def main():
# Đặc tả loại hành động quản trị nội bộ
InternalAdminAction: type = sp.variant(
addSigners=sp.list[sp.address],
ChangeQuorum=sp.nat,
RemoveSigners=sp.list[sp.address],
)
lớp MultisigAction(sp.Contract):
"""Một hợp đồng có thể được nhiều người ký sử dụng để quản lý
hợp đồng khác. Các hợp đồng được quản trị triển khai
giao diện giúp có thể trình bày rõ ràng quy trình quản trị cho người dùng không phải là chuyên gia.
Người ký bỏ phiếu cho các đề xuất. Đề xuất là danh sách mục tiêu với danh sách
hành động. Một hành động là một byte đơn giản nhưng nó được dự định là giá trị gói bằng
biến thể. Mẫu đơn giản này giúp bạn có thể xây dựng giao diện UX
hiển thị nội dung của một đề xuất hoặc xây dựng một đề xuất.
"""
def __init__(tự, số đại biểu, người ký):
self.data.inactiveBefore = 0
self.data.nextId = 0
self.data.proposals = sp.cast(
sp.big_map(),
sp.big_map[
sp.nat,
sp.list[sp.record(target=sp.address, actions=sp.list[sp.bytes])],
],
)
self.data.quorum = sp.cast(quorum, sp.nat)
self.data.signers = sp.cast(signers, sp.set[sp.address])
self.data.votes = sp.cast(
sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]]
)
@sp.entrypoint
def send_proposal(self, đề xuất):
"""Chỉ dành cho người ký. Gửi đề xuất để bỏ phiếu.
Args:
đề xuất (sp.list of sp.record of target address and action): Danh sách\
mục tiêu và các hành động quản trị liên quan.
"""
khẳng định self.data.signers.contains(sp.sender), "Chỉ người ký mới có thể đề xuất"
self.data.proposals[self.data.nextId] = đề xuất
self.data.votes[self.data.nextId] = sp.set()
self.data.nextId += 1
@sp.entrypoint
def vote(self, pId):
"""Bỏ phiếu cho một hoặc nhiều đề xuất
Args:
pId (sp.nat): Id của đề xuất.
"""
khẳng định self.data.signers.contains(sp.sender), "Chỉ người ký mới có thể bỏ phiếu"
khẳng định self.data.votes.contains(pId), "Đề xuất không xác định"
khẳng định pId >= self.data.inactiveBefore, "Đề xuất không hoạt động"
self.data.votes[pId].add(sp.sender)
nếu sp.len(self.data.votes.get(pId, mặc định=sp.set())) >= self.data.quorum:
self._onApproved(pId)
@sp.private(with_storage="read-write", with_Operations=True)
def _onApproved(self, pId):
"""Hàm nội tuyến. Logic được áp dụng khi đề xuất đã được phê duyệt."""
đề xuất = self.data.proposals.get(pId, default=[])
cho p_item trong đề xuất:
hợp đồng = sp.contract(sp.list[sp.bytes], p_item.target)
sp.transfer(
p_item.actions,
sp.tez(0),
hợp đồng.unwrap_some(error="InvalidTarget"),
)
# Vô hiệu hóa tất cả các đề xuất đã được gửi.
self.data.inactiveTrước = self.data.nextId
@sp.entrypoint
def administrate(self, actions):
"""Chỉ tự gọi. Quản lý hợp đồng này.
Điểm vào này phải được gọi thông qua hệ thống đề xuất.
Args:
hành động (sp.list of sp.bytes): Danh sách biến thể đóng gói của \
`InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`).
"""
khẳng định (
sp.sender == sp.self_address()
), "Điểm vào này phải được gọi thông qua hệ thống đề xuất."
cho pack_actions trong hành động:
action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some(
error="Định dạng hành động xấu"
)
với sp.match(action):
với sp.case.changeQuorum với số đại biểu:
self.data.quorum = số đại biểu
với sp.case.addSigners được thêm vào:
cho người ký đã thêm:
self.data.signers.add(signer)
với sp.case.removeSigners như đã xóa:
cho địa chỉ đã xóa:
self.data.signers.remove(address)
# Đảm bảo rằng hợp đồng không bao giờ yêu cầu số đại biểu nhiều hơn tổng số người ký.
khẳng định self.data.quorum <= sp.len(
self.data.signers
), "Số đại biểu nhiều hơn người ký."
nếu "mẫu" không có trong __name__:
@sp.add_test(name="Basic script", is_default=True)
def test():
signer1 = sp.test_account("signer1")
người ký2 = sp.test_account("signer2")
người ký3 = sp.test_account("signer3")
s = sp.test_scenario(chính)
s.h1("Kịch bản cơ bản")
s.h2("Nguồn gốc")
c1 = main.MultisigAction(
quorum=2,
signers=sp.set([signer1.address, signer2.address]),
)
s += c1
s.h2("Đề xuất thêm người ký mới")
target = sp.to_address(
sp.contract(sp.TList(sp.TBytes), c1.address, "administrate").open_some()
)
action = sp.pack(
sp.set_type_expr(
sp.variant("addSigners", [signer3.address]), main.InternalAdminAction
)
)
c1.send_proposal([sp.record(target=target, actions=[action])]).run(
sender=signer1
)
s.h2("Người ký 1 phiếu cho đề xuất")
c1.vote(0).run(sender=signer1)
s.h2("Người ký 2 phiếu cho đề xuất")
c1.vote(0).run(sender=signer2)
s.verify(c1.data.signers.contains(signer3.address))
Nó cũng sử dụng một cơ chế bỏ phiếu. Hợp đồng này cho phép các thành viên gửi và bỏ phiếu cho các byte tùy ý. Khi một đề xuất đạt được số phiếu bầu cần thiết, trạng thái của nó có thể được xác nhận thông qua chế độ xem.
Python
import smartpy as sp
@sp.module
def main():
class MultisigView(sp.Contract):
"""Nhiều thành viên bỏ phiếu cho các byte tùy ý.
Hợp đồng này có thể được bắt nguồn từ danh sách địa chỉ và số phiếu bầu bắt buộc là
. Bất kỳ thành viên nào cũng có thể gửi bao nhiêu byte tùy thích và bỏ phiếu
cho các đề xuất đang hoạt động.
Bất kỳ byte nào đạt được số phiếu yêu cầu đều có thể được xác nhận thông qua chế độ xem.
"""
def __init__(tự, thành viên, bắt buộc_vote):
"""Constructor
Args:
thành viên (sp.set of sp.address): những người có thể gửi và bỏ phiếu cho
lambda.
bắt buộc_votes (sp.nat): số phiếu bầu cần thiết
"""
khẳng định bắt buộc_votes <= sp.len(
thành viên
), "required_votes phải là <= len(thành viên)"
self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.byte, sp.bool])
self.data.votes = sp.cast(
sp.big_map(), sp.big_map[sp.byte, sp.set[sp.address]]
)
self.data.members = sp.cast(thành viên, sp.set[sp.address])
self.data.required_votes = sp.cast(required_votes, sp.nat)
@sp.entrypoint
def submit_proposal(self, bytes):
"""Gửi đề xuất mới để bỏ phiếu.
Việc gửi một đề xuất không có nghĩa là bỏ phiếu ủng hộ đề xuất đó.
Đối số:
byte(sp.byte): byte được đề xuất bỏ phiếu.
Tăng:
`Bạn không phải là thành viên`
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
self.data.proposals[bytes] = Sai
self.data.votes[bytes] = sp.set()
@sp.entrypoint
def vote_proposal(self, byte):
"""Bỏ phiếu cho một đề xuất.
Không có phiếu chống hoặc thông qua. Nếu một người không đồng ý với một đề xuất,
có thể tránh bỏ phiếu. Cảnh báo: những đề xuất cũ chưa được bình chọn không bao giờ trở nên
thời.
Đối số:
id(sp.byte): byte của đề xuất.
Tăng:
`Bạn không phải là thành viên`, `Không tìm thấy đề xuất`
"""
khẳng định self.data.members.contains(sp.sender), "Bạn không phải là thành viên"
khẳng định self.data.proposals.contains(byte), "Không tìm thấy đề xuất"
self.data.votes[bytes].add(sp.sender)
if sp.len(self.data.votes[bytes]) >= self.data.required_votes:
self.data.proposals[byte] = Đúng
@sp.onchain_view()
def is_voted(self, id):
"""Trả về một boolean cho biết đề xuất đã được bỏ phiếu hay chưa.
Args:
id (sp.bytes): byte của đề xuất
Return:
(sp.bool): Đúng nếu đề xuất đã được biểu quyết, nếu không thì sai.
"""
trả về self.data.proposals.get(id, error="Không tìm thấy đề xuất")
nếu "mẫu" không có trong __name__:
@sp.add_test(name="MultisigView basic script", is_default=True)
def basic_scenario():
"""Một kịch bản có một cuộc bỏ phiếu cho hợp đồng multisigView.
Kiểm tra:
- Khởi tạo
- Nộp đề xuất
- Phiếu đề xuất
"""
sc = sp.test_scenario(main)
sc.h1("Kịch bản cơ bản.")
member1 = sp.test_account("member1")
member2 = sp.test_account("member2")
member3 = sp.test_account("member3")
thành viên = sp.set([member1.address, thành viên2.address, member3.address])
sc.h2("Gốc")
c1 = main.MultisigView(thành viên, 2)
sc += c1
sc.h2("submit_proposal")
c1.submit_proposal(sp.bytes("0x42")).run( sender=member1)
sc.h2("vote_proposal")
c1.vote_proposal(sp.bytes("0x42")).run(sender=member1)
c1.vote_proposal(sp.bytes("0x42")).run (sender=member2)
# Chúng tôi có thể kiểm tra xem đề xuất đã được xác thực chưa.
sc.verify(c1.is_voted(sp.bytes("0x42")))
Mỗi hợp đồng cung cấp một cơ chế khác nhau để đạt được khả năng kiểm soát đa chữ ký, mang lại sự linh hoạt tùy thuộc vào nhu cầu cụ thể trong trường hợp sử dụng blockchain của bạn.
Để thử các hợp đồng nhiều chữ ký mà chúng tôi đã viết trong SmartPy, bạn có thể làm theo các bước sau:
Truy cập SmartPy IDE tại https://smartpy.io/ide.
Dán mã hợp đồng vào trình chỉnh sửa. Bạn có thể thay thế mã hiện có.
Để thực hiện hợp đồng, hãy nhấp vào nút “Chạy” nằm ở bảng trên cùng.
Sau khi chạy hợp đồng, bạn có thể xem việc thực hiện kịch bản trong bảng “Đầu ra” ở bên phải. Tại đây, bạn có thể xem chi tiết về từng hành động, bao gồm đề xuất, phiếu bầu và phê duyệt.
Để triển khai hợp đồng của bạn trên mạng Tezos, trước tiên bạn cần biên dịch nó. Nhấp vào nút "Biên dịch" trên bảng trên cùng.
Sau khi biên dịch, bạn có thể triển khai hợp đồng trên mạng thử nghiệm bằng cách nhấp vào “Triển khai Hợp đồng Michelson”. Bạn sẽ cần cung cấp Khóa bí mật cho tài khoản Tezos với đủ tiền để thanh toán chi phí gas khi triển khai.
Sau khi hợp đồng được triển khai, bạn sẽ được cung cấp địa chỉ của hợp đồng trên blockchain. Bạn có thể sử dụng địa chỉ này để tương tác với hợp đồng thông qua các giao dịch.
Để gửi đề xuất hoặc bỏ phiếu trong hợp đồng, bạn có thể sử dụng các điểm nhập được xác định trong mã hợp đồng, chẳng hạn như submit_proposal
hoặc vote_proposal
. Chúng có thể được gọi trực tiếp từ các giao dịch mà bạn tạo.
Hãy nhớ rằng, mặc dù SmartPy IDE cho phép bạn kiểm tra hợp đồng của mình trên chuỗi khối mô phỏng, nhưng việc triển khai hợp đồng trên mạng Tezos thực tế sẽ phát sinh chi phí gas, khoản phí này phải được thanh toán bằng XTZ, tiền điện tử gốc của mạng Tezos.