レッスン1

イントロダクションとマルチシグコントラクト

マルチシグ(マルチシグ)コントラクトは、「M-of-N」コントラクトとも呼ばれ、ブロックチェーン環境におけるトランザクションのセキュリティと柔軟性を高めるために使用される重要なメカニズムです。 これらの契約は、取引を実行する前に複数の当事者からの承認を必要とすることにより、資産に対する管理の方法を変更します。 「M-of-N」という用語は、取引が有効であるためには、合計N人の当事者のうちM人が取引を承認する必要があるという要件を指します。

マルチシグ契約の理論

マルチシグコントラクトは、アセットに対する共有制御を作成する手段を提供します。 一般的なユースケースには、エスクローサービス、法人口座管理、金融契約の共同署名などが含まれます。 これらの契約は、集団的な意思決定が必要な組織やグループにとって非常に有益です。

設計上、マルチシグコントラクトは改ざん防止機能を備えており、単一障害点を防止します。 一方の当事者の鍵が侵害されたとしても、攻撃者は他方の当事者の承認なしにトランザクションを実行することはできません。 これにより、セキュリティのレイヤーが追加されます。

マルチシグ契約は、複数の鍵を開ける必要がある貸金庫のデジタル版と考えることができます。 鍵の総数(N)と箱を開けるのに必要な最小鍵数(M)は、契約の作成時に合意されます。

マルチシグコントラクトは、M と N の値に応じて、さまざまな設定を持つことができます。

  • 1-of-N: 合計のうちの 1 つの当事者がトランザクションを承認できます。 この構成は、マルチシグを使用しない通常のトランザクションと同じです。 便宜上、複数のキーが存在するが、そのうちの 1 つでトランザクションを承認できる場合に使用される場合があります。
  • N-of-N: すべての関係者がトランザクションを承認する必要があります。 この構成は最高レベルのセキュリティを提供しますが、パーティがキーを紛失したり、トランザクションの承認を拒否したりした場合に問題になる可能性があります。
  • M-of-N (M < N): 当事者全体のサブセットがトランザクションを承認する必要があります。 この構成は、セキュリティと柔軟性のバランスが取れているため、実際にはよく使用されます。

ブロックチェーンにおけるマルチシグ契約

ブロックチェーンの文脈では、マルチシグコントラクトは、トランザクションのセキュリティを強化したり、複雑なガバナンスメカニズムをサポートしたり、ブロックチェーン資産の柔軟な制御を維持したりするために広く使用されています。 次に例をいくつか示します。

  • ウォレット: マルチシグウォレットは、資産を保護するために使用されます。 複数の当事者が取引を承認する必要があるため、盗難、外部からのハッキング、インサイダーの脅威に対するセキュリティが強化されます。
  • 分散型自律組織(DAO):分散型自律組織(DAO)は、ガバナンスルールを実施するためにマルチシグコントラクトを使用することがよくあります。 提案に対する投票はマルチシグトランザクションとして実施され、DAOのメンバーが署名者として行動します。 提案は、十分な票を獲得した場合にのみ実行されます。
  • クロスチェーン操作:クロスチェーン操作では、マルチシグコントラクトは資産のカストディアンとして機能することができます。 資産が1つのブロックチェーンから別のブロックチェーンに移動する場合、元のチェーンのマルチシグコントラクトは、他のチェーンでの操作が確認されるまで資産が安全にロックされることを保証します。
    マルチシグコントラクトの実装はブロックチェーンごとに異なりますが、トランザクションが実行される前に複数の当事者が承認する必要があるというコアコンセプトは同じです。 このセキュリティの追加レイヤーにより、マルチシグコントラクトはブロックチェーンと暗号空間に不可欠なツールとなっています。

コーディング例: SmartPy によるマルチシグ コントラクトの記述とデプロイ

コード例では、3つの異なるマルチシグコントラクトの実装を見ていきます。

Lambdaコントラクト

非常に用途が広く、幅広い用途に使用できます。 任意のラムダ関数を実行するには、複数のシグネチャが必要です。

ニシキヘビ
SmartPy を SP としてインポートする

@sp.モジュール
デフmain():
    operation_lambda: type = sp.lambda_(sp.unit, sp.unit, with_operations=True)

