บทเรียนที่ 1

Einführung und Multisig-Verträge

Multisignatur-Verträge (Multisig-Verträge), auch bekannt als „M-of-N“-Verträge, sind ein entscheidender Mechanismus zur Erhöhung der Sicherheit und Flexibilität von Transaktionen in der Blockchain-Umgebung. Diese Verträge verändern die Art und Weise, wie die Kontrolle über Vermögenswerte ausgeübt wird, indem sie die Zustimmung mehrerer Parteien erfordern, bevor Transaktionen ausgeführt werden können. Der Begriff „M-von-N“ bezieht sich auf die Anforderung, dass M von insgesamt N Parteien die Transaktion genehmigen müssen, damit sie gültig ist.

Theorie der Multisig-Verträge

Multisig-Verträge bieten eine Möglichkeit, eine gemeinsame Kontrolle über Vermögenswerte zu schaffen. Zu den typischen Anwendungsfällen gehören Treuhanddienste, Unternehmenskontoverwaltung, Mitunterzeichnung von Finanzvereinbarungen und mehr. Diese Verträge sind besonders vorteilhaft für Organisationen oder Gruppen, bei denen eine gemeinsame Entscheidungsfindung erforderlich ist.

Multisig-Verträge sind von Natur aus manipulationssicher und verhindern Single Points of Failure. Selbst wenn die Schlüssel einer Partei kompromittiert werden, kann der Angreifer keine Transaktionen ohne Zustimmung der anderen Parteien ausführen. Dies fügt eine zusätzliche Sicherheitsebene hinzu.

Multisig-Verträge können als digitales Äquivalent eines Schließfachs betrachtet werden, für dessen Öffnung mehrere Schlüssel erforderlich sind. Die Gesamtzahl der Schlüssel (N) und die Mindestanzahl der zum Öffnen der Box erforderlichen Schlüssel (M) werden bei Vertragsabschluss vereinbart.

Multisig-Verträge können abhängig von den Werten von M und N viele verschiedene Konfigurationen haben:

  • 1-von-N: Eine einzelne Partei aus der Gesamtzahl kann die Transaktion genehmigen. Diese Konfiguration entspricht einer regulären Transaktion ohne Multisig. Es kann verwendet werden, wenn der Einfachheit halber mehrere Schlüssel vorhanden sind, aber jeder von ihnen Transaktionen genehmigen kann.
  • N-von-N: Alle Parteien müssen der Transaktion zustimmen. Diese Konfiguration bietet das höchste Maß an Sicherheit, kann jedoch problematisch werden, wenn eine Partei ihren Schlüssel verliert oder die Genehmigung von Transaktionen verweigert.
  • M-von-N (wobei M < N): Eine Teilmenge aller Parteien muss die Transaktion genehmigen. Diese Konfiguration wird in der Praxis häufig verwendet, da sie Sicherheit und Flexibilität in Einklang bringt.

Multisig-Verträge in Blockchain

Im Kontext der Blockchain werden Multisig-Verträge häufig verwendet, um die Transaktionssicherheit zu verbessern, komplexe Governance-Mechanismen zu unterstützen oder eine flexible Kontrolle über Blockchain-Assets aufrechtzuerhalten. Hier sind einige Beispiele:

  • Wallets: Multisig-Wallets dienen der Sicherung von Vermögenswerten. Sie erfordern die Zustimmung mehrerer Parteien zu Transaktionen und bieten so zusätzliche Sicherheit gegen Diebstahl, externe Hacks und Insider-Bedrohungen.
  • Dezentrale autonome Organisationen (DAOs): DAOs verwenden häufig Multisig-Verträge, um ihre Governance-Regeln durchzusetzen. Abstimmungen über Vorschläge werden als Multisig-Transaktionen durchgeführt, wobei Mitglieder der DAO als Unterzeichner fungieren. Der Vorschlag wird nur umgesetzt, wenn er genügend Stimmen erhält.
  • Kettenübergreifende Operationen: Bei kettenübergreifenden Operationen können Multisig-Verträge als Verwahrer von Vermögenswerten fungieren. Wenn Vermögenswerte von einer Blockchain in eine andere verschoben werden, kann ein Multisig-Vertrag auf der Ursprungskette sicherstellen, dass die Vermögenswerte sicher gesperrt sind, bis der Vorgang auf der anderen Kette bestätigt wird.
    Während die Implementierung von Multisig-Verträgen von Blockchain zu Blockchain unterschiedlich sein kann, bleibt das Kernkonzept dasselbe – die Notwendigkeit, dass mehrere Parteien eine Transaktion genehmigen, bevor sie ausgeführt wird. Diese zusätzliche Sicherheitsebene macht Multisig-Verträge zu einem unverzichtbaren Werkzeug im Blockchain- und Krypto-Bereich.

Codierungsbeispiel: Schreiben und Bereitstellen von Multisig-Verträgen mit SmartPy

Was unsere Codebeispiele betrifft, werden wir uns drei verschiedene Multisignatur-Vertragsimplementierungen ansehen:

Der Lambda-Vertrag

Es ist sehr vielseitig und ermöglicht ein breites Einsatzspektrum. Zur Ausführung beliebiger Lambda-Funktionen sind mehrere Signaturen erforderlich.

