💭

🔑マルチシグ・AA・MPCについて〜Web3 セキュリティ〜

2023/01/01に公開

Web3セキュリティ.001.jpeg

皆さん、こんにちは!

先日、UNCHAINでマルチシグをテーマとした勉強会を実施したのですが。思いのほか反響がありましたので多くの方にシェアしたいと思い、ブログでもまとめることにしました!

ぜひ UNCHAINに興味のある方は下のリンクからエントリーしてみてください!
本当に素晴らしいコミュニティですよ!!

https://unchain.tech/

目次

  1. マルチシグについて
  2. マルチシグコントラクトの実装例について
  3. デモ
  4. 残課題と拡張性について
  5. マルチシグとコントラクトウォレットについて
  6. マルチシグ VS MPC
  7. 最後に

1. マルチシグについて

そもそもマルチシグとは?

https://bitcoin.dmm.com/glossary/multisig

日本の暗号資産交換所であれば、交換所のラインセンスを取得するために顧客資産をコールドウォレットとマルチシグの仕組みを利用して管理することは必須となっています!

大事な顧客資産(それも膨大な額)を管理することになるので、内部不正を防ぐという意味でマルチシグは重要な鍵を握っていると考えています。

🔽 有名な暗号資産交換所でも取り入れられています!

https://www.whalefin.com/ja

https://bitflyer.com/ja-jp/security

ビットコインとイーサリアムでの実装の違い

マルチシグといってもその実装方法は、プロトコルレイヤーであるブロックチェーンの仕様にかなり左右されることになる。例えば、ビットコインとイーサリアムを比較しても次の様な違いがあります。

ビットコイン イーサリアム
標準であるか マルチシグは標準仕様として存在している。 マルチシグは標準仕様ではない。
開発言語等 bitcore-jsなどのライブラリを使えばマルチシグウォレットを作成することができる。 標準ではないのでSolidityなどで自分達で作るか事業者が提供しているものを利用する必要がある
セキュリティ上の留意点 標準ライブラリの機能として提供されているのでそのライブラリを信じるしかないが、万が一脆弱性が見つかった場合にはバージョンアップなどの対応が必要 コードを書くことになるため、商用の際には監査の実施が必須となる。その他、Solidityの仕様を把握した上でコーディングしていくことが必要。

上記は、ビットコインとイーサリアムを比較した場合の話。その他のチェーンも考慮する必要がああるので日本の交換所は新しいチェーンに対応するのにかなり苦労しているのではないかと考えられます・・。

2. マルチシグコントラクトの実装例について

今回は、非常にシンプルなマルチシグコントラクトの仕組みをポンチ絵にまとめてみました!

multiSig.drawio.png

factoryコントラクトを介してマルチシグコントラクトを生成することで、使う環境にあったownerのアドレス数と閾値を決めることができます!

この場合であれば下記のように処理を進めて目的のアドレスに送金します!

