1. スプートニク-DAO工場契約スプートニク-DAOは、プラットフォームの分散型自律組織(DAO)の統一的な作成と管理を実現するために、創造的な工場設計パターン(Factory Pattern)を採用しています。
この記事では、Sputnik-DAO プラットフォーム ファクトリ パターン (sputnikdao-factory) の設計と実装について詳しく紹介します。
対応するコントラクトのソース コード ウェアハウスは、https://github.com/near-daos/sputnik-dao-contract/tree/518ad1d97614fff4b945aba75b6c8bd2483187a2 にあります。

最初のレベルのタイトル
2. DAPPモジュールの機能紹介
スプートニク DAO プラットフォームの DAPP ページを開くと、多くの分散型自律組織がプラットフォーム上で独自の DAO インスタンス オブジェクト (Sputnikdaov2 コントラクト) を作成およびカスタマイズしていることがわかります。
2022年3月の時点で、このプラットフォームの下で作成された最も活発なDAOはnews.sputnik-dao.nearであり、3,051件の提案(提案)が公開投票中または終了状態となっています。

📄読者の便宜のために、契約の構造図を参考として上に示します。

つまり、スプートニク DAO プラットフォームに基づいて作成されたすべての DAO インスタンス コントラクトは、NEAR アカウントのサブアカウントの下にデプロイされます。次に例を示します。
pcp.sputnik-dao.near
test-dao-bro.sputnik-dao.near
blaqkstereo.sputnik-dao.near
octopode-dao.sputnik-dao.near
NEAR プロトコルのサブアカウントの定義については、https://docs.near.org/docs/concepts/account#subaccounts 🔗 でリファレンスを入手できます。
以下の図に示すように、分散型組織は NEAR メイン ネットワーク上でオープンにトランザクションを開始し、sputnikdao-factory コントラクトによって提供される create() メソッドを呼び出すことで新しい DAO インスタンスを作成できます。

3. スプートニクダオファクトリー契約コードの解釈
Rustのファクトリーモードコントラクトの書き方をより深く理解していただくために、この記事ではsputnikdao-factoryのコントラクトコードを深く解釈していきます。
3.1 DAOの作成
スプートニクダオ工場の契約状況は主に次の 2 つの部分で構成されます。

Factory_manager: コントラクトの主要な内部関数ロジック実装。DAO インスタンスを作成/削除/更新するための一連のメソッドを提供します。
daos: コレクション データ構造を採用し、プラットフォームの履歴に作成されたすべての DAO インスタンスの NEAR アカウント アドレスを記録します。
DAO インスタンスの作成に使用される sputnikdao-factory コントラクト メソッド create() は次のように定義されます。

コードの 3 ~ 5 行目の機能は、将来 DAO コントラクトをデプロイするための NEAR サブアカウント アドレスを取得するために、create() メソッドを呼び出すときに関数パラメーターで指定されたユーザー名を完成させることです。ここで、 env::current_account_id() は、sputnikdao-factory コントラクト、つまり sputnik-dao.near のアドレスを指します。
コードの 6 行目から 11 行目では、create() メソッドが Factory_manager.create_contract を呼び出した後、コールバック関数 on_create の関数パラメータを構築します。
コードの 12 行目から 19 行目では、ファクトリ コントラクトの Factory_manager によって提供される create_contract インターフェイスを呼び出し、create() メソッドの呼び出し元に対して新しい DAO インスタンス コントラクトを作成してデプロイします。同時に、新しくデプロイされた DAO インスタンス コントラクトの場合、コントラクトの基本構成情報を、create_contract パラメーターの引数を通じて Base64 文字列の形式で渡すことができます。
以下は、NEAR メインネットの分散型組織が Sputnik-DAO プラットフォーム上で DAO インスタンス コントラクトを作成するために使用するトランザクションです。
FyECaggFxATGaUMrRKkbotRWAPkhjw5SBnZfRHpzSiQ8🔗
このトランザクションは、sputnikdao-factory コントラクト コードの create() メソッドを呼び出し、multicall.sputnik-dao.near サブアカウントの作成を実現し、対応する DAO インスタンスのコントラクト コードを正常にデプロイします (具体的な実装の詳細は次のとおりです)。詳細については後で説明を展開します)。


Base64 デコード後の args パラメータの具体的な内容は次のとおりです。
この内容はまさに、multicall.sputnik-dao.near コントラクトをデプロイする際にコントラクト初期化メソッド new() を実行する際に必要なコントラクト設定情報です。
次の記事では、factory_manager.create_contract の特定の実装を詳細に分析します。

この関数のパラメータは次のように指定されます。
code_hash: Sputnik-DAO プラットフォームによって提供される標準 DAO インスタンス コントラクト テンプレート コードのハッシュ値。
account_id: multicall.sputnik-dao.near など、今後新しく作成される DAO インスタンス コントラクトのデプロイメント アカウント。このパラメーターの内容は、create_contract() の上位関数 create() で構築されます。
new_method: 新しく作成された DAO インスタンス コントラクト内のコントラクト初期化関数 (通常は new()) を指定します。
args: DAO インスタンスのコントラクト初期化関数 new() を実行するために必要な構成情報。次の 2 つの側面が含まれます。
分散型自律組織が提供するDAO基本情報:Config