Python 
 smartpy als sp importieren 


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

 Klasse MultisigLambda(sp.Contract): 
 """Mehrere Mitglieder stimmen für die Ausführung von Lambdas.

        Dieser Vertrag kann mit einer Adressenliste und einer Anzahl von 
 erforderlichen Stimmen zustande kommen. Jedes Mitglied kann so viele Lambdas einreichen, wie es möchte, und 
 für aktive Vorschläge stimmen. Wenn ein Lambda die erforderlichen Stimmen erreicht, wird sein Code 
 und die Ausgabeoperationen werden ausgeführt. Dadurch kann dieser Vertrag 
 alles tun, was ein Vertrag tun kann: Token übertragen, Vermögenswerte verwalten, 
 einen anderen Vertrag verwalten ...

        Wenn ein Lambda angewendet wird, werden alle bisher übermittelten Lambdas inaktiviert.
        Die Mitglieder können weiterhin neue Lambdas einreichen.
        „““ 

 def __init__(selbst, Mitglieder, erforderliche_Stimmen): 
 „““Konstruktor 

 Argumente: 
 Mitglieder (sp.set of sp.address): Personen, die 
 für Lambda einreichen und abstimmen können.
                erforderliche_Stimmen (sp.nat): Anzahl der erforderlichen Stimmen 
 „““ 
 gelten erforderliche_Stimmen <= sp.len( 
 Mitglieder 
 ), „erforderliche_Stimmen müssen <= len(Mitglieder) sein“ 
 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(members, sp.set[sp.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def subscribe_lambda(self, lambda_): 
 """Einen neuen Lambda zur Abstimmung einreichen.

            Das Einreichen eines Vorschlags bedeutet nicht, dass Sie dafür stimmen.

            Argumente: 
 lambda_(sp.lambda mit Operationen): Lambda zur Abstimmung vorgeschlagen.
            Löst aus: 
 „Sie sind kein Mitglied“ 
 „““ 
 Assert self.data.members.contains(sp.sender), „Sie sind kein Mitglied“ 
 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): 
 """Vote for a lambda.

            Argumente: 
 id(sp.nat): ID des Lambda, für das man stimmen soll.
            Erhöht: 
 „Sie sind kein Mitglied“, „Das Lambda ist inaktiv“, „Lambda nicht gefunden“ 

 Es gibt keine Gegenstimme oder Annahme. Wenn jemand mit einem Lambda 
 nicht einverstanden ist, kann er die Abstimmung vermeiden.
            „““ 
 behaupten self.data.members.contains(sp.sender), „Sie sind kein Mitglied“ 
 Assert id >= self.data.inactiveBefore, „Das Lambda ist inaktiv“ 
 affirm self.data.lambdas.contains(id), „Lambda nicht gefunden“ 
 self.data.votes[id].add(sp.sender)
            if 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): 
 """Gibt das entsprechende Lambda zurück.

            Argumente: 
 id (sp.nat): ID des abzurufenden Lambda.

            Rückgabe: 
 Paar des Lambda und ein boolescher Wert, der anzeigt, ob das Lambda aktiv ist.
            „““ 
 return (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): 
 Assert sp.sender == self.data.admin 
 self.data.value = value 


 @sp.add_test(name="MultisigLambda Basisszenario", is_default=True ) 
 def basic_scenario(): 
 """Verwenden Sie multisigLambda als Administrator eines Beispielvertrags.

    Tests: 
 – Entstehung 
 – Lambda-Einreichung 
 – Lambda-Abstimmung 
 „““ 
 sc = sp.test_scenario([main, test]) 
 sc.h1("Grundszenario.")

    member1 = sp.test_account("member1")
    member2 = sp.test_account("member2")
    member3 = sp.test_account("member3")
    Mitglieder = sp.set([Mitglied1.Adresse, member2.address, member3.address])

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

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

 sc.h2("MultisigLambda: subscribe_lambda") 

 def set_42(params): 
 administrated = sp.contract(sp.TInt, c2.address, enterpoint="set_value") 
 sp.transfer(sp. int(42), sp.tez(0), administrated.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(sender=member2)

    # Wir können überprüfen, ob der verwaltete Vertrag die Überweisung erhalten hat.
    sc.verify(c2.data.value == 42)

Der MultisigAction-Vertrag

Es führt das Konzept der Abstimmung über Vorschläge ein. In diesem Vertrag können die Unterzeichner für bestimmte Maßnahmen stimmen, und wenn ein Quorum erreicht ist, werden die vorgeschlagenen Maßnahmen ausgeführt.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 # Spezifikation des internen Verwaltungsaktionstyps 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.list[sp.address],
        changeQuorum=sp.nat,
        removeSigners=sp.list[sp.address],
    ) 

 Klasse MultisigAction(sp.Contract): 
 """Ein Vertrag, der von mehreren Unterzeichnern zur Verwaltung anderer 
 Verträge verwendet werden kann. Die verwalteten Verträge implementieren eine Schnittstelle, die es 
 ermöglicht, den Verwaltungsvorgang auch für nicht erfahrene Benutzer zu erläutern.

        Unterzeichner stimmen für Vorschläge. Ein Vorschlag ist eine Liste eines Ziels mit einer Liste von 
 Aktion. Eine Aktion ist ein einfaches Byte, soll aber einen Paketwert von 
 pro Variante haben. Dieses einfache Muster ermöglicht es, eine UX-Schnittstelle 
 zu erstellen, die den Inhalt eines Vorschlags anzeigt, oder einen solchen zu erstellen.
        „““ 

 def __init__(selbst, Quorum, Unterzeichner): 
 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, Proposal): 
 """Nur für Unterzeichner. Reichen Sie einen Vorschlag zur Abstimmung ein.

            Argumente: 
 Vorschlag (SP.Liste des SP.Datensatzes der Zieladresse und Aktion): Liste\ 
 des Ziels und zugehöriger Verwaltungsaktionen.
            „““ 
 behaupten self.data.signers.contains(sp.sender), „Nur Unterzeichner können Vorschläge machen“ 
 self.data.proposals[self.data.nextId] = Vorschlag 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Vote für einen oder mehrere Vorschläge 

 Args: 
 pId (sp.nat): ID des Vorschlags.
            „““ 
 behaupten self.data.signers.contains(sp.sender), „Nur Unterzeichner können abstimmen“ 
 affirm self.data.votes.contains(pId), „Vorschlag unbekannt“ 
 Assert pId >= self.data.inactiveBefore, „Der Vorschlag ist inaktiv“ 
 self.data.votes[pId].add(sp.sender)

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

        @sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """Inline-Funktion. Angewendete Logik, wenn ein Vorschlag genehmigt wurde.“
            Vorschlag = self.data.proposals.get(pId, default=[]) 
 für p_item im Vorschlag: 
 Contract = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer( 
 p_item.actions,
                    sp.tez(0), 
 (error="InvalidTarget"),
                ) 
 # Alle bereits eingereichten Vorschläge deaktivieren.
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def administrate(self, actions): 
 """Nur Selbstaufruf. Verwalten Sie diesen Vertrag.

            Dieser Einstiegspunkt muss über das Vorschlagssystem aufgerufen werden.

            Argumente: 
 Aktionen (sp.list of sp.bytes): Liste der gepackten Variante von \ 
 „InternalAdminAction“ („addSigners“, „changeQuorum“, „removeSigners“).
            „““ 
 Assert ( 
 sp.sender == sp.self_address() 
 ), „Dieser Einstiegspunkt muss über das Vorschlagssystem aufgerufen werden.“ 

 für gepackte_Aktionen in Aktionen: 
 Aktion = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="Ungültiges Aktionsformat" 
 ) 
 mit sp.match(action): 
 mit sp.case.changeQuorum als Quorum: 
 self.data.quorum = Quorum 
 mit sp.case.addSigners als hinzugefügt: 
 für Unterzeichner hinzugefügt: 
 self.data.signers.add(signer)
                    mit sp.case.removeSigners wie entfernt: 
 für Adresse in entfernt: 
 self.data.signers.remove(address)
                # Stellen Sie sicher, dass der Vertrag niemals mehr Quorum erfordert als die Gesamtzahl der Unterzeichner.
                behaupten self.data.quorum <= sp.len( 
 self.data.signers 
 ), „Mehr Quorum als Unterzeichner.“


wenn „Vorlagen“ nicht in __name__: 

 @sp.add_test(name="Grundszenario", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        signer2 = sp.test_account("signer2")
        signer3 = sp.test_account("signer3")

        s = sp.test_scenario(main)
        s.h1("Grundszenario") 

 s.h2("Origination") 
 c1 = main.MultisigAction( 
 quorum=2, 
 signers=sp.set([signer1.address, signer2.address]), 
 ) 
 s += c1 

 s.h2("Vorschlag zum Hinzufügen eines neuen Unterzeichners") 
 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("Unterzeichner 1 stimmt für den Vorschlag") 
 c1.vote(0).run(sender=signer1)
        s.h2("Unterzeichner 2 stimmt für den Vorschlag") 
 c1.vote(0).run(sender=signer2)

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

Der MultisigView-Vertrag

Es nutzt auch einen Abstimmungsmechanismus. Dieser Vertrag ermöglicht es Mitgliedern, beliebige Bytes einzureichen und dafür zu stimmen. Sobald ein Vorschlag die erforderliche Anzahl an Stimmen erreicht, kann sein Status über eine Ansicht bestätigt werden.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 class MultisigView(sp.Contract): 
 """Mehrere Mitglieder stimmen für beliebige Bytes.

        Dieser Vertrag kann mit einer Adressenliste und einer Anzahl von 
 erforderlichen Stimmen zustande kommen. Jedes Mitglied kann so viele Bytes einreichen, wie es möchte, und 
 für aktive Vorschläge stimmen.

        Alle Bytes, die die erforderlichen Stimmen erreicht haben, können über eine Ansicht bestätigt werden.
        „““ 

 def __init__(selbst, Mitglieder, erforderliche_Stimmen): 
 „““Konstruktor 

 Argumente: 
 Mitglieder (sp.set of sp.address): Personen, die 
 Lambda einreichen und dafür stimmen können.
                erforderliche_Stimmen (sp.nat): Anzahl der erforderlichen Stimmen 
 „““ 
 gelten erforderliche_Stimmen <= sp.len( 
 Mitglieder 
 ), „erforderliche_Stimmen müssen <= len(Mitglieder) sein“ 
 self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.bytes, 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.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def subscribe_proposal(self, bytes): 
 """Einen neuen Vorschlag zur Abstimmung einreichen.

            Das Einreichen eines Vorschlags bedeutet nicht, dass Sie dafür stimmen.

            Argumente: 
 Bytes(sp.bytes): Bytes zur Abstimmung vorgeschlagen.
            Löst aus: 
 „Sie sind kein Mitglied“ 
 „““ 
 Assert self.data.members.contains(sp.sender), „Sie sind kein Mitglied“ 
 self.data.proposals[bytes] = Falsch 
 self.data.votes[Bytes] = sp.set()

        @sp.entrypoint 
 def vote_proposal(self, bytes): 
 """Stimmen Sie für einen Vorschlag.

            Es gibt keine Gegenstimme oder Annahme. Wenn jemand mit einem Vorschlag nicht einverstanden ist 
 kann er die Abstimmung vermeiden. Achtung: Alte, nicht abgestimmte Vorschläge werden 
 obsolet.

            Argumente: 
 id(sp.bytes): Bytes des Vorschlags.
            Löst aus: 
 „Sie sind kein Mitglied“, „Vorschlag nicht gefunden“, 
 „““ 
 „asset self.data.members.contains(sp.sender),“ „Sie sind kein Mitglied“ 
 Assertion self.data.proposals.contains(bytes), „Vorschlag nicht gefunden“ 
 self.data.votes[bytes].add(sp.sender)
            if sp.len(self.data.votes[bytes]) >= self.data.required_votes:
                self.data.proposals[Bytes] = True 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """Gibt einen booleschen Wert zurück, der angibt, ob über den Vorschlag abgestimmt wurde.

            Argumente: 
 ID (sp.bytes): Bytes des Vorschlags 
 Rückgabe: 
 (sp.bool): True, wenn über den Vorschlag abgestimmt wurde, andernfalls False.
            „““ 
 return self.data.proposals.get(id, error="Vorschlag nicht gefunden") 


 wenn "Vorlagen" nicht in __name__: 

 @sp.add_test(name="MultisigView Basisszenario", is_default=True) 
 def basic_scenario(): 
 """Ein Szenario mit eine Abstimmung über den MultisigView-Vertrag.

        Tests: 
 – Entstehung 
 – Vorschlagseinreichung 
 – Vorschlagabstimmung 
 „““ 
 sc = sp.test_scenario(main)
        sc.h1("Grundszenario.")

        member1 = sp.test_account("member1")
        member2 = sp.test_account("member2")
        member3 = sp.test_account("member3")
        Mitglieder = sp.set([Mitglied1.Adresse, member2.address, member3.address])

        sc.h2("Origination") 
 c1 = main.MultisigView(members, 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) 

 # Wir können überprüfen, ob der Vorschlag validiert wurde.
        sc.verify(c1.is_voted(sp.bytes("0x42")))