class MultisigLambda(sp.契約):
        """複数のメンバーがラムダの実行に投票します。        このコントラクトは、アドレスのリストといくつかの
        必要な投票数。 どのメンバーも、好きなだけラムダを提出し、投票することができます
        アクティブなプロポーザルの場合。 ラムダが必要な投票数に達すると、そのコードは
        呼び出され、出力操作が実行されます。 これにより、このコントラクトは
        コントラクトでできることは何でもする:トークンの転送、資産の管理、
        別の契約を管理しています...        ラムダが適用されると、これまでに送信されたすべてのラムダが無効になります。        メンバーは引き続き新しいラムダを送信できます。        """

デフ __init__(自己、メンバー、required_votes):
            """コンストラクタ

引数:
                メンバー (sp.address の sp.set): 投稿と投票ができる人
                    ラムダ用。                required_votes (sp.nat): 必要な投票数
            """
            assert 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.エントリポイント
        def submit_lambda(self, lambda_):
            """新しいラムダを投票に提出します。            提案を提出しても、賛成票を投じるわけではありません。            引数:
                lambda_(sp.lambda with operations): lambda は投票を提案しました。            発生 させます:
                「あなたはメンバーではありません」
            """
            self.data.members.contains(sp.sender)をアサートし、 「あなたはメンバーではありません」
            self.data.lambdas[self.data.nextId] (英語) = lambda_
            self.data.votes[self.data.nextId] (self.data.次の Id) = sp.set()            self.data.nextId += 1

@sp.エントリポイント
        def vote_lambda(self, id):
            """ラムダに投票します。            引数:
                id(sp.nat): 投票するラムダの ID。            発生 させます:
                「あなたはメンバーではありません」、「ラムダは非アクティブです」、「ラムダが見つかりません」

反対票も可決もありません。 ラムダに意見が合わない人がいる場合
            彼らは投票を避けることができます。            """
            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):
            """対応するラムダを返します。            引数:
                id (sp.nat): 取得するラムダの ID。            帰る:
                ラムダのペアと、ラムダがアクティブかどうかを示すブール値。            """
            戻り値(self.data.lambdas[id]、 id >= self.data.inactiveBefore)#「テンプレート」が __name__にない場合:

@sp.モジュール
def test()を使用します。
    class Administrated(sp.契約):
        デフ __init__(自己、管理者):
            self.data.admin =管理者
            self.data.value = sp.int(0)        @sp.エントリポイント
        def set_value(self, value):
            sp.sender == self.data.adminをアサートします
            self.data.value =値

@sp.add_test(name="マルチシグラムダ基本シナリオ", is_default=True)
def basic_scenario():
    """マルチシグラムダをサンプルコントラクトの管理者として使用します。    テスト:
    -創始
    - Lambdaの提出
    - ラムダ投票
    """
    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, member2.address、 member3.address])    sc.h2("MultisigLambda: オリジネーション")
    C1 = メイン。MultisigLambda(メンバー, 2)
    sc + = c1の

sc.h2("管理: オリジネーション")
    C2 = テスト。管理者(c1.address)    sc += c2

sc.h2("マルチシグラムダ: submit_lambda")

def set_42(params):
        administrated = sp.contract(sp.TN、c2.address、entrypoint="set_value")
        sp.transfer(sp.int(42), sp.tez(0), administrated.open_some())    lambda_ = sp.build_lambda(set_42、 with_operations=真)
    c1.submit_lambda(lambda_).run(sender=member1)

sc.h2("マルチシグラムダ:vote_lambda")
    c1.vote_lambda(0).run(送信者=メンバー1)    c1.vote_lambda(0).run(送信者=メンバー2)    # 管理された契約が転送を受け取ったことを確認できます。    sc.verify(c2.data.value == 42)

マルチシグアクション契約

提案への投票の概念を紹介します。 この契約では、署名者は特定のアクションに投票でき、定足数に達すると、提案されたアクションが実行されます。

ニシキヘビ
SmartPy を SP としてインポートする

@sp.モジュール
デフmain():
    # 内部管理アクションタイプの指定
    InternalAdminAction: type = sp.variant(        addSigners=sp.list[sp.address]、        changeQuorum=sp.natの場合、        removeSigners=sp.list[sp.address]、    )

