Lição 1

บทนำและสัญญา Multisig

สัญญาหลายลายเซ็น (multisig) หรือที่เรียกว่าสัญญา "M-of-N" เป็นกลไกสำคัญที่ใช้ในการเพิ่มความปลอดภัยและความยืดหยุ่นของธุรกรรมในสภาพแวดล้อมบล็อกเชน สัญญาเหล่านี้เปลี่ยนวิธีการควบคุมสินทรัพย์โดยต้องได้รับการอนุมัติจากหลายฝ่ายก่อนจึงจะสามารถดำเนินธุรกรรมได้ คำว่า "M-of-N" หมายถึงข้อกำหนดที่ M จาก N ฝ่ายทั้งหมดต้องอนุมัติธุรกรรมเพื่อให้ธุรกรรมถูกต้อง

ทฤษฎีสัญญา Multisig

สัญญา Multisig มอบวิธีการสร้างการควบคุมสินทรัพย์ร่วมกัน กรณีการใช้งานทั่วไปเกี่ยวข้องกับบริการเอสโครว์ การจัดการบัญชีบริษัท การลงนามข้อตกลงทางการเงินร่วมกัน และอื่นๆ สัญญาเหล่านี้มีประโยชน์อย่างยิ่งสำหรับองค์กรหรือกลุ่มที่จำเป็นต้องมีการตัดสินใจร่วมกัน

จากการออกแบบ สัญญา Multisig ทนต่อการงัดแงะและป้องกันความล้มเหลวจุดเดียว แม้ว่าคีย์ของฝ่ายหนึ่งจะถูกบุกรุก ผู้โจมตีก็ไม่สามารถดำเนินธุรกรรมโดยไม่ได้รับอนุมัติจากอีกฝ่าย นี่เป็นการเพิ่มการรักษาความปลอดภัยอีกชั้นหนึ่ง

สัญญา Multisig ถือได้ว่าเป็นดิจิทัลที่เทียบเท่ากับตู้เซฟที่ต้องใช้กุญแจหลายดอกในการเปิด จำนวนกุญแจทั้งหมด (N) และจำนวนกุญแจขั้นต่ำที่ต้องใช้ในการเปิดกล่อง (M) ได้รับการตกลงกันเมื่อมีการสร้างสัญญา

สัญญา Multisig สามารถมีการกำหนดค่าที่แตกต่างกันได้มากมาย ขึ้นอยู่กับค่าของ M และ N:

  • 1-of-N: ฝ่ายเดียวจากทั้งหมดสามารถอนุมัติธุรกรรมได้ การกำหนดค่านี้เหมือนกับธุรกรรมปกติที่ไม่มี multisig อาจใช้เมื่อมีคีย์หลายตัวเพื่อความสะดวก แต่คีย์ใดคีย์หนึ่งสามารถอนุมัติธุรกรรมได้
  • N-of-N: ทุกฝ่ายจะต้องอนุมัติการทำธุรกรรม การกำหนดค่านี้ให้การรักษาความปลอดภัยระดับสูงสุด แต่อาจกลายเป็นปัญหาได้หากฝ่ายทำคีย์หายหรือปฏิเสธที่จะอนุมัติธุรกรรม
  • M-of-N (โดยที่ M < N): ส่วนย่อยของฝ่ายทั้งหมดจะต้องอนุมัติธุรกรรม การกำหนดค่านี้มักใช้ในทางปฏิบัติเพราะจะทำให้การรักษาความปลอดภัยสมดุลกับความยืดหยุ่น

สัญญา Multisig ใน Blockchain

ในบริบทของบล็อกเชน สัญญา Multisig ถูกนำมาใช้อย่างกว้างขวางเพื่อเพิ่มความปลอดภัยของธุรกรรม สนับสนุนกลไกการกำกับดูแลที่ซับซ้อน หรือรักษาการควบคุมสินทรัพย์บล็อกเชนที่ยืดหยุ่น นี่คือตัวอย่างบางส่วน:

  • กระเป๋าเงิน: กระเป๋าเงิน Multisig ใช้เพื่อรักษาความปลอดภัยทรัพย์สิน พวกเขาต้องการให้หลายฝ่ายลงนามในการทำธุรกรรม จึงเพิ่มความปลอดภัยจากการโจรกรรม การแฮ็กจากภายนอก และภัยคุกคามจากภายใน
  • องค์กรปกครองตนเองแบบกระจายอำนาจ (DAO): DAO มักใช้สัญญาหลายสัญญาเพื่อบังคับใช้กฎการกำกับดูแลของตน การลงคะแนนเสียงในข้อเสนอจะดำเนินการเป็นธุรกรรมหลายรายการ โดยมีสมาชิกของ DAO ทำหน้าที่เป็นผู้ลงนาม ข้อเสนอจะดำเนินการก็ต่อเมื่อได้รับคะแนนเสียงเพียงพอ
  • การดำเนินงานข้ามสายโซ่: ในการดำเนินงานข้ามสายโซ่ สัญญาหลายสัญญาสามารถทำหน้าที่เป็นผู้ดูแลทรัพย์สินได้ เมื่อสินทรัพย์ถูกย้ายจากบล็อกเชนหนึ่งไปยังอีกบล็อกหนึ่ง สัญญา multisig บนเชนต้นทางสามารถรับประกันได้ว่าสินทรัพย์จะถูกล็อคอย่างปลอดภัยจนกว่าการดำเนินการบนเชนอื่นจะได้รับการยืนยัน
    แม้ว่าการดำเนินการตามสัญญา Multisig อาจแตกต่างกันไปในแต่ละบล็อคเชน แต่แนวคิดหลักยังคงเหมือนเดิม นั่นคือความจำเป็นที่หลายฝ่ายจะต้องอนุมัติธุรกรรมก่อนที่จะดำเนินการ การรักษาความปลอดภัยอีกชั้นหนึ่งทำให้สัญญา multisig เป็นเครื่องมือสำคัญในพื้นที่บล็อกเชนและสกุลเงินดิจิทัล

ตัวอย่างการเขียนโค้ด: การเขียนและการปรับใช้สัญญา Multisig ด้วย SmartPy

สำหรับตัวอย่างโค้ดของเรา เราจะดูการใช้งานสัญญาแบบหลายลายเซ็นที่แตกต่างกันสามแบบ:

สัญญาแลมบ์ดา

มันค่อนข้างหลากหลายและสามารถใช้งานได้หลากหลาย ต้องใช้ลายเซ็นหลายรายการเพื่อเรียกใช้ฟังก์ชันแลมบ์ดาตามอำเภอใจ