Jeder Vertrag bietet einen anderen Mechanismus zur Erzielung einer Multi-Signatur-Kontrolle und bietet so Flexibilität je nach den spezifischen Anforderungen Ihres Blockchain-Anwendungsfalls.

Schritt-für-Schritt-Anleitung zum Ausprobieren des Multisig-Vertrags auf SmartPy Online

Um die von uns in SmartPy geschriebenen Multisig-Verträge auszuprobieren, können Sie die folgenden Schritte ausführen:

  1. Gehen Sie zur SmartPy-IDE unter https://smartpy.io/ide.

  2. Fügen Sie den Vertragscode in den Editor ein. Sie können den vorhandenen Code ersetzen.

  3. Um den Vertrag auszuführen, klicken Sie auf die Schaltfläche „Ausführen“ im oberen Bereich.

  4. Nachdem Sie den Vertrag ausgeführt haben, können Sie die Szenarioausführung im Bereich „Ausgabe“ auf der rechten Seite anzeigen. Hier können Sie Details zu jeder Aktion einsehen, einschließlich Vorschlägen, Abstimmungen und Genehmigungen.

  5. Um Ihren Vertrag im Tezos-Netzwerk bereitzustellen, müssen Sie ihn zunächst kompilieren. Klicken Sie im oberen Bereich auf die Schaltfläche „Kompilieren“.

  6. Nach dem Kompilieren können Sie den Vertrag im Testnetz bereitstellen, indem Sie auf „Michelson-Vertrag bereitstellen“ klicken. Sie müssen einen Geheimschlüssel für ein Tezos-Konto bereitstellen, das über ausreichend Guthaben verfügt, um die Gaskosten für die Bereitstellung zu bezahlen.

  7. Sobald der Vertrag bereitgestellt wird, erhalten Sie die Adresse des Vertrags in der Blockchain. Über diese Adresse können Sie über Transaktionen mit dem Vertrag interagieren.

  8. Um Vorschläge einzureichen oder über die Verträge abzustimmen, können Sie die im Vertragscode definierten Einstiegspunkte verwenden, z. B. submit_proposal “ oder vote_proposal. Diese können direkt aus den von Ihnen erstellten Transaktionen aufgerufen werden.