5. callback_method: create_contract() メソッドの実行後のコールバック関数を指定します。この関数は、このファクトリ コントラクト内の新しい DAO インスタンス コントラクトの情報を維持および処理するために使用されます。
6. callback_args: コールバック関数の関数パラメータ。
この関数の実行は主に次のステップに分かれています。
コードの 15 ~ 22 行目では、ファクトリ コントラクトによって提供される DAO インスタンス コントラクト テンプレート コード (wasm 形式) を検索し、code_hash に従って番号 0 のレジスタにロードします。
コードの 23 ~ 25 行目は、次のすべてのステップ (3 ~ 6) の処理結果を追跡する Promise を構築します。
コードの 26 ~ 27 行目では、DAO インスタンス コントラクトをデプロイするためのアカウントを作成します。
コード 28-29 は、元のファクトリ コントラクトの create() メソッドの呼び出し元によって Attached_deposited された金額に由来する NEAR トークンを、新しく作成されたアカウントに転送します。
コードの 30 ~ 31 行目は、レジスタ 0 から wasm コードを読み取り、コントラクトをデプロイします。
コードの 32 ~ 41 行目では、DAO インスタンス コントラクト コードの初期化関数 new() を呼び出します。
最終的な DAO インスタンス コントラクトがデプロイされた後、on_create() 関数は、factory_manager.create_contract() 実行コード 32 ~ 53 行の最後でコールバックされます。
以下は、コールバック関数 on_create の内部コード実装です。
この関数の具体的な処理ロジックは次のとおりです。
上記手順(3~6)でエラーが発生し、正常に実行できない場合、コールバック関数on_create()内のnear_sdk::is_promise_success()クエリを呼び出して得られるPromiseの戻り結果はfalseとなります。このとき、元のファクトリコントラクトのcreate()メソッドの呼び出し元によってattached_depositされたNEARトークンの量が返金されます。
上記の手順(3~6)が正しく実行されれば、ユーザーが要求した新しいDAOインスタンスコントラクト(Sputnikdaov2)が正常に作成されたことになります。同時に、ファクトリー コントラクトは、DAO インスタンス コントラクトのサブアカウント アドレスを記録および追跡します。
副題

文章
文章
文章
コードは次の場所にあります: sputnikdao-factory2/src/lib.rs # Line136-149
Factory_manager.update_contract()の処理詳細は以下のとおりです。 このインターフェースは、対応するDAOインスタンスコントラクト内のupdate()関数の呼び出しを実現できます。
以下の点に言及する価値があります。
BlockSec は、Sputnik-DAO コードの分析中に、Factory 契約に重大なセキュリティ上の問題を発見しました。これは、Sputnik-DAO を使用するすべての契約に影響を及ぼします。プロジェクト関係者に連絡した後、問題は最終的に確認され、時間内に修正されました。
💡セキュリティ脆弱性は具体的には次のように説明されています。
以前のバージョンのコードでは、sputinikdao ファクトリ コントラクトによって提供されるパブリック update() メソッドには、次のキー アサーション チェックが欠けていました。これにより、誰でもメソッドを呼び出すことができるようになります。
偶然にも、DAO インスタンス コントラクト (Sputnikdaov2 コントラクト) では、デフォルトで、クロスコントラクト呼び出しを通じて Sputnik-DAO Factory によるこのコントラクトのアップグレードが許可されています。
DAO インスタンス コントラクトに実装された update() メソッドは次のとおりです。コードは sputnikdao2/src/upgrade.rs # 62 行目にあります。
上記のコードの 9 行目では、DAO インスタンス コントラクトがデプロイされ、初期化のために new() メソッドが呼び出されるときに、factory_info.auto_update の値がデフォルトで True に設定されます。
DAO インスタンス コントラクトの new() メソッドは次のように実装されます。コードは sputnikdao2/src/lib.rs # 行 83-104 にあります。
要約すると、一般ユーザー (Factory コントラクトや DAO コントラクト自体ではない) は、Factory コントラクトによって提供される pub fn update() メソッドを通じて DAO コントラクトのコードをアップグレード (改ざん) できます。これにより、Sputnik-DAO プラットフォームとスプートニク-DAOプラットフォームに依存するすべての契約プロジェクトは、大きなセキュリティリスクをもたらします。
🪴 幸いなことに、この問題が発見されたとき、このバージョンのコードはまだ NEAR メインネット上で公開されていなかったため、損失はありませんでした。
プロジェクト関係者の迅速な対応により、合理的なホワイトリスト検証メカニズムを追加することで脆弱性が正しく修正されました😊
最初のレベルのタイトル
文章
上記で発見され修正された脆弱性に加えて、Sputnik-DAO Factory 契約のセキュリティは主に次の側面から保証されています。
次の関数はいずれも状態変数を変更しません。
get_owner(&self)
get_number_daos(&self)
get_default_version(&self)
get_default_code_hash(&self)
get_daos(&self, from_index: u64, limit: u64)
get_dao_list(&self)
get_contracts_metadata(&self)
get_code(&self, code_hash: Base58CryptoHash)
[アクセス制御] コントラクトによって開かれた特権関数。これらの関数はコントラクト所有者 (または DAO コントラクト アカウント) によってのみ実行できます。メソッドには対応するアサーションがあります。次に例を示します。

プロジェクト関係者の迅速な対応により、合理的なホワイトリスト検証メカニズムを追加することで脆弱性が正しく修正されました😊
この修正コミットを参照してください: 518ad1d97614fff4b945aba75b6c8bd2483187a2🔗