class MultisigAction(sp.契約):
        """複数の署名者が他の
        契約。 管理コントラクトは、それを実現するインターフェイスを実装します
        専門家以外のユーザーに管理プロセスを明示できます。        署名者は提案に投票します。 プロポーザルは、ターゲットのリストであり、
        アクション。 アクションは単純なバイトですが、
        バリアント。 この単純なパターンにより、UX インターフェイスを構築できます
        これは、提案またはビルドの内容を示します。        """

def __init__(self, quorum, signers):
            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.エントリポイント
        def send_proposal(self, proposal):
            """署名者専用です。 投票に提案を提出します。            引数:
                プロポーザル (ターゲットアドレスとアクションの sp.record の sp.list リスト): リスト\
                    ターゲットおよび関連する管理アクションの。            """
            self.data.signers.contains(sp.sender)をアサートします。 「署名者だけがプロポーズできる」
            self.data.proposals[self.data.nextId] (英語) = プロポーザル
            self.data.votes[self.data.nextId] (self.data.次の Id) = sp.set()            self.data.nextId += 1

@sp.エントリポイント
        デフ投票(自己、pId):
            """1 つ以上の提案に投票する

引数:
                pId (sp.nat): プロポーザルの ID。            """
            self.data.signers.contains(sp.sender)をアサートします。 「署名者のみが投票できる」
            assert self.data.votes.contains(pId), 「提案が不明」
            assert 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="読み取り/書き込み", with_operations=True)
        def _onApproved(self, pId):
            """インライン関数。 提案が承認されたときに適用されるロジック。            proposal = self.data.proposals.get(pId, デフォルト=[])
            提案のp_itemの場合:
                コントラクト = sp.contract(sp.list[sp.bytes], p_item.ターゲット)                sp.transfer(
                    p_item.アクション、                    sp.tez(0)、
                    contract.unwrap_some(error="InvalidTarget")、                )
            # すでに提出されているすべてのプロポーザルを無効化します。            self.data.inactiveBefore (英語) = self.data.nextId

@sp.エントリポイント
        def administrate(self, actions):
            """セルフコールのみ。 この契約を管理します。            このエントリポイントは、プロポーザルシステムを介して呼び出す必要があります。            引数:
                actions (sp.list of sp.bytes): \ のパックされたバリアントのリスト
                    'InternalAdminAction' ('addSigners', 'changeQuorum', 'removeSigners')。            """
            アサート (
                sp.sender == sp.self_address()
            )、「このエントリポイントは、プロポーザルシステムを通じて呼び出す必要があります。」

packed_actions in アクションの場合:
                アクション= 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="基本シナリオ", is_default=True)
    def test()を使用します。
        signer1 = sp.test_account("signer1")        signer2 = sp.test_account("signer2")        signer3 = sp.test_account("signer3")        s = sp.test_scenario(メイン)        s.h1(基本シナリオ)

s.h2("オリジネーション")
        C1 = メイン。マルチシグアクション(
            クォーラム=2、
            signers=sp.set([signer1.address, signer2.address])、
        )
        s += c1

s.h2("新しい署名者を追加するための提案")
        ターゲット = 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]), メイン。内部管理アクション
            )
        )
        c1.send_proposal([sp.record(target=target, actions=[action])]).run(
            送信者=署名者1
        )

s.h2("署名者 1 が提案に賛成)
        c1.vote(0).run(送信者=署名者1)        s.h2("署名者 2 が提案に賛成)
        c1.vote(0).run(送信者=署名者2)        s.verify(c1.data.signers.contains(signer3.address))

MultisigViewコントラクト

また、投票メカニズムも利用しています。 このコントラクトにより、メンバーは任意のバイトを送信して投票できます。 プロポーザルが必要な投票数に達すると、そのステータスをビューで確認できます。

ニシキヘビ
SmartPy を SP としてインポートする

@sp.モジュール
デフmain():
    class MultisigView(sp.契約):
        """複数のメンバーが任意のバイトに投票します。        このコントラクトは、アドレスのリストといくつかの
        必要な投票数。 どのメンバーも、必要な数のバイトを送信して投票できます
        アクティブなプロポーザルの場合。        必要な投票数に達したバイトは、ビューで確認できます。        """

デフ __init__(自己、メンバー、required_votes):
            """コンストラクタ