核となるMulitiSigWalletコントラクトとしては以下のような実装例があります!(Solidity by Exampleから抜粋してきました。)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract MultiSigWallet {
    event Deposit(address indexed sender, uint amount, uint balance);
    event SubmitTransaction(
        address indexed owner,
        uint indexed txIndex,
        address indexed to,
        uint value,
        bytes data
    );
    event ConfirmTransaction(address indexed owner, uint indexed txIndex);
    event RevokeConfirmation(address indexed owner, uint indexed txIndex);
    event ExecuteTransaction(address indexed owner, uint indexed txIndex);

    address[] public owners;
    mapping(address => bool) public isOwner;
    uint public numConfirmationsRequired;

    struct Transaction {
        address to;
        uint value;
        bytes data;
        bool executed;
        uint numConfirmations;
    }

    // mapping from tx index => owner => bool
    mapping(uint => mapping(address => bool)) public isConfirmed;

    Transaction[] public transactions;

    modifier onlyOwner() {
        require(isOwner[msg.sender], "not owner");
        _;
    }

    modifier txExists(uint _txIndex) {
        require(_txIndex < transactions.length, "tx does not exist");
        _;
    }

    modifier notExecuted(uint _txIndex) {
        require(!transactions[_txIndex].executed, "tx already executed");
        _;
    }

    modifier notConfirmed(uint _txIndex) {
        require(!isConfirmed[_txIndex][msg.sender], "tx already confirmed");
        _;
    }

    constructor(address[] memory _owners, uint _numConfirmationsRequired) {
        require(_owners.length > 0, "owners required");
        require(
            _numConfirmationsRequired > 0 &&
                _numConfirmationsRequired <= _owners.length,
            "invalid number of required confirmations"
        );

        for (uint i = 0; i < _owners.length; i++) {
            address owner = _owners[i];

            require(owner != address(0), "invalid owner");
            require(!isOwner[owner], "owner not unique");

            isOwner[owner] = true;
            owners.push(owner);
        }

        numConfirmationsRequired = _numConfirmationsRequired;
    }

    receive() external payable {
        emit Deposit(msg.sender, msg.value, address(this).balance);
    }

    function submitTransaction(
        address _to,
        uint _value,
        bytes memory _data
    ) public onlyOwner {
        uint txIndex = transactions.length;

        transactions.push(
            Transaction({
                to: _to,
                value: _value,
                data: _data,
                executed: false,
                numConfirmations: 0
            })
        );

        emit SubmitTransaction(msg.sender, txIndex, _to, _value, _data);
    }

    function confirmTransaction(
        uint _txIndex
    ) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) notConfirmed(_txIndex) {
        Transaction storage transaction = transactions[_txIndex];
        transaction.numConfirmations += 1;
        isConfirmed[_txIndex][msg.sender] = true;

        emit ConfirmTransaction(msg.sender, _txIndex);
    }

    function executeTransaction(
        uint _txIndex
    ) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) {
        Transaction storage transaction = transactions[_txIndex];

        require(
            transaction.numConfirmations >= numConfirmationsRequired,
            "cannot execute tx"
        );

        transaction.executed = true;

        (bool success, ) = transaction.to.call{value: transaction.value}(
            transaction.data
        );
        require(success, "tx failed");

        emit ExecuteTransaction(msg.sender, _txIndex);
    }

    function revokeConfirmation(
        uint _txIndex
    ) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) {
        Transaction storage transaction = transactions[_txIndex];

        require(isConfirmed[_txIndex][msg.sender], "tx not confirmed");

        transaction.numConfirmations -= 1;
        isConfirmed[_txIndex][msg.sender] = false;

        emit RevokeConfirmation(msg.sender, _txIndex);
    }

    function getOwners() public view returns (address[] memory) {
        return owners;
    }

    function getTransactionCount() public view returns (uint) {
        return transactions.length;
    }

    function getTransaction(
        uint _txIndex
    )
        public
        view
        returns (
            address to,
            uint value,
            bytes memory data,
            bool executed,
            uint numConfirmations
        )
    {
        Transaction storage transaction = transactions[_txIndex];

        return (
            transaction.to,
            transaction.value,
            transaction.data,
            transaction.executed,
            transaction.numConfirmations
        );
    }
}

その他にもhttps://solidity-by-example.org/app/multi-sig-wallet/ やyoutubeなどにシンプルなマルチシグコントラクトを作成する方法は記載されています!

https://www.youtube.com/watch?v=8ja72g_Dac4

その他、セキュリティのスペシャリストとも言えるBitGo社やGnosisSafeが作成しているマルチシグコントラクトもあり、こちらはテストやコードのなどもかなりしっかりしており、構造も上のコントラクトより複雑になっています!

セキュアなコードを開発するのは難易度がやはり高そうです・・・。

https://github.com/BitGo/eth-multisig-v4

https://gnosis-safe.io/

3. デモ

デモでは、2-of-3のマルチシグコントラクトを作成して実際に署名・送金を行います!

デモでやったこと

  1. 2-0f-3のマルチシグコントラクトウォレットを作成する。
  2. トランザクションデータを作成する。
  3. 1.で作成したマルチシグコントラクトウォレットに送金用のETHをdepositする。
  4. 2人のownerにより承認処理を実施する。
  5. 送金処理を実行する。
  6. 送金結果を確認する!

デモで利用するアドレス

owner1 : 0x51908F598A5e0d8F1A3bAbFa6DF76F9704daD072