Denken Sie daran, dass Sie mit der SmartPy-IDE Ihren Vertrag zwar auf einer simulierten Blockchain testen können, für die Bereitstellung des Vertrags im tatsächlichen Tezos-Netzwerk jedoch Gaskosten anfallen, die in XTZ, der nativen Kryptowährung des Tezos-Netzwerks, bezahlt werden müssen.

ข้อจำกัดความรับผิด
* การลงทุนคริปโตมีความเสี่ยงสูง โปรดดำเนินการด้วยความระมัดระวัง หลักสูตรนี้ไม่ได้มีไว้เพื่อเป็นคำแนะนำในการลงทุน
* หลักสูตรนี้สร้างขึ้นโดยผู้เขียนที่ได้เข้าร่วม Gate Learn ความคิดเห็นของผู้เขียนไม่ได้มาจาก Gate Learn
แคตตาล็อก
บทเรียนที่ 1

Einführung und Multisig-Verträge

Multisignatur-Verträge (Multisig-Verträge), auch bekannt als „M-of-N“-Verträge, sind ein entscheidender Mechanismus zur Erhöhung der Sicherheit und Flexibilität von Transaktionen in der Blockchain-Umgebung. Diese Verträge verändern die Art und Weise, wie die Kontrolle über Vermögenswerte ausgeübt wird, indem sie die Zustimmung mehrerer Parteien erfordern, bevor Transaktionen ausgeführt werden können. Der Begriff „M-von-N“ bezieht sich auf die Anforderung, dass M von insgesamt N Parteien die Transaktion genehmigen müssen, damit sie gültig ist.