引数:
                メンバー(sp.addressのsp.set):投稿・投票できる人
                    ラムダ。                required_votes (sp.nat): 必要な投票数
            """
            assert required_votes <= sp.len(
                メンバーズ
            ), "required_votes は <= len(members)" でなければなりません。
            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(メンバー、 sp.set[sp.address])            self.data.required_votes = sp.cast(required_votes、 sp.natなど)

@sp.エントリポイント
        def submit_proposal(self, bytes):
            """新しい提案を投票に提出します。            提案を提出しても、賛成票を投じるわけではありません。            引数:
                bytes(sp.bytes): BYTESは投票を提案しました。            発生 させます:
                「あなたはメンバーではありません」
            """
            self.data.members.contains(sp.sender)をアサートし、 「あなたはメンバーではありません」
            self.data.proposals[バイト] = 誤り
            self.data.votes[バイト] = sp.set()        @sp.エントリポイント
        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[bytes])の場合 >= self.data.required_votes:                self.data.proposals[バイト] = 真

@sp.onchain_view()を呼び出します。
        def is_voted(self, id):
            """提案が投票されたかどうかを示すブール値を返します。            引数:
                id (sp.bytes): プロポーザルのバイト数
            帰る:
                (sp.bool): 提案が投票された場合は true、そうでない場合は False。            """
            self.data.proposals.get(id, error="プロポーザルが見つかりません")

「テンプレート」が __name__にない場合:

@sp.add_test(name="マルチシグビューの基本シナリオ", is_default=True)
    def basic_scenario():
        """multisigViewコントラクトに投票するシナリオ。        テスト:
        -創始
        - 提案書の提出
        - 提案の投票
        """
        sc = sp.test_scenario(メイン)        sc.h1("基本シナリオ")        member1 = sp.test_account("member1")        member2 = sp.test_account("member2")        member3 = sp.test_account("member3")        メンバー= sp.set([member1.address, member2.address、 member3.address])        sc.h2("オリジネーション")
        C1 = メイン。MultisigView(メンバー, 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)

# プロポーザルが検証されたことを確認できます。        sc.verify(c1.is_voted(sp.bytes("0x42")))

各コントラクトは、マルチシグレーション制御を実現するための異なるメカニズムを提供し、ブロックチェーンのユースケースの特定のニーズに応じて柔軟性を提供します。

SmartPy Online でマルチシグコントラクトを試すためのステップバイステップガイド

SmartPy で記述したマルチシグコントラクトを試すには、以下の手順に従ってください。

  1. https://smartpy.io/ide の SmartPy IDE に移動します。

  2. コントラクトコードをエディタに貼り付けます。 既存のコードを置き換えることができます。

  3. 契約を実行するには、トップパネルにある「実行」ボタンをクリックします。

  4. コントラクトの実行後、右側の「出力」パネルでシナリオの実行を確認できます。 ここでは、提案、投票、承認など、各アクションの詳細を確認できます。

  5. Tezosネットワークにコントラクトをデプロイするには、まずコントラクトをコンパイルする必要があります。 トップパネルの「コンパイル」ボタンをクリックします。

  6. コンパイル後、「Deploy Michelson Contract」をクリックして、コントラクトをテストネットにデプロイできます。 Tezosアカウントには、デプロイのガス代を支払うのに十分な資金があるシークレットキーを提供する必要があります。

  7. コントラクトがデプロイされると、ブロックチェーン上のコントラクトのアドレスが提供されます。 このアドレスを使用して、トランザクションを介して契約とやり取りすることができます。

  8. 契約で提案を送信したり、投票したりするには、契約コードで定義されているエントリポイント ( や vote_proposalなど) submit_proposal を使用できます。これらは、作成したトランザクションから直接呼び出すことができます。

SmartPy IDEでは、シミュレートされたブロックチェーン上でコントラクトをテストできますが、実際のTezosネットワークにコントラクトを展開するとガス代が発生し、Tezosネットワークのネイティブ暗号通貨であるXTZで支払う必要があることを覚えておいてください。

免責事項
* 暗号資産投資には重大なリスクが伴います。注意して進めてください。このコースは投資アドバイスを目的としたものではありません。
※ このコースはGate Learnに参加しているメンバーが作成したものです。作成者が共有した意見はGate Learnを代表するものではありません。
カタログ
レッスン1

イントロダクションとマルチシグコントラクト

マルチシグ(マルチシグ)コントラクトは、「M-of-N」コントラクトとも呼ばれ、ブロックチェーン環境におけるトランザクションのセキュリティと柔軟性を高めるために使用される重要なメカニズムです。 これらの契約は、取引を実行する前に複数の当事者からの承認を必要とすることにより、資産に対する管理の方法を変更します。 「M-of-N」という用語は、取引が有効であるためには、合計N人の当事者のうちM人が取引を承認する必要があるという要件を指します。

マルチシグ契約の理論

マルチシグコントラクトは、アセットに対する共有制御を作成する手段を提供します。 一般的なユースケースには、エスクローサービス、法人口座管理、金融契約の共同署名などが含まれます。 これらの契約は、集団的な意思決定が必要な組織やグループにとって非常に有益です。

設計上、マルチシグコントラクトは改ざん防止機能を備えており、単一障害点を防止します。 一方の当事者の鍵が侵害されたとしても、攻撃者は他方の当事者の承認なしにトランザクションを実行することはできません。 これにより、セキュリティのレイヤーが追加されます。

マルチシグ契約は、複数の鍵を開ける必要がある貸金庫のデジタル版と考えることができます。 鍵の総数(N)と箱を開けるのに必要な最小鍵数(M)は、契約の作成時に合意されます。

マルチシグコントラクトは、M と N の値に応じて、さまざまな設定を持つことができます。

  • 1-of-N: 合計のうちの 1 つの当事者がトランザクションを承認できます。 この構成は、マルチシグを使用しない通常のトランザクションと同じです。 便宜上、複数のキーが存在するが、そのうちの 1 つでトランザクションを承認できる場合に使用される場合があります。
  • N-of-N: すべての関係者がトランザクションを承認する必要があります。 この構成は最高レベルのセキュリティを提供しますが、パーティがキーを紛失したり、トランザクションの承認を拒否したりした場合に問題になる可能性があります。
  • M-of-N (M < N): 当事者全体のサブセットがトランザクションを承認する必要があります。 この構成は、セキュリティと柔軟性のバランスが取れているため、実際にはよく使用されます。

ブロックチェーンにおけるマルチシグ契約

ブロックチェーンの文脈では、マルチシグコントラクトは、トランザクションのセキュリティを強化したり、複雑なガバナンスメカニズムをサポートしたり、ブロックチェーン資産の柔軟な制御を維持したりするために広く使用されています。 次に例をいくつか示します。

  • ウォレット: マルチシグウォレットは、資産を保護するために使用されます。 複数の当事者が取引を承認する必要があるため、盗難、外部からのハッキング、インサイダーの脅威に対するセキュリティが強化されます。
  • 分散型自律組織(DAO):分散型自律組織(DAO)は、ガバナンスルールを実施するためにマルチシグコントラクトを使用することがよくあります。 提案に対する投票はマルチシグトランザクションとして実施され、DAOのメンバーが署名者として行動します。 提案は、十分な票を獲得した場合にのみ実行されます。
  • クロスチェーン操作:クロスチェーン操作では、マルチシグコントラクトは資産のカストディアンとして機能することができます。 資産が1つのブロックチェーンから別のブロックチェーンに移動する場合、元のチェーンのマルチシグコントラクトは、他のチェーンでの操作が確認されるまで資産が安全にロックされることを保証します。
    マルチシグコントラクトの実装はブロックチェーンごとに異なりますが、トランザクションが実行される前に複数の当事者が承認する必要があるというコアコンセプトは同じです。 このセキュリティの追加レイヤーにより、マルチシグコントラクトはブロックチェーンと暗号空間に不可欠なツールとなっています。

コーディング例: SmartPy によるマルチシグ コントラクトの記述とデプロイ

コード例では、3つの異なるマルチシグコントラクトの実装を見ていきます。

Lambdaコントラクト

非常に用途が広く、幅広い用途に使用できます。 任意のラムダ関数を実行するには、複数のシグネチャが必要です。

ニシキヘビ
SmartPy を SP としてインポートする

@sp.モジュール
デフmain():
    operation_lambda: type = sp.lambda_(sp.unit, sp.unit, with_operations=True)

class MultisigLambda(sp.契約):
        """複数のメンバーがラムダの実行に投票します。        このコントラクトは、アドレスのリストといくつかの
        必要な投票数。 どのメンバーも、好きなだけラムダを提出し、投票することができます
        アクティブなプロポーザルの場合。 ラムダが必要な投票数に達すると、そのコードは
        呼び出され、出力操作が実行されます。 これにより、このコントラクトは
        コントラクトでできることは何でもする:トークンの転送、資産の管理、
        別の契約を管理しています...        ラムダが適用されると、これまでに送信されたすべてのラムダが無効になります。        メンバーは引き続き新しいラムダを送信できます。        """

デフ __init__(自己、メンバー、required_votes):
            """コンストラクタ