owner2 : 0x9f9115893713934AA5AFd3540937eB60ea43eb3a

owner3 : 0xb5a05Af300C50baAadE18f8EA8AA7e856177Afe8

送信先:0xEef377Bdf67A227a744e386231fB3f264C158CDF

実際にデプロイしてあるコントラクトの情報

No. コントラクト名 ネットワーク アドレス
1 WalletFactory Goerli 0x3c955E552Fd383435765313330301c23f014e0a6
2 MultiSigWallet Goerli 0x5ba5DF0955d0C95500966acE28041F3D7BC45D1D
3 WalletFactory Mumbai 0xFEbf942Ce0f403a48a01D4757710289E0458bca9
4 MultiSigWallet Mumbai 0x455d21AE3F9Cdd21365757B5528bbE3b3eCC1C1A

4. 残課題と拡張性について

現段階で感じている残課題・拡張性については以下の通りです。

  • 残課題
No 課題
1 送金処理にのみしか対応していないこと
2 もっとセキュアにするためには、ecrecover()などを利用して検証を厳格化する必要がある。
3 どんなアカウントでもマルチシグを作れてしまう状態になっている。
4 どんなアカウントでもマルチシグを作れてしまう状態になっている。
5 考えにくいが、複数のチームがマルチシグコントラクトを共有していた場合に、資金が抜かれる可能性がある。
  • 拡張性
No 項目
1 現在流行りのAAにも使える設計パターンなので、秘密鍵を必要としないウォレットアプリが開発できそう!→ Web3のマスアダオプション?
2 NFTの発行処理など送金以外の処理にも応用できればウォレットとしても十分に機能するはずなので、ハッカソンなどでやってみたい。

5. マルチシグと コントラクトウォレットについて

MultiSigWalletコントラクトには、「コントラクトウォレット」という考え方が採用されています。

コントラクトウォレットの応用としては、**AA(Account Abstraction)がかなり盛り上がってきており、ETHIndiaでもAA(Account Abstraction)**の考え方を採用したプロダクトが1st prizeを獲得するなど注目を集めています。

AAは使っていませんが、せっかくなのでコントラクトウォレットとAAの考え方についても触れていきたいと思います!!

一旦、関連用語の整理をします!

EIP-4337の詳細についてはヴィタリックさんの記事の中でも紹介されている。

重要になるのは、Bundlerとエントリーポイントコントラクト!!

今回のデモで共有したマルチシグDappdeでは、factoryコントラクトからMultiSigWalletコントラクトを生成し、owner達の共有の資金プールとしてMultiSigWalletコントラクトを利用しています。(複数人で一つのウォレットを共有して使っているイメージ)

AAとして実装されてはいないのでユーザーであるownerは、自分で鍵を所有して署名を行う必要がありますが、実行したいトランザクションデータをコントラクトウォレットに登録しておき、その後実行指示を出して送金用のトランザクションを実行しています。(ETHが出るのはコントラクトウォレットから!)

外からみると個人のウォレット(EOA)と何ら変わらないように振る舞っていますが、通常のウォレットと異なる点を整理すると下記の通りです。

  1. 個人のウォレット(EOA)のように秘密鍵が存在しないこと
  2. 承認が一定数得られた後宛先のアドレスにETHを送るという機能を持っていること
  3. deposit機能を持っていること

コントラクトウォレットの考え方については下記の記事とスライドがわかりやすくまとめてくれています!

https://zenn.dev/sivira/articles/d041f1ac44ca1e

https://speakerdeck.com/avcdsld/dapper-contract

AAについてさらに深く知りたいという方は、CryptoGamesのYukiさんがわかりやすく解説してくれている記事があるのでこちらも参照をおすすめします!

https://zenn.dev/yuki2020/articles/302e0d2b58e958

ERC-4337で実装されたシステムがどのように動くのか実際に手を動かしてみたいという方は、StackUp社がサンプルのリポジトリを公開してくれているのでお手軽にAAを利用した送金の仕組みを体験することができます!

https://docs.stackup.sh/docs/guides/quickstart