Theorie der Multisig-Verträge

Multisig-Verträge bieten eine Möglichkeit, eine gemeinsame Kontrolle über Vermögenswerte zu schaffen. Zu den typischen Anwendungsfällen gehören Treuhanddienste, Unternehmenskontoverwaltung, Mitunterzeichnung von Finanzvereinbarungen und mehr. Diese Verträge sind besonders vorteilhaft für Organisationen oder Gruppen, bei denen eine gemeinsame Entscheidungsfindung erforderlich ist.

Multisig-Verträge sind von Natur aus manipulationssicher und verhindern Single Points of Failure. Selbst wenn die Schlüssel einer Partei kompromittiert werden, kann der Angreifer keine Transaktionen ohne Zustimmung der anderen Parteien ausführen. Dies fügt eine zusätzliche Sicherheitsebene hinzu.

Multisig-Verträge können als digitales Äquivalent eines Schließfachs betrachtet werden, für dessen Öffnung mehrere Schlüssel erforderlich sind. Die Gesamtzahl der Schlüssel (N) und die Mindestanzahl der zum Öffnen der Box erforderlichen Schlüssel (M) werden bei Vertragsabschluss vereinbart.

Multisig-Verträge können abhängig von den Werten von M und N viele verschiedene Konfigurationen haben:

  • 1-von-N: Eine einzelne Partei aus der Gesamtzahl kann die Transaktion genehmigen. Diese Konfiguration entspricht einer regulären Transaktion ohne Multisig. Es kann verwendet werden, wenn der Einfachheit halber mehrere Schlüssel vorhanden sind, aber jeder von ihnen Transaktionen genehmigen kann.
  • N-von-N: Alle Parteien müssen der Transaktion zustimmen. Diese Konfiguration bietet das höchste Maß an Sicherheit, kann jedoch problematisch werden, wenn eine Partei ihren Schlüssel verliert oder die Genehmigung von Transaktionen verweigert.
  • M-von-N (wobei M < N): Eine Teilmenge aller Parteien muss die Transaktion genehmigen. Diese Konfiguration wird in der Praxis häufig verwendet, da sie Sicherheit und Flexibilität in Einklang bringt.

Multisig-Verträge in Blockchain

Im Kontext der Blockchain werden Multisig-Verträge häufig verwendet, um die Transaktionssicherheit zu verbessern, komplexe Governance-Mechanismen zu unterstützen oder eine flexible Kontrolle über Blockchain-Assets aufrechtzuerhalten. Hier sind einige Beispiele:

  • Wallets: Multisig-Wallets dienen der Sicherung von Vermögenswerten. Sie erfordern die Zustimmung mehrerer Parteien zu Transaktionen und bieten so zusätzliche Sicherheit gegen Diebstahl, externe Hacks und Insider-Bedrohungen.
  • Dezentrale autonome Organisationen (DAOs): DAOs verwenden häufig Multisig-Verträge, um ihre Governance-Regeln durchzusetzen. Abstimmungen über Vorschläge werden als Multisig-Transaktionen durchgeführt, wobei Mitglieder der DAO als Unterzeichner fungieren. Der Vorschlag wird nur umgesetzt, wenn er genügend Stimmen erhält.
  • Kettenübergreifende Operationen: Bei kettenübergreifenden Operationen können Multisig-Verträge als Verwahrer von Vermögenswerten fungieren. Wenn Vermögenswerte von einer Blockchain in eine andere verschoben werden, kann ein Multisig-Vertrag auf der Ursprungskette sicherstellen, dass die Vermögenswerte sicher gesperrt sind, bis der Vorgang auf der anderen Kette bestätigt wird.
    Während die Implementierung von Multisig-Verträgen von Blockchain zu Blockchain unterschiedlich sein kann, bleibt das Kernkonzept dasselbe – die Notwendigkeit, dass mehrere Parteien eine Transaktion genehmigen, bevor sie ausgeführt wird. Diese zusätzliche Sicherheitsebene macht Multisig-Verträge zu einem unverzichtbaren Werkzeug im Blockchain- und Krypto-Bereich.

Codierungsbeispiel: Schreiben und Bereitstellen von Multisig-Verträgen mit SmartPy

Was unsere Codebeispiele betrifft, werden wir uns drei verschiedene Multisignatur-Vertragsimplementierungen ansehen:

Der Lambda-Vertrag

Es ist sehr vielseitig und ermöglicht ein breites Einsatzspektrum. Zur Ausführung beliebiger Lambda-Funktionen sind mehrere Signaturen erforderlich.