引数:
                メンバー (sp.address の sp.set): 投稿と投票ができる人
                    ラムダ用。                required_votes (sp.nat): 必要な投票数
            """
            assert 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.エントリポイント
        def submit_lambda(self, lambda_):
            """新しいラムダを投票に提出します。            提案を提出しても、賛成票を投じるわけではありません。            引数:
                lambda_(sp.lambda with operations): lambda は投票を提案しました。            発生 させます:
                「あなたはメンバーではありません」
            """
            self.data.members.contains(sp.sender)をアサートし、 「あなたはメンバーではありません」
            self.data.lambdas[self.data.nextId] (英語) = lambda_
            self.data.votes[self.data.nextId] (self.data.次の Id) = sp.set()            self.data.nextId += 1

@sp.エントリポイント
        def vote_lambda(self, id):
            """ラムダに投票します。            引数:
                id(sp.nat): 投票するラムダの ID。            発生 させます:
                「あなたはメンバーではありません」、「ラムダは非アクティブです」、「ラムダが見つかりません」

反対票も可決もありません。 ラムダに意見が合わない人がいる場合
            彼らは投票を避けることができます。            """
            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):
            """対応するラムダを返します。            引数:
                id (sp.nat): 取得するラムダの ID。            帰る:
                ラムダのペアと、ラムダがアクティブかどうかを示すブール値。            """
            戻り値(self.data.lambdas[id]、 id >= self.data.inactiveBefore)#「テンプレート」が __name__にない場合:

@sp.モジュール
def test()を使用します。
    class Administrated(sp.契約):
        デフ __init__(自己、管理者):
            self.data.admin =管理者
            self.data.value = sp.int(0)        @sp.エントリポイント
        def set_value(self, value):
            sp.sender == self.data.adminをアサートします
            self.data.value =値

@sp.add_test(name="マルチシグラムダ基本シナリオ", is_default=True)
def basic_scenario():
    """マルチシグラムダをサンプルコントラクトの管理者として使用します。    テスト:
    -創始
    - Lambdaの提出
    - ラムダ投票
    """
    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, member2.address、 member3.address])    sc.h2("MultisigLambda: オリジネーション")
    C1 = メイン。MultisigLambda(メンバー, 2)
    sc + = c1の

sc.h2("管理: オリジネーション")
    C2 = テスト。管理者(c1.address)    sc += c2

sc.h2("マルチシグラムダ: submit_lambda")

def set_42(params):
        administrated = sp.contract(sp.TN、c2.address、entrypoint="set_value")
        sp.transfer(sp.int(42), sp.tez(0), administrated.open_some())    lambda_ = sp.build_lambda(set_42、 with_operations=真)
    c1.submit_lambda(lambda_).run(sender=member1)

sc.h2("マルチシグラムダ:vote_lambda")
    c1.vote_lambda(0).run(送信者=メンバー1)    c1.vote_lambda(0).run(送信者=メンバー2)    # 管理された契約が転送を受け取ったことを確認できます。    sc.verify(c2.data.value == 42)

マルチシグアクション契約

提案への投票の概念を紹介します。 この契約では、署名者は特定のアクションに投票でき、定足数に達すると、提案されたアクションが実行されます。

ニシキヘビ
SmartPy を SP としてインポートする

@sp.モジュール
デフmain():
    # 内部管理アクションタイプの指定
    InternalAdminAction: type = sp.variant(        addSigners=sp.list[sp.address]、        changeQuorum=sp.natの場合、        removeSigners=sp.list[sp.address]、    )

class MultisigAction(sp.契約):
        """複数の署名者が他の
        契約。 管理コントラクトは、それを実現するインターフェイスを実装します
        専門家以外のユーザーに管理プロセスを明示できます。        署名者は提案に投票します。 プロポーザルは、ターゲットのリストであり、
        アクション。 アクションは単純なバイトですが、
        バリアント。 この単純なパターンにより、UX インターフェイスを構築できます
        これは、提案またはビルドの内容を示します。        """

def __init__(self, quorum, signers):
            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.エントリポイント
        def send_proposal(self, proposal):
            """署名者専用です。 投票に提案を提出します。            引数:
                プロポーザル (ターゲットアドレスとアクションの sp.record の sp.list リスト): リスト\
                    ターゲットおよび関連する管理アクションの。            """
            self.data.signers.contains(sp.sender)をアサートします。 「署名者だけがプロポーズできる」
            self.data.proposals[self.data.nextId] (英語) = プロポーザル
            self.data.votes[self.data.nextId] (self.data.次の Id) = sp.set()            self.data.nextId += 1

@sp.エントリポイント
        デフ投票(自己、pId):
            """1 つ以上の提案に投票する