https://github.com/mashharuki/AA-dapp

  • 送金処理の実行例

    • MATICの送金処理
    Signed UserOperation: {
      "sender": "0x100cD9e97EdAEe0950d97f75251313e45213C8Fb",
      "nonce": "0x0",
      "initCode": "0xe19e9755942bb0bd0cccce25b1742596b8a8250b3bf2c3e700000000000000000000000078d4f01f56b982a3b03c4e127a5d3afa8ebee68600000000000000000000000051908f598a5e0d8f1a3babfa6df76f9704dad0720000000000000000000000000000000000000000000000000000000000000000",
      "callData": "0x80c5c7d00000000000000000000000001431ea8af860c3862a919968c71f901aede1910e000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
      "callGasLimit": "0x5580",
      "verificationGasLimit": "0x129727",
      "maxFeePerGas": "0xa2c22e83",
      "maxPriorityFeePerGas": "0xa2c22e65",
      "paymasterAndData": "0x",
      "preVerificationGas": "0xc644",
      "signature": "0xff954fe43538bf8fff0f928aa03f58c62b7c28d9b14912c4e1b452853124bd622053de4915a85e55697f714e80bf7668670e043467d2116df33979a1c2fd2f551b"
    }
    UserOpHash: 0x854c14b08bd913b7960edc3d4cbef6d65666e20e1494c4a0831a46cb18829fd1
    Waiting for transaction...
    Transaction hash: 0xc561b6a5410ffc8c10bd36b4102a08e57dc92040e4706526c3acedc42143d20d
    ✨  Done in 13.44s.
    
    • ERC20規格トークンの送金処理
    Transferring 0.04 LINK...
    Signed UserOperation: {
      "sender": "0x100cD9e97EdAEe0950d97f75251313e45213C8Fb",
      "nonce": "0x1",
      "initCode": "0x",
      "callData": "0x80c5c7d0000000000000000000000000326c977e6efc84e512bb9c30f76e30c160ed06fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000001431ea8af860c3862a919968c71f901aede1910e000000000000000000000000000000000000000000000000008e1bc9bf04000000000000000000000000000000000000000000000000000000000000",
      "callGasLimit": "0xe6fc",
      "verificationGasLimit": "0x186a0",
      "maxFeePerGas": "0xca0f4bb3",
      "maxPriorityFeePerGas": "0xca0f4b95",
      "paymasterAndData": "0x",
      "preVerificationGas": "0xc37c",
      "signature": "0x06bcf3dcded028a8c42ef90c72f4fea98e54ecd4da2366288cb50851ee60ee0512d6f74a998633109d9352ad0453e3665aa3171147bcf8a876676449499eeef61b"
    }
    UserOpHash: 0xade156da0c2b036926618ed914b2f87dca006d14ba4de29e949326be89cc4096
    Waiting for transaction...
    Transaction hash: 0xdc695a878c6bbfa8e149ea19fddcaad782f98707971d81201d4954113810dd2c
    ✨  Done in 13.16s.
    
    • MATICの送金処理(複数の一括送金)
    Signed UserOperation: {
      "sender": "0x100cD9e97EdAEe0950d97f75251313e45213C8Fb",
      "nonce": "0x0",
      "initCode": "0x",
      "callData": "0x80c5c7d0000000000000000000000000100cd9e97edaee0950d97f75251313e45213c8fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000204d0cb75fa000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000100cd9e97edaee0950d97f75251313e45213c8fb000000000000000000000000100cd9e97edaee0950d97f75251313e45213c8fb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000051908f598a5e0d8f1a3babfa6df76f9704dad07200000000000000000000000000000000000000000000000003782dace9d90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000001431ea8af860c3862a919968c71f901aede1910e00000000000000000000000000000000000000000000000003782dace9d900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
      "callGasLimit": "0xe31a",
      "verificationGasLimit": "0x186a0",
      "maxFeePerGas": "0x7d7a574e",
      "maxPriorityFeePerGas": "0x7d7a5730",
      "paymasterAndData": "0x",
      "preVerificationGas": "0xd56c",
      "signature": "0x804964cc35c21fcb0fd1b4c4fe854a9deb5c7ea9117d68c31b4948914d282fdc53812ebb003ee542e35af8793b60917b0600be54fd05192598a6df396c6c47a81b"
    }
    UserOpHash: 0x77b6dd28dbd8691ea0ae23cc63bbd32f18889723e1c31f04cf534ae7e47569e5
    Waiting for transaction...
    Transaction hash: 0x381f4d559a033559d38370b24280ecad09017bf657f41e6eb277a0691cf5f248
    ✨  Done in 16.04s.
    
    • ERC20規格トークンの送金処理(複数の一括送金)
    Batch transferring 0.04 LINK to 2 recipients...
    Signed UserOperation: {
      "sender": "0x100cD9e97EdAEe0950d97f75251313e45213C8Fb",
      "nonce": "0x2",
      "initCode": "0x",
      "callData": "0x80c5c7d0000000000000000000000000100cd9e97edaee0950d97f75251313e45213c8fb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000204d0cb75fa000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000326c977e6efc84e512bb9c30f76e30c160ed06fb000000000000000000000000326c977e6efc84e512bb9c30f76e30c160ed06fb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000001431ea8af860c3862a919968c71f901aede1910e000000000000000000000000000000000000000000000000008e1bc9bf040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000051908f598a5e0d8f1a3babfa6df76f9704dad072000000000000000000000000000000000000000000000000008e1bc9bf0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
      "callGasLimit": "0xea85",
      "verificationGasLimit": "0x186a0",
      "maxFeePerGas": "0x99adc148",
      "maxPriorityFeePerGas": "0x99adc12a",
      "paymasterAndData": "0x",
      "preVerificationGas": "0xd560",
      "signature": "0x9efbbce4eedb58268350032f6033db120bfe54d34ca920bb15ff72c46f681a2032b2609cb910b646abb5cd87b65a5e1d1e86205bcc64e60d971fff210e72cd611c"
    }
    UserOpHash: 0x856e6d3741c710a76eb4e300fee208b8a33f93425f59be5ed3fea4a35ace9db9
    Waiting for transaction...
    Transaction hash: 0xab22e6bf970c6784aa6d9aa0e44a19adf428999b28370fbe4390fdcc63a31c63
    ✨  Done in 16.64s.
    