Python 
 นำเข้า smartpy เป็น sp 


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

 คลาส MultisigLambda(sp.Contract): 
 """สมาชิกหลายคนโหวตให้ดำเนินการ lambdas

        สัญญานี้สามารถจัดทำขึ้นโดยมีรายชื่อที่อยู่และการลงคะแนนเสียงที่ต้องการจำนวน 
 เสียง สมาชิกคนใดก็ตามสามารถส่ง lambdas ได้มากเท่าที่ต้องการและโหวต 
 สำหรับข้อเสนอที่ยังดำเนินการอยู่ เมื่อแลมบ์ดาถึงคะแนนที่ต้องการ รหัสของมันจะถูกเรียก 
 และการดำเนินการเอาต์พุตจะถูกดำเนินการ สิ่งนี้ทำให้สัญญานี้สามารถ 
 ทำทุกสิ่งที่สัญญาทำได้: การโอนโทเค็น การจัดการสินทรัพย์ 
 การบริหารสัญญาอื่น...

        เมื่อใช้ lambda แลมบ์ดาที่ส่งมาทั้งหมดจนถึงขณะนี้จะถูกปิดใช้งาน
        สมาชิกยังสามารถส่งแลมบ์ดาใหม่ได้
        """ 

 def __init__(ตนเอง, สมาชิก, required_votes): 
 """Constructor 

 Args: สมาชิก 
 คน (sp.set of sp.address): ผู้ที่สามารถส่งและโหวต 
 สำหรับ lambda
                required_votes (sp.nat): จำนวนโหวตที่ต้องการ 
 """ 
 ยืนยัน required_votes <= sp.len( 
 สมาชิก 
 ), "required_votes ต้องเป็น <= len(members)" 
 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 (สมาชิก sp.set[sp.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def send_lambda(self, lambda_): 
 """ส่ง lambda ใหม่เพื่อโหวต

            การส่งข้อเสนอไม่ได้หมายความถึงการลงคะแนนเสียงเห็นชอบด้วย

            อาร์กิวเมนต์: 
 lambda_(sp.lambda พร้อมการดำเนินการ): lambda เสนอให้ลงคะแนน
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก` 
 """ 
 ยืนยัน self.data.members.contains(sp.sender) "คุณไม่ได้เป็นสมาชิก" 
 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): 
 """โหวตให้แลมบ์ดา

            อาร์กิวเมนต์: 
 id(sp.nat): รหัสแลมบ์ดาที่จะลงคะแนนให้
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก`, `แลมบ์ดาไม่ได้ใช้งาน`, `ไม่พบแลมบ์ดา' 

 ไม่มีการลงคะแนนคัดค้านหรือผ่าน หากมีใครไม่เห็นด้วยกับ lambda 
 ก็สามารถหลีกเลี่ยงการลงคะแนนได้
            """ 
 ยืนยัน self.data.members.contains (sp.sender) "คุณไม่ใช่สมาชิก" 
 ยืนยัน id >= self.data.inactiveBefore, "แลมบ์ดาไม่ได้ใช้งาน" 
 ยืนยัน self.data.lambdas.contains(id) "ไม่พบแลมบ์ดา" 
 self.data.votes[id].add(sp.sender)
            ถ้า sp.len (self.data.votes [id]) >= self.data.required_votes:
                self.data.lambdas[id]()
                self.data.inactiveBefore = self.data.nextId 

 @sp.onchain_view() 
 def get_lambda(self, id): 
 """ส่งคืนแลมบ์ดาที่เกี่ยวข้อง

            Args: 
 id (sp.nat): id ของแลมบ์ดาที่จะได้รับ

            กลับ: แลมบ์ดา 
 คู่และบูลีนแสดงว่าแลมบ์ดาทำงานอยู่หรือไม่
            """ 
 คืน (self.data.lambdas[id], id >= self.data.inactiveBefore)


# ถ้า "เทมเพลต" ไม่ได้อยู่ใน __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 (ตนเอง, ค่า): 
 ยืนยัน sp.sender == self.data.admin 
 self.data.value = ค่า 


 @sp.add_test (name = "สถานการณ์พื้นฐาน MultisigLambda", is_default = True ) 
 def basic_scenario(): 
 """ใช้ multisigLambda เป็นผู้ดูแลระบบของสัญญาตัวอย่าง

    การทดสอบ: 
 - กำเนิด 
 - การส่งแลมบ์ดา 
 - โหวตแลมบ์ดา 
 """ 
 sc = sp.test_scenario([main, ทดสอบ]) 
 sc.h1("สถานการณ์พื้นฐาน")

    member1 = sp.test_account("member1")
    member2 = sp.test_account("member2")
    member3 = sp.test_account("member3")
    สมาชิก = sp.set ([member1.address, สมาชิก2.ที่อยู่ สมาชิก3.ที่อยู่])

    sc.h2("MultisigLambda: origination") 
 c1 = main.MultisigLambda (สมาชิก 2) 
 sc += c1 

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

 sc.h2("MultisigLambda: send_lambda") 

 def set_42(params): 
 ผู้ดูแลระบบ = sp.contract(sp.TInt, c2.address, entrypoint="set_value") 
 sp.transfer(sp. อินท์(42) sp.tez(0), ผู้ดูแลระบบ.open_some())

    แลมบ์ดา_ = 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(sender=member2)

    # เราสามารถตรวจสอบได้ว่าสัญญาที่บริหารได้รับการโอนแล้ว
    sc.verify(c2.data.value == 42)

สัญญา MultisigAction

โดยจะแนะนำแนวคิดของการลงคะแนนเสียงสำหรับข้อเสนอ ในสัญญานี้ ผู้ลงนามสามารถลงคะแนนสำหรับการดำเนินการบางอย่างที่จะดำเนินการ และหากถึงองค์ประชุม การดำเนินการที่เสนอไว้ก็จะดำเนินการ

Python 
 นำเข้า smartpy เป็น sp 


 @sp.module 
 def main(): 
 # ข้อกำหนดประเภทการดำเนินการดูแลระบบภายใน 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.list[sp.address],
        changeQuorum=sp.nat,
        RemoveSigners=sp.list[sp.address],
    ) 

 คลาส MultisigAction(sp.Contract): 
 """สัญญาที่ผู้ลงนามหลายคนสามารถใช้ได้เพื่อจัดการสัญญาอีก 
 สัญญา สัญญาที่ได้รับการดูแลใช้อินเทอร์เฟซที่ทำให้สามารถ 
 กระบวนการดูแลระบบแก่ผู้ใช้ที่ไม่ใช่ผู้เชี่ยวชาญได้

        ผู้ลงนามลงคะแนนเสียงสำหรับข้อเสนอ ข้อเสนอคือรายการเป้าหมายที่มีรายการการดำเนินการ 
 รายการ การดำเนินการเป็นไบต์ธรรมดา แต่ตั้งใจให้เป็นค่าแพ็ก 
 ตัวแปร รูปแบบที่เรียบง่ายนี้ทำให้สามารถสร้างอินเทอร์เฟซ UX 
 ที่แสดงเนื้อหาของข้อเสนอหรือสร้างข้อเสนอได้
        """ 

 def __init__(ตนเอง, โควรัม, ผู้ลงนาม): 
 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, การกระทำ=sp.list[sp.bytes])],
                ], 
 ) 
 self.data.quorum = sp.cast(องค์ประชุม, sp.nat) 
 self.data.signers = sp.cast (ผู้ลงนาม 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, ข้อเสนอ): 
 """ผู้ลงนามเท่านั้น ส่งข้อเสนอเพื่อลงคะแนนเสียง

            Args: 
 ข้อเสนอ (sp.list of sp.record ของที่อยู่เป้าหมายและการดำเนินการ): รายการ\ 
 ของเป้าหมายและการดำเนินการด้านการบริหารที่เกี่ยวข้อง
            """ 
 ยืนยัน self.data.signers.contains (sp.sender) "มีเพียงผู้ลงนามเท่านั้นที่สามารถเสนอได้" 
 self.data.proposals[self.data.nextId] = ข้อเสนอ 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Vote for one or more ข้อเสนอที่ 

 Args: 
 pId (sp.nat): รหัสข้อเสนอ
            """ 
 ยืนยัน self.data.signers.contains (sp.sender) "มีเพียงผู้ลงนามเท่านั้นที่สามารถลงคะแนนได้" 
 ยืนยัน self.data.votes.contains(pId) "ไม่ทราบข้อเสนอ" 
 ยืนยัน pId >= self.data.inactiveBefore "ข้อเสนอไม่ทำงาน" 
 self.data.votes[pId].add(sp.sender)

            ถ้า sp.len(self.data.votes.get(pId, ค่าเริ่มต้น=sp.set())) >= self.data.quorum:
                self._onApproved(pId)

        @ sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """ฟังก์ชันอินไลน์ ตรรกะที่ใช้เมื่อข้อเสนอได้รับการอนุมัติแล้ว"""
            ข้อเสนอ = self.data.proposals.get (pId, default=[]) 
 สำหรับ p_item ในข้อเสนอ: 
 สัญญา = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer ( 
 p_item.actions,
                    sp.tez(0), 
 สัญญา unwrap_some (ข้อผิดพลาด = "InvalidTarget"),
                ) 
 # ยกเลิกข้อเสนอทั้งหมดที่ถูกส่งไปแล้ว
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def ผู้ดูแลระบบ (ตนเอง, การดำเนินการ): 
 """โทรด้วยตนเองเท่านั้น จัดการสัญญานี้

            จุดเริ่มต้นนี้จะต้องถูกเรียกผ่านระบบข้อเสนอ

            Args: 
 การดำเนินการ (sp.list ของ sp.bytes): รายการตัวแปรที่อัดแน่นของ \ 
 `InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`)
            """ 
 assert ( 
 sp.sender == sp.self_address() 
 ), "จุดเริ่มต้นนี้ต้องถูกเรียกผ่านระบบข้อเสนอ" 

 สำหรับ pack_actions ในการดำเนินการ: 
 action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="รูปแบบการกระทำที่ไม่ถูกต้อง" 
 ) 
 ด้วย sp.match(action): 
 พร้อม sp.case.changeQuorum เป็นองค์ประชุม: 
 self.data.quorum = องค์ประชุม 
 พร้อมด้วย sp.case.addSigners ตามที่เพิ่ม: 
 สำหรับผู้ลงนามที่เพิ่ม: 
 self.data.signers.add(ผู้ลงนาม)
                    ด้วย sp.case.removeSigners เมื่อถูกลบออก: 
 สำหรับที่อยู่ในการลบออก: 
 self.data.signers.remove (ที่อยู่)
                # ตรวจสอบให้แน่ใจว่าสัญญาไม่จำเป็นต้องมีองค์ประชุมมากกว่าจำนวนผู้ลงนามทั้งหมด
                assert self.data.quorum <= sp.len( 
 self.data.signers 
 ), "มีองค์ประชุมมากกว่าผู้ลงนาม"


หาก "เทมเพลต" ไม่ได้อยู่ใน __name__: 

 @sp.add_test(name="Basic allowance", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        ผู้ลงนาม2 = sp.test_account("ผู้ลงนาม2")
        ผู้ลงนาม3 = sp.test_account("ผู้ลงนาม3")

        s = sp.test_scenario (หลัก)
        s.h1("สถานการณ์พื้นฐาน") 

 s.h2("จุดเริ่มต้น") 
 c1 = main.MultisigAction( 
 quorum=2, 
 signers=sp.set([signer1.address, signer2.address]), 
 ) 
 s += c1 

 s.h2("ข้อเสนอสำหรับการเพิ่มผู้ลงนามใหม่") 
 target = sp.to_address(
            sp.contract(sp.TList(sp.TBytes), c1.address, "administrate").open_some() 
 ) 
 การกระทำ = sp.pack(
            sp.set_type_expr( 
 sp.variant("addSigners", [signer3.address]), main.InternalAdminAction 
 ) 
 ) 
 c1.send_proposal([sp.record(target=target, actions=[action])]).run( 
 ผู้ส่ง=signer1 
 ) 

 s.h2("ผู้ลงนาม 1 โหวตสำหรับข้อเสนอ") 
 c1.vote(0).run(sender=signer1)
        s.h2("ผู้ลงนาม 2 โหวตสำหรับข้อเสนอ") 
 c1.vote(0).run(sender=signer2)

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

สัญญา MultisigView

นอกจากนี้ยังใช้กลไกการลงคะแนนเสียง สัญญานี้อนุญาตให้สมาชิกส่งและลงคะแนนให้กับไบต์ตามอำเภอใจ เมื่อข้อเสนอได้รับคะแนนโหวตตามจำนวนที่กำหนด สถานะจะได้รับการยืนยันผ่านการดู

Python 
 นำเข้า smartpy เป็น sp 


 @sp.module 
 def main(): 
 คลาส MultisigView(sp.Contract): 
 """สมาชิกหลายคนโหวตให้กับไบต์ที่กำหนดเอง

        สัญญานี้สามารถจัดทำขึ้นโดยมีรายชื่อที่อยู่และการลงคะแนนเสียงที่ต้องการจำนวน 
 เสียง สมาชิกคนใดก็ตามสามารถส่งไบต์ได้มากเท่าที่ต้องการและโหวต 
 สำหรับข้อเสนอที่ใช้งานอยู่

        ไบต์ใดๆ ที่ได้รับการโหวตตามที่กำหนดสามารถยืนยันได้ผ่านมุมมอง
        """ 

 def __init__(ตนเอง, สมาชิก, required_votes): 
 """Constructor 

 Args: สมาชิก 
 คน (sp.set of sp.address): ผู้คนที่สามารถส่งและโหวตให้ 
 lambda
                required_votes (sp.nat): จำนวนโหวตที่ต้องการ 
 """ 
 ยืนยัน required_votes <= sp.len( 
 สมาชิก 
 ), "required_votes ต้องเป็น <= len(members)" 
 self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.ไบต์, sp.bool]) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.bytes, sp.set[sp.address]] 
 ) 
 self.data.members = sp.cast(สมาชิก, sp.set[sp.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def send_proposal(self, bytes): 
 """ส่งข้อเสนอใหม่เพื่อโหวต

            การส่งข้อเสนอไม่ได้หมายความถึงการลงคะแนนเสียงเห็นชอบ

            อาร์กิวเมนต์: 
 ไบต์ (sp.bytes): ไบต์ที่เสนอให้ลงคะแนนเสียง
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก` 
 """ 
 ยืนยัน self.data.members.contains(sp.sender) "คุณไม่ได้เป็นสมาชิก" 
 self.data.proposals[bytes] = เท็จ 
 self.data.votes[ไบต์] = sp.set()

        @ sp.entrypoint 
 def vote_proposal(self, bytes): 
 """โหวตข้อเสนอ

            ไม่มีการลงคะแนนเสียงคัดค้านหรือผ่าน หากใครไม่เห็นด้วยกับข้อเสนอ 
 สามารถหลีกเลี่ยงการลงคะแนนเสียงได้ คำเตือน: ข้อเสนอที่เก่าที่ไม่ได้ลงคะแนนจะไม่กลายเป็น 
 ล้าสมัย

            อาร์กิวเมนต์: 
 id (sp.bytes): จำนวนไบต์ของข้อเสนอ
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก`, `ไม่พบข้อเสนอ` 
 """ 
 ยืนยัน self.data.members.contains(sp.sender) "คุณไม่ใช่สมาชิก" 
 ยืนยัน self.data.proposals.contains(bytes) "ไม่พบข้อเสนอ" 
 self.data.votes [bytes] .add (sp.sender)
            ถ้า sp.len (self.data.votes [ไบต์]) >= self.data.required_votes:
                self.data.proposals [ไบต์] = True 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """ส่งคืนบูลีนเพื่อระบุว่าข้อเสนอได้รับการโหวตแล้วหรือ

            Args: 
 id (sp.bytes): ไบต์ของข้อเสนอ 
 ส่งคืน: 
 (sp.bool): เป็นจริงหากข้อเสนอได้รับการโหวต มิฉะนั้นจะเป็นเท็จ
            """ 
 คืน self.data.proposals.get (id, error="ไม่พบข้อเสนอ") 


 หาก "เทมเพลต" ไม่ได้อยู่ใน __name__: 

 @sp.add_test(name="MultisigView basic allowance", is_default=True) 
 def basic_scenario(): 
 """A สถานการณ์ด้วย การลงคะแนนในสัญญา multisigView

        การทดสอบ: 
 - การกำเนิด 
 - การส่งข้อเสนอ 
 - การโหวตข้อเสนอ 
 """ 
 sc = sp.test_scenario(main)
        sc.h1("สถานการณ์พื้นฐาน")

        member1 = sp.test_account("member1")
        member2 = sp.test_account("member2")
        member3 = sp.test_account("member3")
        สมาชิก = sp.set ([member1.address, สมาชิก2.ที่อยู่ สมาชิก3.ที่อยู่])

        sc.h2("การกำเนิด") 
 c1 = main.MultisigView(สมาชิก, 2) 
 sc += c1 

 sc.h2("submit_proposal") 
 c1.submit_proposal(sp.bytes("0x42")).run( ผู้ส่ง=member1) 

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

 # เราสามารถตรวจสอบได้ว่าข้อเสนอได้รับการตรวจสอบแล้ว
        sc.verify(c1.is_voted(sp.bytes("0x42")))

แต่ละสัญญามีกลไกที่แตกต่างกันเพื่อให้บรรลุการควบคุมหลายลายเซ็น โดยให้ความยืดหยุ่นโดยขึ้นอยู่กับความต้องการเฉพาะของกรณีการใช้งานบล็อกเชนของคุณ

คำแนะนำทีละขั้นตอนในการลองใช้สัญญา Multisig บน SmartPy Online

หากต้องการลองใช้สัญญา Multisig ที่เราเขียนใน SmartPy คุณสามารถทำตามขั้นตอนเหล่านี้:

  1. ไปที่ SmartPy IDE ที่ https://smartpy.io/ide

  2. วางรหัสสัญญาลงในตัวแก้ไข คุณสามารถแทนที่รหัสที่มีอยู่ได้

  3. ในการดำเนินการตามสัญญา ให้คลิกที่ปุ่ม "เรียกใช้" ที่แผงด้านบน

  4. หลังจากดำเนินการตามสัญญา คุณสามารถดูการดำเนินการตามสถานการณ์ได้ในแผง "ผลลัพธ์" ทางด้านขวา ที่นี่ คุณสามารถดูรายละเอียดของการดำเนินการแต่ละอย่าง รวมถึงข้อเสนอ การลงคะแนนเสียง และการอนุมัติ

  5. หากต้องการปรับใช้สัญญาของคุณบนเครือข่าย Tezos คุณต้องคอมไพล์ก่อน คลิกปุ่ม "คอมไพล์" ที่แผงด้านบน

  6. หลังจากคอมไพล์แล้ว คุณสามารถปรับใช้สัญญาบนเทสเน็ตได้โดยคลิก “ปรับใช้สัญญา Michelson” คุณจะต้องจัดเตรียมรหัสลับสำหรับบัญชี Tezos ที่มีเงินทุนเพียงพอสำหรับชำระค่าน้ำมันในการใช้งาน

  7. เมื่อปรับใช้สัญญาแล้ว คุณจะได้รับที่อยู่ของสัญญาบนบล็อกเชน คุณสามารถใช้ที่อยู่นี้เพื่อโต้ตอบกับสัญญาผ่านธุรกรรม

  8. หากต้องการส่งข้อเสนอหรือลงคะแนนในสัญญา คุณสามารถใช้จุดเข้าใช้งานที่กำหนดไว้ในรหัสสัญญา เช่น submit_proposal หรือ vote_proposal สิ่งเหล่านี้สามารถเรียกได้โดยตรงจากธุรกรรมที่คุณสร้าง

โปรดจำไว้ว่า แม้ว่า SmartPy IDE จะช่วยให้คุณสามารถทดสอบสัญญาของคุณบนบล็อกเชนจำลองได้ แต่การปรับใช้สัญญาบนเครือข่าย Tezos จริงจะต้องเสียค่าน้ำมัน ซึ่งจะต้องชำระเป็น XTZ ซึ่งเป็นสกุลเงินดิจิทัลดั้งเดิมของเครือข่าย Tezos

Exclusão de responsabilidade
* O investimento em criptomoedas envolve riscos significativos. Prossiga com cuidado. O curso não pretende ser um conselho de investimento.
* O curso é criado pelo autor que se juntou ao Gate Learn. Qualquer opinião partilhada pelo autor não representa o Gate Learn.
Catálogo
Lição 1

บทนำและสัญญา Multisig

สัญญาหลายลายเซ็น (multisig) หรือที่เรียกว่าสัญญา "M-of-N" เป็นกลไกสำคัญที่ใช้ในการเพิ่มความปลอดภัยและความยืดหยุ่นของธุรกรรมในสภาพแวดล้อมบล็อกเชน สัญญาเหล่านี้เปลี่ยนวิธีการควบคุมสินทรัพย์โดยต้องได้รับการอนุมัติจากหลายฝ่ายก่อนจึงจะสามารถดำเนินธุรกรรมได้ คำว่า "M-of-N" หมายถึงข้อกำหนดที่ M จาก N ฝ่ายทั้งหมดต้องอนุมัติธุรกรรมเพื่อให้ธุรกรรมถูกต้อง

ทฤษฎีสัญญา Multisig

สัญญา Multisig มอบวิธีการสร้างการควบคุมสินทรัพย์ร่วมกัน กรณีการใช้งานทั่วไปเกี่ยวข้องกับบริการเอสโครว์ การจัดการบัญชีบริษัท การลงนามข้อตกลงทางการเงินร่วมกัน และอื่นๆ สัญญาเหล่านี้มีประโยชน์อย่างยิ่งสำหรับองค์กรหรือกลุ่มที่จำเป็นต้องมีการตัดสินใจร่วมกัน

จากการออกแบบ สัญญา Multisig ทนต่อการงัดแงะและป้องกันความล้มเหลวจุดเดียว แม้ว่าคีย์ของฝ่ายหนึ่งจะถูกบุกรุก ผู้โจมตีก็ไม่สามารถดำเนินธุรกรรมโดยไม่ได้รับอนุมัติจากอีกฝ่าย นี่เป็นการเพิ่มการรักษาความปลอดภัยอีกชั้นหนึ่ง

สัญญา Multisig ถือได้ว่าเป็นดิจิทัลที่เทียบเท่ากับตู้เซฟที่ต้องใช้กุญแจหลายดอกในการเปิด จำนวนกุญแจทั้งหมด (N) และจำนวนกุญแจขั้นต่ำที่ต้องใช้ในการเปิดกล่อง (M) ได้รับการตกลงกันเมื่อมีการสร้างสัญญา

สัญญา Multisig สามารถมีการกำหนดค่าที่แตกต่างกันได้มากมาย ขึ้นอยู่กับค่าของ M และ N:

  • 1-of-N: ฝ่ายเดียวจากทั้งหมดสามารถอนุมัติธุรกรรมได้ การกำหนดค่านี้เหมือนกับธุรกรรมปกติที่ไม่มี multisig อาจใช้เมื่อมีคีย์หลายตัวเพื่อความสะดวก แต่คีย์ใดคีย์หนึ่งสามารถอนุมัติธุรกรรมได้
  • N-of-N: ทุกฝ่ายจะต้องอนุมัติการทำธุรกรรม การกำหนดค่านี้ให้การรักษาความปลอดภัยระดับสูงสุด แต่อาจกลายเป็นปัญหาได้หากฝ่ายทำคีย์หายหรือปฏิเสธที่จะอนุมัติธุรกรรม
  • M-of-N (โดยที่ M < N): ส่วนย่อยของฝ่ายทั้งหมดจะต้องอนุมัติธุรกรรม การกำหนดค่านี้มักใช้ในทางปฏิบัติเพราะจะทำให้การรักษาความปลอดภัยสมดุลกับความยืดหยุ่น

สัญญา Multisig ใน Blockchain

ในบริบทของบล็อกเชน สัญญา Multisig ถูกนำมาใช้อย่างกว้างขวางเพื่อเพิ่มความปลอดภัยของธุรกรรม สนับสนุนกลไกการกำกับดูแลที่ซับซ้อน หรือรักษาการควบคุมสินทรัพย์บล็อกเชนที่ยืดหยุ่น นี่คือตัวอย่างบางส่วน:

  • กระเป๋าเงิน: กระเป๋าเงิน Multisig ใช้เพื่อรักษาความปลอดภัยทรัพย์สิน พวกเขาต้องการให้หลายฝ่ายลงนามในการทำธุรกรรม จึงเพิ่มความปลอดภัยจากการโจรกรรม การแฮ็กจากภายนอก และภัยคุกคามจากภายใน
  • องค์กรปกครองตนเองแบบกระจายอำนาจ (DAO): DAO มักใช้สัญญาหลายสัญญาเพื่อบังคับใช้กฎการกำกับดูแลของตน การลงคะแนนเสียงในข้อเสนอจะดำเนินการเป็นธุรกรรมหลายรายการ โดยมีสมาชิกของ DAO ทำหน้าที่เป็นผู้ลงนาม ข้อเสนอจะดำเนินการก็ต่อเมื่อได้รับคะแนนเสียงเพียงพอ
  • การดำเนินงานข้ามสายโซ่: ในการดำเนินงานข้ามสายโซ่ สัญญาหลายสัญญาสามารถทำหน้าที่เป็นผู้ดูแลทรัพย์สินได้ เมื่อสินทรัพย์ถูกย้ายจากบล็อกเชนหนึ่งไปยังอีกบล็อกหนึ่ง สัญญา multisig บนเชนต้นทางสามารถรับประกันได้ว่าสินทรัพย์จะถูกล็อคอย่างปลอดภัยจนกว่าการดำเนินการบนเชนอื่นจะได้รับการยืนยัน
    แม้ว่าการดำเนินการตามสัญญา Multisig อาจแตกต่างกันไปในแต่ละบล็อคเชน แต่แนวคิดหลักยังคงเหมือนเดิม นั่นคือความจำเป็นที่หลายฝ่ายจะต้องอนุมัติธุรกรรมก่อนที่จะดำเนินการ การรักษาความปลอดภัยอีกชั้นหนึ่งทำให้สัญญา multisig เป็นเครื่องมือสำคัญในพื้นที่บล็อกเชนและสกุลเงินดิจิทัล

ตัวอย่างการเขียนโค้ด: การเขียนและการปรับใช้สัญญา Multisig ด้วย SmartPy

สำหรับตัวอย่างโค้ดของเรา เราจะดูการใช้งานสัญญาแบบหลายลายเซ็นที่แตกต่างกันสามแบบ:

สัญญาแลมบ์ดา

มันค่อนข้างหลากหลายและสามารถใช้งานได้หลากหลาย ต้องใช้ลายเซ็นหลายรายการเพื่อเรียกใช้ฟังก์ชันแลมบ์ดาตามอำเภอใจ

Python 
 นำเข้า smartpy เป็น sp 


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

 คลาส MultisigLambda(sp.Contract): 
 """สมาชิกหลายคนโหวตให้ดำเนินการ lambdas

        สัญญานี้สามารถจัดทำขึ้นโดยมีรายชื่อที่อยู่และการลงคะแนนเสียงที่ต้องการจำนวน 
 เสียง สมาชิกคนใดก็ตามสามารถส่ง lambdas ได้มากเท่าที่ต้องการและโหวต 
 สำหรับข้อเสนอที่ยังดำเนินการอยู่ เมื่อแลมบ์ดาถึงคะแนนที่ต้องการ รหัสของมันจะถูกเรียก 
 และการดำเนินการเอาต์พุตจะถูกดำเนินการ สิ่งนี้ทำให้สัญญานี้สามารถ 
 ทำทุกสิ่งที่สัญญาทำได้: การโอนโทเค็น การจัดการสินทรัพย์ 
 การบริหารสัญญาอื่น...

        เมื่อใช้ lambda แลมบ์ดาที่ส่งมาทั้งหมดจนถึงขณะนี้จะถูกปิดใช้งาน
        สมาชิกยังสามารถส่งแลมบ์ดาใหม่ได้
        """ 

 def __init__(ตนเอง, สมาชิก, required_votes): 
 """Constructor 

 Args: สมาชิก 
 คน (sp.set of sp.address): ผู้ที่สามารถส่งและโหวต 
 สำหรับ lambda
                required_votes (sp.nat): จำนวนโหวตที่ต้องการ 
 """ 
 ยืนยัน required_votes <= sp.len( 
 สมาชิก 
 ), "required_votes ต้องเป็น <= len(members)" 
 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 (สมาชิก sp.set[sp.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def send_lambda(self, lambda_): 
 """ส่ง lambda ใหม่เพื่อโหวต

            การส่งข้อเสนอไม่ได้หมายความถึงการลงคะแนนเสียงเห็นชอบด้วย

            อาร์กิวเมนต์: 
 lambda_(sp.lambda พร้อมการดำเนินการ): lambda เสนอให้ลงคะแนน
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก` 
 """ 
 ยืนยัน self.data.members.contains(sp.sender) "คุณไม่ได้เป็นสมาชิก" 
 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): 
 """โหวตให้แลมบ์ดา

            อาร์กิวเมนต์: 
 id(sp.nat): รหัสแลมบ์ดาที่จะลงคะแนนให้
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก`, `แลมบ์ดาไม่ได้ใช้งาน`, `ไม่พบแลมบ์ดา' 

 ไม่มีการลงคะแนนคัดค้านหรือผ่าน หากมีใครไม่เห็นด้วยกับ lambda 
 ก็สามารถหลีกเลี่ยงการลงคะแนนได้
            """ 
 ยืนยัน self.data.members.contains (sp.sender) "คุณไม่ใช่สมาชิก" 
 ยืนยัน id >= self.data.inactiveBefore, "แลมบ์ดาไม่ได้ใช้งาน" 
 ยืนยัน self.data.lambdas.contains(id) "ไม่พบแลมบ์ดา" 
 self.data.votes[id].add(sp.sender)
            ถ้า sp.len (self.data.votes [id]) >= self.data.required_votes:
                self.data.lambdas[id]()
                self.data.inactiveBefore = self.data.nextId 

 @sp.onchain_view() 
 def get_lambda(self, id): 
 """ส่งคืนแลมบ์ดาที่เกี่ยวข้อง

            Args: 
 id (sp.nat): id ของแลมบ์ดาที่จะได้รับ

            กลับ: แลมบ์ดา 
 คู่และบูลีนแสดงว่าแลมบ์ดาทำงานอยู่หรือไม่
            """ 
 คืน (self.data.lambdas[id], id >= self.data.inactiveBefore)


# ถ้า "เทมเพลต" ไม่ได้อยู่ใน __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 (ตนเอง, ค่า): 
 ยืนยัน sp.sender == self.data.admin 
 self.data.value = ค่า 


 @sp.add_test (name = "สถานการณ์พื้นฐาน MultisigLambda", is_default = True ) 
 def basic_scenario(): 
 """ใช้ multisigLambda เป็นผู้ดูแลระบบของสัญญาตัวอย่าง

    การทดสอบ: 
 - กำเนิด 
 - การส่งแลมบ์ดา 
 - โหวตแลมบ์ดา 
 """ 
 sc = sp.test_scenario([main, ทดสอบ]) 
 sc.h1("สถานการณ์พื้นฐาน")

    member1 = sp.test_account("member1")
    member2 = sp.test_account("member2")
    member3 = sp.test_account("member3")
    สมาชิก = sp.set ([member1.address, สมาชิก2.ที่อยู่ สมาชิก3.ที่อยู่])

    sc.h2("MultisigLambda: origination") 
 c1 = main.MultisigLambda (สมาชิก 2) 
 sc += c1 

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

 sc.h2("MultisigLambda: send_lambda") 

 def set_42(params): 
 ผู้ดูแลระบบ = sp.contract(sp.TInt, c2.address, entrypoint="set_value") 
 sp.transfer(sp. อินท์(42) sp.tez(0), ผู้ดูแลระบบ.open_some())

    แลมบ์ดา_ = 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(sender=member2)

    # เราสามารถตรวจสอบได้ว่าสัญญาที่บริหารได้รับการโอนแล้ว
    sc.verify(c2.data.value == 42)

สัญญา MultisigAction

โดยจะแนะนำแนวคิดของการลงคะแนนเสียงสำหรับข้อเสนอ ในสัญญานี้ ผู้ลงนามสามารถลงคะแนนสำหรับการดำเนินการบางอย่างที่จะดำเนินการ และหากถึงองค์ประชุม การดำเนินการที่เสนอไว้ก็จะดำเนินการ

Python 
 นำเข้า smartpy เป็น sp 


 @sp.module 
 def main(): 
 # ข้อกำหนดประเภทการดำเนินการดูแลระบบภายใน 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.list[sp.address],
        changeQuorum=sp.nat,
        RemoveSigners=sp.list[sp.address],
    ) 

 คลาส MultisigAction(sp.Contract): 
 """สัญญาที่ผู้ลงนามหลายคนสามารถใช้ได้เพื่อจัดการสัญญาอีก 
 สัญญา สัญญาที่ได้รับการดูแลใช้อินเทอร์เฟซที่ทำให้สามารถ 
 กระบวนการดูแลระบบแก่ผู้ใช้ที่ไม่ใช่ผู้เชี่ยวชาญได้

        ผู้ลงนามลงคะแนนเสียงสำหรับข้อเสนอ ข้อเสนอคือรายการเป้าหมายที่มีรายการการดำเนินการ 
 รายการ การดำเนินการเป็นไบต์ธรรมดา แต่ตั้งใจให้เป็นค่าแพ็ก 
 ตัวแปร รูปแบบที่เรียบง่ายนี้ทำให้สามารถสร้างอินเทอร์เฟซ UX 
 ที่แสดงเนื้อหาของข้อเสนอหรือสร้างข้อเสนอได้
        """ 

 def __init__(ตนเอง, โควรัม, ผู้ลงนาม): 
 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, การกระทำ=sp.list[sp.bytes])],
                ], 
 ) 
 self.data.quorum = sp.cast(องค์ประชุม, sp.nat) 
 self.data.signers = sp.cast (ผู้ลงนาม 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, ข้อเสนอ): 
 """ผู้ลงนามเท่านั้น ส่งข้อเสนอเพื่อลงคะแนนเสียง

            Args: 
 ข้อเสนอ (sp.list of sp.record ของที่อยู่เป้าหมายและการดำเนินการ): รายการ\ 
 ของเป้าหมายและการดำเนินการด้านการบริหารที่เกี่ยวข้อง
            """ 
 ยืนยัน self.data.signers.contains (sp.sender) "มีเพียงผู้ลงนามเท่านั้นที่สามารถเสนอได้" 
 self.data.proposals[self.data.nextId] = ข้อเสนอ 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Vote for one or more ข้อเสนอที่ 

 Args: 
 pId (sp.nat): รหัสข้อเสนอ
            """ 
 ยืนยัน self.data.signers.contains (sp.sender) "มีเพียงผู้ลงนามเท่านั้นที่สามารถลงคะแนนได้" 
 ยืนยัน self.data.votes.contains(pId) "ไม่ทราบข้อเสนอ" 
 ยืนยัน pId >= self.data.inactiveBefore "ข้อเสนอไม่ทำงาน" 
 self.data.votes[pId].add(sp.sender)

            ถ้า sp.len(self.data.votes.get(pId, ค่าเริ่มต้น=sp.set())) >= self.data.quorum:
                self._onApproved(pId)

        @ sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """ฟังก์ชันอินไลน์ ตรรกะที่ใช้เมื่อข้อเสนอได้รับการอนุมัติแล้ว"""
            ข้อเสนอ = self.data.proposals.get (pId, default=[]) 
 สำหรับ p_item ในข้อเสนอ: 
 สัญญา = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer ( 
 p_item.actions,
                    sp.tez(0), 
 สัญญา unwrap_some (ข้อผิดพลาด = "InvalidTarget"),
                ) 
 # ยกเลิกข้อเสนอทั้งหมดที่ถูกส่งไปแล้ว
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def ผู้ดูแลระบบ (ตนเอง, การดำเนินการ): 
 """โทรด้วยตนเองเท่านั้น จัดการสัญญานี้

            จุดเริ่มต้นนี้จะต้องถูกเรียกผ่านระบบข้อเสนอ

            Args: 
 การดำเนินการ (sp.list ของ sp.bytes): รายการตัวแปรที่อัดแน่นของ \ 
 `InternalAdminAction` (`addSigners`, `changeQuorum`, `removeSigners`)
            """ 
 assert ( 
 sp.sender == sp.self_address() 
 ), "จุดเริ่มต้นนี้ต้องถูกเรียกผ่านระบบข้อเสนอ" 

 สำหรับ pack_actions ในการดำเนินการ: 
 action = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="รูปแบบการกระทำที่ไม่ถูกต้อง" 
 ) 
 ด้วย sp.match(action): 
 พร้อม sp.case.changeQuorum เป็นองค์ประชุม: 
 self.data.quorum = องค์ประชุม 
 พร้อมด้วย sp.case.addSigners ตามที่เพิ่ม: 
 สำหรับผู้ลงนามที่เพิ่ม: 
 self.data.signers.add(ผู้ลงนาม)
                    ด้วย sp.case.removeSigners เมื่อถูกลบออก: 
 สำหรับที่อยู่ในการลบออก: 
 self.data.signers.remove (ที่อยู่)
                # ตรวจสอบให้แน่ใจว่าสัญญาไม่จำเป็นต้องมีองค์ประชุมมากกว่าจำนวนผู้ลงนามทั้งหมด
                assert self.data.quorum <= sp.len( 
 self.data.signers 
 ), "มีองค์ประชุมมากกว่าผู้ลงนาม"


หาก "เทมเพลต" ไม่ได้อยู่ใน __name__: 

 @sp.add_test(name="Basic allowance", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        ผู้ลงนาม2 = sp.test_account("ผู้ลงนาม2")
        ผู้ลงนาม3 = sp.test_account("ผู้ลงนาม3")

        s = sp.test_scenario (หลัก)
        s.h1("สถานการณ์พื้นฐาน") 

 s.h2("จุดเริ่มต้น") 
 c1 = main.MultisigAction( 
 quorum=2, 
 signers=sp.set([signer1.address, signer2.address]), 
 ) 
 s += c1 

 s.h2("ข้อเสนอสำหรับการเพิ่มผู้ลงนามใหม่") 
 target = sp.to_address(
            sp.contract(sp.TList(sp.TBytes), c1.address, "administrate").open_some() 
 ) 
 การกระทำ = sp.pack(
            sp.set_type_expr( 
 sp.variant("addSigners", [signer3.address]), main.InternalAdminAction 
 ) 
 ) 
 c1.send_proposal([sp.record(target=target, actions=[action])]).run( 
 ผู้ส่ง=signer1 
 ) 

 s.h2("ผู้ลงนาม 1 โหวตสำหรับข้อเสนอ") 
 c1.vote(0).run(sender=signer1)
        s.h2("ผู้ลงนาม 2 โหวตสำหรับข้อเสนอ") 
 c1.vote(0).run(sender=signer2)

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

สัญญา MultisigView

นอกจากนี้ยังใช้กลไกการลงคะแนนเสียง สัญญานี้อนุญาตให้สมาชิกส่งและลงคะแนนให้กับไบต์ตามอำเภอใจ เมื่อข้อเสนอได้รับคะแนนโหวตตามจำนวนที่กำหนด สถานะจะได้รับการยืนยันผ่านการดู

Python 
 นำเข้า smartpy เป็น sp 


 @sp.module 
 def main(): 
 คลาส MultisigView(sp.Contract): 
 """สมาชิกหลายคนโหวตให้กับไบต์ที่กำหนดเอง

        สัญญานี้สามารถจัดทำขึ้นโดยมีรายชื่อที่อยู่และการลงคะแนนเสียงที่ต้องการจำนวน 
 เสียง สมาชิกคนใดก็ตามสามารถส่งไบต์ได้มากเท่าที่ต้องการและโหวต 
 สำหรับข้อเสนอที่ใช้งานอยู่

        ไบต์ใดๆ ที่ได้รับการโหวตตามที่กำหนดสามารถยืนยันได้ผ่านมุมมอง
        """ 

 def __init__(ตนเอง, สมาชิก, required_votes): 
 """Constructor 

 Args: สมาชิก 
 คน (sp.set of sp.address): ผู้คนที่สามารถส่งและโหวตให้ 
 lambda
                required_votes (sp.nat): จำนวนโหวตที่ต้องการ 
 """ 
 ยืนยัน required_votes <= sp.len( 
 สมาชิก 
 ), "required_votes ต้องเป็น <= len(members)" 
 self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.ไบต์, sp.bool]) 
 self.data.votes = sp.cast(
                sp.big_map(), sp.big_map[sp.bytes, sp.set[sp.address]] 
 ) 
 self.data.members = sp.cast(สมาชิก, sp.set[sp.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def send_proposal(self, bytes): 
 """ส่งข้อเสนอใหม่เพื่อโหวต

            การส่งข้อเสนอไม่ได้หมายความถึงการลงคะแนนเสียงเห็นชอบ

            อาร์กิวเมนต์: 
 ไบต์ (sp.bytes): ไบต์ที่เสนอให้ลงคะแนนเสียง
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก` 
 """ 
 ยืนยัน self.data.members.contains(sp.sender) "คุณไม่ได้เป็นสมาชิก" 
 self.data.proposals[bytes] = เท็จ 
 self.data.votes[ไบต์] = sp.set()

        @ sp.entrypoint 
 def vote_proposal(self, bytes): 
 """โหวตข้อเสนอ

            ไม่มีการลงคะแนนเสียงคัดค้านหรือผ่าน หากใครไม่เห็นด้วยกับข้อเสนอ 
 สามารถหลีกเลี่ยงการลงคะแนนเสียงได้ คำเตือน: ข้อเสนอที่เก่าที่ไม่ได้ลงคะแนนจะไม่กลายเป็น 
 ล้าสมัย

            อาร์กิวเมนต์: 
 id (sp.bytes): จำนวนไบต์ของข้อเสนอ
            เพิ่ม: 
 `คุณไม่ใช่สมาชิก`, `ไม่พบข้อเสนอ` 
 """ 
 ยืนยัน self.data.members.contains(sp.sender) "คุณไม่ใช่สมาชิก" 
 ยืนยัน self.data.proposals.contains(bytes) "ไม่พบข้อเสนอ" 
 self.data.votes [bytes] .add (sp.sender)
            ถ้า sp.len (self.data.votes [ไบต์]) >= self.data.required_votes:
                self.data.proposals [ไบต์] = True 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """ส่งคืนบูลีนเพื่อระบุว่าข้อเสนอได้รับการโหวตแล้วหรือ

            Args: 
 id (sp.bytes): ไบต์ของข้อเสนอ 
 ส่งคืน: 
 (sp.bool): เป็นจริงหากข้อเสนอได้รับการโหวต มิฉะนั้นจะเป็นเท็จ
            """ 
 คืน self.data.proposals.get (id, error="ไม่พบข้อเสนอ") 


 หาก "เทมเพลต" ไม่ได้อยู่ใน __name__: 

 @sp.add_test(name="MultisigView basic allowance", is_default=True) 
 def basic_scenario(): 
 """A สถานการณ์ด้วย การลงคะแนนในสัญญา multisigView

        การทดสอบ: 
 - การกำเนิด 
 - การส่งข้อเสนอ 
 - การโหวตข้อเสนอ 
 """ 
 sc = sp.test_scenario(main)
        sc.h1("สถานการณ์พื้นฐาน")

        member1 = sp.test_account("member1")
        member2 = sp.test_account("member2")
        member3 = sp.test_account("member3")
        สมาชิก = sp.set ([member1.address, สมาชิก2.ที่อยู่ สมาชิก3.ที่อยู่])

        sc.h2("การกำเนิด") 
 c1 = main.MultisigView(สมาชิก, 2) 
 sc += c1 

 sc.h2("submit_proposal") 
 c1.submit_proposal(sp.bytes("0x42")).run( ผู้ส่ง=member1) 

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

 # เราสามารถตรวจสอบได้ว่าข้อเสนอได้รับการตรวจสอบแล้ว
        sc.verify(c1.is_voted(sp.bytes("0x42")))

แต่ละสัญญามีกลไกที่แตกต่างกันเพื่อให้บรรลุการควบคุมหลายลายเซ็น โดยให้ความยืดหยุ่นโดยขึ้นอยู่กับความต้องการเฉพาะของกรณีการใช้งานบล็อกเชนของคุณ

คำแนะนำทีละขั้นตอนในการลองใช้สัญญา Multisig บน SmartPy Online

หากต้องการลองใช้สัญญา Multisig ที่เราเขียนใน SmartPy คุณสามารถทำตามขั้นตอนเหล่านี้:

  1. ไปที่ SmartPy IDE ที่ https://smartpy.io/ide

  2. วางรหัสสัญญาลงในตัวแก้ไข คุณสามารถแทนที่รหัสที่มีอยู่ได้

  3. ในการดำเนินการตามสัญญา ให้คลิกที่ปุ่ม "เรียกใช้" ที่แผงด้านบน

  4. หลังจากดำเนินการตามสัญญา คุณสามารถดูการดำเนินการตามสถานการณ์ได้ในแผง "ผลลัพธ์" ทางด้านขวา ที่นี่ คุณสามารถดูรายละเอียดของการดำเนินการแต่ละอย่าง รวมถึงข้อเสนอ การลงคะแนนเสียง และการอนุมัติ

  5. หากต้องการปรับใช้สัญญาของคุณบนเครือข่าย Tezos คุณต้องคอมไพล์ก่อน คลิกปุ่ม "คอมไพล์" ที่แผงด้านบน

  6. หลังจากคอมไพล์แล้ว คุณสามารถปรับใช้สัญญาบนเทสเน็ตได้โดยคลิก “ปรับใช้สัญญา Michelson” คุณจะต้องจัดเตรียมรหัสลับสำหรับบัญชี Tezos ที่มีเงินทุนเพียงพอสำหรับชำระค่าน้ำมันในการใช้งาน

  7. เมื่อปรับใช้สัญญาแล้ว คุณจะได้รับที่อยู่ของสัญญาบนบล็อกเชน คุณสามารถใช้ที่อยู่นี้เพื่อโต้ตอบกับสัญญาผ่านธุรกรรม

  8. หากต้องการส่งข้อเสนอหรือลงคะแนนในสัญญา คุณสามารถใช้จุดเข้าใช้งานที่กำหนดไว้ในรหัสสัญญา เช่น submit_proposal หรือ vote_proposal สิ่งเหล่านี้สามารถเรียกได้โดยตรงจากธุรกรรมที่คุณสร้าง

โปรดจำไว้ว่า แม้ว่า SmartPy IDE จะช่วยให้คุณสามารถทดสอบสัญญาของคุณบนบล็อกเชนจำลองได้ แต่การปรับใช้สัญญาบนเครือข่าย Tezos จริงจะต้องเสียค่าน้ำมัน ซึ่งจะต้องชำระเป็น XTZ ซึ่งเป็นสกุลเงินดิจิทัลดั้งเดิมของเครือข่าย Tezos

Exclusão de responsabilidade
* O investimento em criptomoedas envolve riscos significativos. Prossiga com cuidado. O curso não pretende ser um conselho de investimento.
* O curso é criado pelo autor que se juntou ao Gate Learn. Qualquer opinião partilhada pelo autor não representa o Gate Learn.