引数:
                pId (sp.nat): プロポーザルの ID。            """
            self.data.signers.contains(sp.sender)をアサートします。 「署名者のみが投票できる」
            assert self.data.votes.contains(pId), 「提案が不明」
            assert 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="読み取り/書き込み", with_operations=True)
        def _onApproved(self, pId):
            """インライン関数。 提案が承認されたときに適用されるロジック。            proposal = self.data.proposals.get(pId, デフォルト=[])
            提案のp_itemの場合:
                コントラクト = sp.contract(sp.list[sp.bytes], p_item.ターゲット)                sp.transfer(
                    p_item.アクション、                    sp.tez(0)、
                    contract.unwrap_some(error="InvalidTarget")、                )
            # すでに提出されているすべてのプロポーザルを無効化します。            self.data.inactiveBefore (英語) = self.data.nextId

@sp.エントリポイント
        def administrate(self, actions):
            """セルフコールのみ。 この契約を管理します。            このエントリポイントは、プロポーザルシステムを介して呼び出す必要があります。            引数:
                actions (sp.list of sp.bytes): \ のパックされたバリアントのリスト
                    'InternalAdminAction' ('addSigners', 'changeQuorum', 'removeSigners')。            """
            アサート (
                sp.sender == sp.self_address()
            )、「このエントリポイントは、プロポーザルシステムを通じて呼び出す必要があります。」