残課題にも記載した通り、秘密鍵を必要としないウォレットアプリ(その代わりKYCには厳格な仕様が求められるはず)の開発にも繋げられそうです!!

今の状態ではセキュリティを意識した仕組みになっていますが、様々なユースケースにも応用が効きそうな気がしています!

先日、ETHIndiaで1st prizeを獲得したFireWalletでもAAの考え方が採用されており、今後は使いやすく且つ安全なウォレットを躍起になって開発する動きが活発化していくと考えられるのでやはりこの設計パターンはマスターしたい・・!

秘密鍵の管理は、Web3のマスアダプションを妨げている大きな要因の一つだと考えており、AIやクラウドなどの様な世の中に受け入れられる汎用的な技術に昇華するためにはこの課題解決が必須!!

ただ、中にはAAが浸透していくことについて疑問視する方もいるので、どのようなウォレットがもっとも受け入れられていくかは、引き続きアンテナを貼って情報を収集しつつ、ハッカソンなどに出てプロトタイプを作っていくことが重要だと考えています!!

和組DAOのukishimaさんのツイートは参考になりました!!

6. マルチシグ VS MPC

マルチシグと並んで、Web3領域で注目されている技術が MPC(マルチパーティ計算)を活用した署名の仕組みです!!

また用語の整理として、秘密計算・秘密分散・MPCの意味を整理します!

この秘密計算の仕組みを実現させるために主要な方法が2種類存在する!🚀🚀🚀🚀🚀🚀🚀

  1. 準同型暗号方式
  2. 秘密分散方式

秘密計算と秘密分散について詳しく知りたい方は、下記リンクを確認してください!

https://www.nri.com/jp/knowledge/glossary/lst/ha/secure_computation

https://www.eaglys.co.jp/news/column/secure-computing/mpc/

↓ 秘密分散を利用して秘密鍵を分散し、そこから再度秘密鍵を復元⇨ アドレス復元 ⇨ 送金までをテストできる簡易スクリプトを作成してみましたので興味ある方はぜひこちらも!!

https://github.com/mashharuki/SecretSharingRepo