Python 
 smartpy als sp importieren 


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

 Klasse MultisigLambda(sp.Contract): 
 """Mehrere Mitglieder stimmen für die Ausführung von Lambdas.

        Dieser Vertrag kann mit einer Adressenliste und einer Anzahl von 
 erforderlichen Stimmen zustande kommen. Jedes Mitglied kann so viele Lambdas einreichen, wie es möchte, und 
 für aktive Vorschläge stimmen. Wenn ein Lambda die erforderlichen Stimmen erreicht, wird sein Code 
 und die Ausgabeoperationen werden ausgeführt. Dadurch kann dieser Vertrag 
 alles tun, was ein Vertrag tun kann: Token übertragen, Vermögenswerte verwalten, 
 einen anderen Vertrag verwalten ...

        Wenn ein Lambda angewendet wird, werden alle bisher übermittelten Lambdas inaktiviert.
        Die Mitglieder können weiterhin neue Lambdas einreichen.
        „““ 

 def __init__(selbst, Mitglieder, erforderliche_Stimmen): 
 „““Konstruktor 

 Argumente: 
 Mitglieder (sp.set of sp.address): Personen, die 
 für Lambda einreichen und abstimmen können.
                erforderliche_Stimmen (sp.nat): Anzahl der erforderlichen Stimmen 
 „““ 
 gelten erforderliche_Stimmen <= sp.len( 
 Mitglieder 
 ), „erforderliche_Stimmen müssen <= len(Mitglieder) sein“ 
 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(members, sp.set[sp.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def subscribe_lambda(self, lambda_): 
 """Einen neuen Lambda zur Abstimmung einreichen.

            Das Einreichen eines Vorschlags bedeutet nicht, dass Sie dafür stimmen.

            Argumente: 
 lambda_(sp.lambda mit Operationen): Lambda zur Abstimmung vorgeschlagen.
            Löst aus: 
 „Sie sind kein Mitglied“ 
 „““ 
 Assert self.data.members.contains(sp.sender), „Sie sind kein Mitglied“ 
 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): 
 """Vote for a lambda.

            Argumente: 
 id(sp.nat): ID des Lambda, für das man stimmen soll.
            Erhöht: 
 „Sie sind kein Mitglied“, „Das Lambda ist inaktiv“, „Lambda nicht gefunden“ 

 Es gibt keine Gegenstimme oder Annahme. Wenn jemand mit einem Lambda 
 nicht einverstanden ist, kann er die Abstimmung vermeiden.
            „““ 
 behaupten self.data.members.contains(sp.sender), „Sie sind kein Mitglied“ 
 Assert id >= self.data.inactiveBefore, „Das Lambda ist inaktiv“ 
 affirm self.data.lambdas.contains(id), „Lambda nicht gefunden“ 
 self.data.votes[id].add(sp.sender)
            if 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): 
 """Gibt das entsprechende Lambda zurück.

            Argumente: 
 id (sp.nat): ID des abzurufenden Lambda.

            Rückgabe: 
 Paar des Lambda und ein boolescher Wert, der anzeigt, ob das Lambda aktiv ist.
            „““ 
 return (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): 
 Assert sp.sender == self.data.admin 
 self.data.value = value 


 @sp.add_test(name="MultisigLambda Basisszenario", is_default=True ) 
 def basic_scenario(): 
 """Verwenden Sie multisigLambda als Administrator eines Beispielvertrags.

    Tests: 
 – Entstehung 
 – Lambda-Einreichung 
 – Lambda-Abstimmung 
 „““ 
 sc = sp.test_scenario([main, test]) 
 sc.h1("Grundszenario.")

    member1 = sp.test_account("member1")
    member2 = sp.test_account("member2")
    member3 = sp.test_account("member3")
    Mitglieder = sp.set([Mitglied1.Adresse, member2.address, member3.address])

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

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

 sc.h2("MultisigLambda: subscribe_lambda") 

 def set_42(params): 
 administrated = sp.contract(sp.TInt, c2.address, enterpoint="set_value") 
 sp.transfer(sp. int(42), sp.tez(0), administrated.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(sender=member2)

    # Wir können überprüfen, ob der verwaltete Vertrag die Überweisung erhalten hat.
    sc.verify(c2.data.value == 42)

Der MultisigAction-Vertrag

Es führt das Konzept der Abstimmung über Vorschläge ein. In diesem Vertrag können die Unterzeichner für bestimmte Maßnahmen stimmen, und wenn ein Quorum erreicht ist, werden die vorgeschlagenen Maßnahmen ausgeführt.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 # Spezifikation des internen Verwaltungsaktionstyps 
 InternalAdminAction: type = sp.variant(
        addSigners=sp.list[sp.address],
        changeQuorum=sp.nat,
        removeSigners=sp.list[sp.address],
    ) 

 Klasse MultisigAction(sp.Contract): 
 """Ein Vertrag, der von mehreren Unterzeichnern zur Verwaltung anderer 
 Verträge verwendet werden kann. Die verwalteten Verträge implementieren eine Schnittstelle, die es 
 ermöglicht, den Verwaltungsvorgang auch für nicht erfahrene Benutzer zu erläutern.

        Unterzeichner stimmen für Vorschläge. Ein Vorschlag ist eine Liste eines Ziels mit einer Liste von 
 Aktion. Eine Aktion ist ein einfaches Byte, soll aber einen Paketwert von 
 pro Variante haben. Dieses einfache Muster ermöglicht es, eine UX-Schnittstelle 
 zu erstellen, die den Inhalt eines Vorschlags anzeigt, oder einen solchen zu erstellen.
        „““ 

 def __init__(selbst, Quorum, Unterzeichner): 
 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, Proposal): 
 """Nur für Unterzeichner. Reichen Sie einen Vorschlag zur Abstimmung ein.

            Argumente: 
 Vorschlag (SP.Liste des SP.Datensatzes der Zieladresse und Aktion): Liste\ 
 des Ziels und zugehöriger Verwaltungsaktionen.
            „““ 
 behaupten self.data.signers.contains(sp.sender), „Nur Unterzeichner können Vorschläge machen“ 
 self.data.proposals[self.data.nextId] = Vorschlag 
 self.data.votes[self.data.nextId] = sp.set()
            self.data.nextId += 1 

 @sp.entrypoint 
 def vote(self, pId): 
 """Vote für einen oder mehrere Vorschläge 

 Args: 
 pId (sp.nat): ID des Vorschlags.
            „““ 
 behaupten self.data.signers.contains(sp.sender), „Nur Unterzeichner können abstimmen“ 
 affirm self.data.votes.contains(pId), „Vorschlag unbekannt“ 
 Assert pId >= self.data.inactiveBefore, „Der Vorschlag ist inaktiv“ 
 self.data.votes[pId].add(sp.sender)

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

        @sp.private(with_storage="read-write", with_operations=True) 
 def _onApproved(self, pId): 
 """Inline-Funktion. Angewendete Logik, wenn ein Vorschlag genehmigt wurde.“
            Vorschlag = self.data.proposals.get(pId, default=[]) 
 für p_item im Vorschlag: 
 Contract = sp.contract(sp.list[sp.bytes], p_item.target)
                sp.transfer( 
 p_item.actions,
                    sp.tez(0), 
 (error="InvalidTarget"),
                ) 
 # Alle bereits eingereichten Vorschläge deaktivieren.
            self.data.inactiveBefore = self.data.nextId 

 @sp.entrypoint 
 def administrate(self, actions): 
 """Nur Selbstaufruf. Verwalten Sie diesen Vertrag.

            Dieser Einstiegspunkt muss über das Vorschlagssystem aufgerufen werden.

            Argumente: 
 Aktionen (sp.list of sp.bytes): Liste der gepackten Variante von \ 
 „InternalAdminAction“ („addSigners“, „changeQuorum“, „removeSigners“).
            „““ 
 Assert ( 
 sp.sender == sp.self_address() 
 ), „Dieser Einstiegspunkt muss über das Vorschlagssystem aufgerufen werden.“ 

 für gepackte_Aktionen in Aktionen: 
 Aktion = sp.unpack(packed_actions, InternalAdminAction).unwrap_some( 
 error="Ungültiges Aktionsformat" 
 ) 
 mit sp.match(action): 
 mit sp.case.changeQuorum als Quorum: 
 self.data.quorum = Quorum 
 mit sp.case.addSigners als hinzugefügt: 
 für Unterzeichner hinzugefügt: 
 self.data.signers.add(signer)
                    mit sp.case.removeSigners wie entfernt: 
 für Adresse in entfernt: 
 self.data.signers.remove(address)
                # Stellen Sie sicher, dass der Vertrag niemals mehr Quorum erfordert als die Gesamtzahl der Unterzeichner.
                behaupten self.data.quorum <= sp.len( 
 self.data.signers 
 ), „Mehr Quorum als Unterzeichner.“