packed_actions in アクションの場合:
                アクション= 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="基本シナリオ", is_default=True)
    def test()を使用します。
        signer1 = sp.test_account("signer1")        signer2 = sp.test_account("signer2")        signer3 = sp.test_account("signer3")        s = sp.test_scenario(メイン)        s.h1(基本シナリオ)

s.h2("オリジネーション")
        C1 = メイン。マルチシグアクション(
            クォーラム=2、
            signers=sp.set([signer1.address, signer2.address])、
        )
        s += c1

s.h2("新しい署名者を追加するための提案")
        ターゲット = 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]), メイン。内部管理アクション
            )
        )
        c1.send_proposal([sp.record(target=target, actions=[action])]).run(
            送信者=署名者1
        )

s.h2("署名者 1 が提案に賛成)
        c1.vote(0).run(送信者=署名者1)        s.h2("署名者 2 が提案に賛成)
        c1.vote(0).run(送信者=署名者2)        s.verify(c1.data.signers.contains(signer3.address))

MultisigViewコントラクト

また、投票メカニズムも利用しています。 このコントラクトにより、メンバーは任意のバイトを送信して投票できます。 プロポーザルが必要な投票数に達すると、そのステータスをビューで確認できます。

ニシキヘビ
SmartPy を SP としてインポートする

@sp.モジュール
デフmain():
    class MultisigView(sp.契約):
        """複数のメンバーが任意のバイトに投票します。        このコントラクトは、アドレスのリストといくつかの
        必要な投票数。 どのメンバーも、必要な数のバイトを送信して投票できます
        アクティブなプロポーザルの場合。        必要な投票数に達したバイトは、ビューで確認できます。        """

デフ __init__(自己、メンバー、required_votes):
            """コンストラクタ

引数:
                メンバー(sp.addressのsp.set):投稿・投票できる人
                    ラムダ。                required_votes (sp.nat): 必要な投票数
            """
            assert required_votes <= sp.len(
                メンバーズ
            ), "required_votes は <= len(members)" でなければなりません。
            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(メンバー、 sp.set[sp.address])            self.data.required_votes = sp.cast(required_votes、 sp.natなど)

@sp.エントリポイント
        def submit_proposal(self, bytes):
            """新しい提案を投票に提出します。            提案を提出しても、賛成票を投じるわけではありません。            引数:
                bytes(sp.bytes): BYTESは投票を提案しました。            発生 させます:
                「あなたはメンバーではありません」
            """
            self.data.members.contains(sp.sender)をアサートし、 「あなたはメンバーではありません」
            self.data.proposals[バイト] = 誤り
            self.data.votes[バイト] = sp.set()        @sp.エントリポイント
        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[bytes])の場合 >= self.data.required_votes:                self.data.proposals[バイト] = 真

@sp.onchain_view()を呼び出します。
        def is_voted(self, id):
            """提案が投票されたかどうかを示すブール値を返します。            引数:
                id (sp.bytes): プロポーザルのバイト数
            帰る:
                (sp.bool): 提案が投票された場合は true、そうでない場合は False。            """
            self.data.proposals.get(id, error="プロポーザルが見つかりません")

「テンプレート」が __name__にない場合:

@sp.add_test(name="マルチシグビューの基本シナリオ", is_default=True)
    def basic_scenario():
        """multisigViewコントラクトに投票するシナリオ。        テスト:
        -創始
        - 提案書の提出
        - 提案の投票
        """
        sc = sp.test_scenario(メイン)        sc.h1("基本シナリオ")        member1 = sp.test_account("member1")        member2 = sp.test_account("member2")        member3 = sp.test_account("member3")        メンバー= sp.set([member1.address, member2.address、 member3.address])        sc.h2("オリジネーション")
        C1 = メイン。MultisigView(メンバー, 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)

# プロポーザルが検証されたことを確認できます。        sc.verify(c1.is_voted(sp.bytes("0x42")))

各コントラクトは、マルチシグレーション制御を実現するための異なるメカニズムを提供し、ブロックチェーンのユースケースの特定のニーズに応じて柔軟性を提供します。

SmartPy Online でマルチシグコントラクトを試すためのステップバイステップガイド

SmartPy で記述したマルチシグコントラクトを試すには、以下の手順に従ってください。

  1. https://smartpy.io/ide の SmartPy IDE に移動します。

  2. コントラクトコードをエディタに貼り付けます。 既存のコードを置き換えることができます。

  3. 契約を実行するには、トップパネルにある「実行」ボタンをクリックします。

  4. コントラクトの実行後、右側の「出力」パネルでシナリオの実行を確認できます。 ここでは、提案、投票、承認など、各アクションの詳細を確認できます。

  5. Tezosネットワークにコントラクトをデプロイするには、まずコントラクトをコンパイルする必要があります。 トップパネルの「コンパイル」ボタンをクリックします。

  6. コンパイル後、「Deploy Michelson Contract」をクリックして、コントラクトをテストネットにデプロイできます。 Tezosアカウントには、デプロイのガス代を支払うのに十分な資金があるシークレットキーを提供する必要があります。

  7. コントラクトがデプロイされると、ブロックチェーン上のコントラクトのアドレスが提供されます。 このアドレスを使用して、トランザクションを介して契約とやり取りすることができます。

  8. 契約で提案を送信したり、投票したりするには、契約コードで定義されているエントリポイント ( や vote_proposalなど) submit_proposal を使用できます。これらは、作成したトランザクションから直接呼び出すことができます。

SmartPy IDEでは、シミュレートされたブロックチェーン上でコントラクトをテストできますが、実際のTezosネットワークにコントラクトを展開するとガス代が発生し、Tezosネットワークのネイティブ暗号通貨であるXTZで支払う必要があることを覚えておいてください。

免責事項
* 暗号資産投資には重大なリスクが伴います。注意して進めてください。このコースは投資アドバイスを目的としたものではありません。
※ このコースはGate Learnに参加しているメンバーが作成したものです。作成者が共有した意見はGate Learnを代表するものではありません。