秘密鍵のデータをそのまま使うマルチシグに比べて、単体では意味のないデータに分割して管理することになるMPCの方がよりセキュアだと考えてられています。(万が一、一箇所で漏れてもダメージはMPCの方が小さいはずです。)

比較表

マルチシグ(ここでは比較のため、スマコンによるマルチシグを想定) MPC
署名処理の実行場所 オンチェーン オフチェーン
実装難易度 中 高
応用の幅 ビットコインとイーサリアムの様に対応するチェーンの仕様によってプログラムが変わるので複数チェーンに対応させるのは至難の技だと考えている。 ブロックチェーンプロトコルの影響を受けにくいので多数のチェーンに応用が効きやすいと考えられている。

実装難易度は、圧倒的にMPCの方が高いですが実装できれば応用が効くものです!

マルチシグの方は、EVM対応のチェーンであればマルチシグコントラクトを使いまわせば行けるかもしれませんが、ビットコインやNEARなどのチェーンなどに対応するとなるとまた違うので対応が大変です。プロトコルレイヤーによって左右されてしまうのは骨が折れるところ。

https://www.fireblocks.com/

もっと知りたい!!という方は下記の記事がおすすめです!

https://academy.binance.com/en/articles/what-is-a-multisig-wallet

https://academy.binance.com/en/articles/threshold-signatures-explained

7. 最後に

ウォレットコントラクトの考え方と合わせるとめちゃめちゃセキュアなウォレットアプリが作れるかも?? (KYCはゼロ知識証明や生体認証などを活用して厳格化iは必須ですね!!)

結局どこかに必ずトラストポイントが生まれるので、そこを如何に守りながら魅力的なアプリを提供していけるかが重要なポイントになりそうです!(一般ユーザーが全員公開鍵暗号方式のことを理解できれば良いのですが、実現できたとしてもとんでもない時間がかかりそうです・・。)

あとは、MetaMaskだとUX的にも少しハードルが高いのでもっとシンプルなプロダクトにする必要がありそうです。下のツイートは面白いと思った一例ですが、指輪の中に署名鍵を入れておいてそれで署名とかできたら見た目的にもオシャレだし使いやすいので世の中に浸透しそうですね!

ウォレットの話になるとソフトウェアだけでなくハード面も考えなくてはいけないので難しいですが最後はここを握ったところが勝ちそうですね!!

https://twitter.com/xembook/status/1602843546381275137

長文となりましたが、最後まで読んでいただきありがとうございました!
2023年もよろしくお願いいたします!!

参考文献

  1. https://github.com/mashharuki/AA-dapp
  2. https://coincheck.com/ja/article/410
  3. https://speakerdeck.com/avcdsld/dapper-contract
  4. https://zenn.dev/mashharuki/articles/a6973bac677427
  5. https://acompany.tech/privacytechlab/mpc-homomorphic-encryption/
  6. https://www.fireblocks.com/
  7. https://jpn.nec.com/press/202201/20220112_02.html
  8. https://solidity-by-example.org/app/multi-sig-wallet/
  9. https://github.com/BitGo/eth-multisig-v4
  10. https://gnosis-safe.io/
  11. https://academy.binance.com/en/articles/what-is-a-multisig-wallet
  12. https://academy.binance.com/en/articles/threshold-signatures-explained
  13. https://qiita.com/bootarouapp/items/b39ca1484874dfb37d46
  14. https://zenn.dev/sivira/articles/d041f1ac44ca1e
  15. https://github.com/mashharuki/SecretSharingRepo
  16. https://drive.google.com/file/d/12MJVIJFYaCWBOB0JUXTNhhTMAryCZYbt/view
  17. https://meety.net/matches/FHQNZgHvWgSh
  18. https://eips.ethereum.org/EIPS/eip-4337
  19. https://www.nri.com/jp/knowledge/glossary/lst/ha/secure_computation
  20. https://www.eaglys.co.jp/news/column/secure-computing/mpc/
  21. https://acompany.tech/privacytechlab/mpc-homomorphic-encryption/
  22. https://zenn.dev/yuki2020/articles/302e0d2b58e95
  23. https://zenn.dev/sivira/articles/34e8147f206ff5

Discussion