wenn „Vorlagen“ nicht in __name__: 

 @sp.add_test(name="Grundszenario", is_default=True) 
 def test(): 
 signer1 = sp.test_account("signer1")
        signer2 = sp.test_account("signer2")
        signer3 = sp.test_account("signer3")

        s = sp.test_scenario(main)
        s.h1("Grundszenario") 

 s.h2("Origination") 
 c1 = main.MultisigAction( 
 quorum=2, 
 signers=sp.set([signer1.address, signer2.address]), 
 ) 
 s += c1 

 s.h2("Vorschlag zum Hinzufügen eines neuen Unterzeichners") 
 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("Unterzeichner 1 stimmt für den Vorschlag") 
 c1.vote(0).run(sender=signer1)
        s.h2("Unterzeichner 2 stimmt für den Vorschlag") 
 c1.vote(0).run(sender=signer2)

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

Der MultisigView-Vertrag

Es nutzt auch einen Abstimmungsmechanismus. Dieser Vertrag ermöglicht es Mitgliedern, beliebige Bytes einzureichen und dafür zu stimmen. Sobald ein Vorschlag die erforderliche Anzahl an Stimmen erreicht, kann sein Status über eine Ansicht bestätigt werden.

Python 
 import smartpy as sp 


 @sp.module 
 def main(): 
 class MultisigView(sp.Contract): 
 """Mehrere Mitglieder stimmen für beliebige Bytes.

        Dieser Vertrag kann mit einer Adressenliste und einer Anzahl von 
 erforderlichen Stimmen zustande kommen. Jedes Mitglied kann so viele Bytes einreichen, wie es möchte, und 
 für aktive Vorschläge stimmen.

        Alle Bytes, die die erforderlichen Stimmen erreicht haben, können über eine Ansicht bestätigt werden.
        „““ 

 def __init__(selbst, Mitglieder, erforderliche_Stimmen): 
 „““Konstruktor 

 Argumente: 
 Mitglieder (sp.set of sp.address): Personen, die 
 Lambda einreichen und dafür stimmen können.
                erforderliche_Stimmen (sp.nat): Anzahl der erforderlichen Stimmen 
 „““ 
 gelten erforderliche_Stimmen <= sp.len( 
 Mitglieder 
 ), „erforderliche_Stimmen müssen <= len(Mitglieder) sein“ 
 self.data.proposals = sp.cast (sp.big_map(), sp.big_map[sp.bytes, 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.address])
            self.data.required_votes = sp.cast(required_votes, sp.nat) 

 @sp.entrypoint 
 def subscribe_proposal(self, bytes): 
 """Einen neuen Vorschlag zur Abstimmung einreichen.

            Das Einreichen eines Vorschlags bedeutet nicht, dass Sie dafür stimmen.

            Argumente: 
 Bytes(sp.bytes): Bytes zur Abstimmung vorgeschlagen.
            Löst aus: 
 „Sie sind kein Mitglied“ 
 „““ 
 Assert self.data.members.contains(sp.sender), „Sie sind kein Mitglied“ 
 self.data.proposals[bytes] = Falsch 
 self.data.votes[Bytes] = sp.set()

        @sp.entrypoint 
 def vote_proposal(self, bytes): 
 """Stimmen Sie für einen Vorschlag.

            Es gibt keine Gegenstimme oder Annahme. Wenn jemand mit einem Vorschlag nicht einverstanden ist 
 kann er die Abstimmung vermeiden. Achtung: Alte, nicht abgestimmte Vorschläge werden 
 obsolet.

            Argumente: 
 id(sp.bytes): Bytes des Vorschlags.
            Löst aus: 
 „Sie sind kein Mitglied“, „Vorschlag nicht gefunden“, 
 „““ 
 „asset self.data.members.contains(sp.sender),“ „Sie sind kein Mitglied“ 
 Assertion self.data.proposals.contains(bytes), „Vorschlag nicht gefunden“ 
 self.data.votes[bytes].add(sp.sender)
            if sp.len(self.data.votes[bytes]) >= self.data.required_votes:
                self.data.proposals[Bytes] = True 

 @sp.onchain_view() 
 def is_voted(self, id): 
 """Gibt einen booleschen Wert zurück, der angibt, ob über den Vorschlag abgestimmt wurde.

            Argumente: 
 ID (sp.bytes): Bytes des Vorschlags 
 Rückgabe: 
 (sp.bool): True, wenn über den Vorschlag abgestimmt wurde, andernfalls False.
            „““ 
 return self.data.proposals.get(id, error="Vorschlag nicht gefunden") 


 wenn "Vorlagen" nicht in __name__: 

 @sp.add_test(name="MultisigView Basisszenario", is_default=True) 
 def basic_scenario(): 
 """Ein Szenario mit eine Abstimmung über den MultisigView-Vertrag.

        Tests: 
 – Entstehung 
 – Vorschlagseinreichung 
 – Vorschlagabstimmung 
 „““ 
 sc = sp.test_scenario(main)
        sc.h1("Grundszenario.")

        member1 = sp.test_account("member1")
        member2 = sp.test_account("member2")
        member3 = sp.test_account("member3")
        Mitglieder = sp.set([Mitglied1.Adresse, member2.address, member3.address])

        sc.h2("Origination") 
 c1 = main.MultisigView(members, 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) 

 # Wir können überprüfen, ob der Vorschlag validiert wurde.
        sc.verify(c1.is_voted(sp.bytes("0x42")))

Jeder Vertrag bietet einen anderen Mechanismus zur Erzielung einer Multi-Signatur-Kontrolle und bietet so Flexibilität je nach den spezifischen Anforderungen Ihres Blockchain-Anwendungsfalls.

Schritt-für-Schritt-Anleitung zum Ausprobieren des Multisig-Vertrags auf SmartPy Online

Um die von uns in SmartPy geschriebenen Multisig-Verträge auszuprobieren, können Sie die folgenden Schritte ausführen:

  1. Gehen Sie zur SmartPy-IDE unter https://smartpy.io/ide.

  2. Fügen Sie den Vertragscode in den Editor ein. Sie können den vorhandenen Code ersetzen.

  3. Um den Vertrag auszuführen, klicken Sie auf die Schaltfläche „Ausführen“ im oberen Bereich.

  4. Nachdem Sie den Vertrag ausgeführt haben, können Sie die Szenarioausführung im Bereich „Ausgabe“ auf der rechten Seite anzeigen. Hier können Sie Details zu jeder Aktion einsehen, einschließlich Vorschlägen, Abstimmungen und Genehmigungen.

  5. Um Ihren Vertrag im Tezos-Netzwerk bereitzustellen, müssen Sie ihn zunächst kompilieren. Klicken Sie im oberen Bereich auf die Schaltfläche „Kompilieren“.

  6. Nach dem Kompilieren können Sie den Vertrag im Testnetz bereitstellen, indem Sie auf „Michelson-Vertrag bereitstellen“ klicken. Sie müssen einen Geheimschlüssel für ein Tezos-Konto bereitstellen, das über ausreichend Guthaben verfügt, um die Gaskosten für die Bereitstellung zu bezahlen.

  7. Sobald der Vertrag bereitgestellt wird, erhalten Sie die Adresse des Vertrags in der Blockchain. Über diese Adresse können Sie über Transaktionen mit dem Vertrag interagieren.

  8. Um Vorschläge einzureichen oder über die Verträge abzustimmen, können Sie die im Vertragscode definierten Einstiegspunkte verwenden, z. B. submit_proposal “ oder vote_proposal. Diese können direkt aus den von Ihnen erstellten Transaktionen aufgerufen werden.

Denken Sie daran, dass Sie mit der SmartPy-IDE Ihren Vertrag zwar auf einer simulierten Blockchain testen können, für die Bereitstellung des Vertrags im tatsächlichen Tezos-Netzwerk jedoch Gaskosten anfallen, die in XTZ, der nativen Kryptowährung des Tezos-Netzwerks, bezahlt werden müssen.

ข้อจำกัดความรับผิด
* การลงทุนคริปโตมีความเสี่ยงสูง โปรดดำเนินการด้วยความระมัดระวัง หลักสูตรนี้ไม่ได้มีไว้เพื่อเป็นคำแนะนำในการลงทุน
* หลักสูตรนี้สร้างขึ้นโดยผู้เขียนที่ได้เข้าร่วม Gate Learn ความคิดเห็นของผู้เขียนไม่ได้มาจาก Gate Learn