AragonOS

in #aragon7 years ago (edited)

aragonOS 3.0 alpha開発者ドキュメント

aragonOS3.0リファレンス実装ドキュメント。2018年3月26日更新(aragonOS v3.1.2リリース)

このドキュメントでは、アーキテクチャに関する技術概要を提供し、仕様書および開発者ガイドとして使用することができます。あまり技術的ではないaragonOS 3.0の紹介については、alpha release blog postがあります。

0. 動機

aragonOSは、分散組織にパワーを供給するためにモジュール式でアップグレード可能なスマートコントラクトを開発するという過程で生まれました。

プロジェクトが成熟し、抽象化が始まるにつれて、アップグレード可能性が必要なあらゆるプロトコルや分散アプリケーションで使用できる非常に汎用的なフレームワークが完成しました。

アップグレード性は、進化し続けるプラットフォームでハイステークスシステムを開発する際には本当に重要です。うまくいけば、プロトコルレベルで問題を引き起こさず、中断を最小限に抑えてバグの修正や改善を行うことができます。歴史上の教訓として、The DAOが効果的なアップグレード可能性メカニズムを持っていた場合、問題を解決するためにハードフォークは必要ありませんでした。非生産的な議論や、ロードマップの遅れ、議定書の時価総額の損失は、フォークのためにネットワーク効果が失われたために発生しました。

しかし、アップグレード性は両刃の剣です。スマートコントラクトがどれほど安全で信頼できないかは問題ではありません。もしそれがアップグレードできれば、コントラクトは実質的に誰がコントラクトをアップグレードすることができるべきでしょうか。アップグレード可能なスマートコントラクトの保証は、ガバナンスメカニズムのように強力であり、ガバナンスをアップグレードすることができます。

同様の方法で、優れたガバナンスにアップグレードする方法がなくても、効果的なガバナンスを確立することはできません。したがって、私たちは、スマートコントラクトというコンテキストでアップグレード可能性ガバナンスが実際に同じコインの両面であることを強く感じています。

aragonOSでは、我々は分散型ガバナンスの研究を行っており、すべての研究結果はaragonOSと互換性があります。つまり、aragonOSを使用することにより、プロトコルがアップグレード性やプロトコルやアプリケーションのガバナンスを両立させることになります。

1. 一般的なアーキテクチャとデザイン哲学

aragonOSを使用してシステムを構築すると、認証ロジックからプロトコルまたはアプリケーションの特定のビジネスロジックを切り離すことができます。

AragonApp基本クラスを継承し、特別な修飾子で認証が必要なアクションを定義するだけで、aragonOSはプロトコルの認証を処理できます。

1.1 基本コンセプト:プロキシとフォワーダ

アーキテクチャに関する一般的なアイデアを説明する前に、フレームワーク全体が構築する2つの概念を理解することが重要です。

  • プロキシ(Proxy): プロキシ(Proxy)は、特定のスマートコントラクトのインスタンスを実際のビジネスロジックの場所と切り離すことからなる非常にシンプルなスマートコントラクトコンストラクトです。コントラクトの個別のインスタンスをプロキシそして、ロジックをベースコントラクトと呼びます。プロキシはすべてのロジックをベースコントラクトとに委譲します。ベースコントラクトへのこのリンクを変更して、プロキシビジネスロジックを効果的に更新できるため、アップグレード可能です。エコシステムの相互運用性を高めるために、ERC897を作成してプロキシインターフェイスを標準化しました。
  • フォワーダー(Forwarder)"フォワーダー(Forwarder)は、ある条件では、ある行動に沿って他のコントラクトにパスするコントラクトです。プロキシによって、契約の特定のインスタンスが、その根底にあるロジックが変更されてもアドレスを変更する必要がないという事実があるため、あるトークンの保有者グループによる一定のサポートを伴う投票でアクションが承認されたというような概念を識別することを可能にし、送信者アドレスは、特定のアドレスを持つ投票アプリケーションのインスタンスか確認するだけです。
    これは、前に説明した認証とロジックの分離を助けます。

1.2 アーキテクチャ:カーネルとアプリケーション

aragonOSで構築された組織やプロトコルは、2種類のスマートコントラクトで構成されています。

  • カーネル: すべての組織の中核であり、組織ごとに1つのインスタンスしか存在しません。アプリケーション、カーネル内の登録アプリ(ACLなど)、またはカーネル自身のベースコントラクトに応じて、異なるベースコントラクトアドレスを追跡するための非常に重要なマッピングを管理します。

  • アプリケーション: アップグレード性とアクセスコントロールのためにカーネルを使用することに依存するコントラクトです。アプリケーションは、カーネルやACLで直接発生するアプリケーションを実装する必要はありません。

1.3 デザイン哲学

Aragonアプリケーションを開発する際に使用する設計思想は、UNIX哲学に非常によく似ています。私たちは、それらを1つのことと1つのことをうまくやるように設計し、いくつかのaragonOSインタフェースを尊重して実装します。

これはテスト容易性のような純粋な技術的利点をもたらしますが、アプリケーションが結合され、1つのアプリケーションの出力が他のアプリケーションの入力になるときにも非常に強力です(フォワーダは何らかの方法でUNIXパイプに似ています)。

1.4 aragonOS呼び出しのライフサイクル

2. カーネル

2.1 アプリマッピング

カーネルの中核には非常に重要なappマッピングと呼ばれるマッピングがあります。

このマッピングを変更すると、完全に破壊的な結果になる可能性があり、資金が失われる可能性があります。このアクションを実行する権限は、ACLの背後で十分に保護されていなければなりません。

function setApp(bytes32 namespace, bytes appId, address app) public;
  • Namespace: 設定されているアプリケーションレコードのタイプを指定します
  • AppId: どのアプリが設定されているかを識別するために使用されます。APMレポのENS namehashです(例: namehash('voting.aragonpm.eth')
  • App: 名前空間によって異なる意味を持つことができるコントラクトののアドレスです

2.2 Namespaces

  • Core Namespace(keccak256('core')): このネームスペースでは、カーネルのコアコンポーネントが存在します。コアマッピングは、カーネルベースコントラクトへの参照です
  • Base namespace(keccak256('base')): appIdsのベースコントラクトを追跡します。
  • App namespace(keccak256('app')): いくつかのアプリは、他のアプリを参照する方法として、アプリの名前空間を使用します。たとえば、これはACLインスタンスまたはEVMScriptsRegistryへの参照を格納するために使用されます

3. アップグレード可能性

アプリケーションとカーネルのアップグレードは、カーネル内のappsマッピングで特定のキーの新しいアドレスを設定することによって行われます。

3.1 カーネルのアップグレード可能性

異なる組織のカーネルインスタンスは同じ実装を共有できます。すべてのKernelインスタンスはKernelProxyです。新しい実装にアップグレードするロジックは実装自体にあります。カーネルをアップグレードすると、アップグレードできなくなる可能性があります。

組織のカーネルをアップグレードするには、Core NamespaceKernel appIdを変更します

kernel.setApp(kernel.CORE_NAMESPACE(), kernel.KERNEL_APP_ID(), newKernelCodeAddr)

3.2 AppProxiesとアップグレード可能性

カーネルと同様に、アプリケーションは実装時にガスを節約するための実装コードを共有することができます。AppProxiesは、アップグレード性をカーネルに依存します。

アプリをアップグレードするには、カーネルのBase NamespaceappIdの新しいアプリアドレスを設定します。

kernel.setApp(kernel.APP_BASES_NAMESPACE(), votingAppId, newVotingAppCodeAddr)

aragonOSには2種類のApp Proxyが用意されています。

  • UpgradeableAppProxy: プロキシへのすべての呼び出しで、カーネルはappIdの現在のコードが何であるかをチェックし、呼び出しを転送します

  • PinnedAppProxy: コントラクト作成時には、現在カーネルにあるアプリケーションコードをチェックして保存します。これは、アプリケーションコードがそのストレージスロットを変更する明示的なロジックを持っていない限り、アップグレードすることはできません。

カーネルには、アプリケーションコードの設定と同時に新しいプロキシの作成を可能にする特別な機能があります。この関数はコードを初めて設定します。

kernel.newAppInstance(votingAppId, votingApp)
kernel.newPinnedAppInstance(votingAppId, votingApp)

3.3 アプリ sandbox(クライアント側)

ユーザーが誤解を招くようにアプリが他のアプリのDOMを変更できないことが最も重要です。そのため、すべてのアプリはsandbox化されています。

これは、アプリが自分自身にアクセスできるiframe内で実行され、トランザクション、コールなどを送信するために、Aragon専用のカスタムRPCプロトコルを使用してAragon dapp("ラッパー")と通信することを意味します。アプリはWeb3に直接アクセスできません。

RPC呼び出しはPostMessage APIを使用してラッパーに送信され、ラッパーは呼び出し内の情報を使用して、イベントのlisten、値のキャッシュ、およびトランザクション・パスの計算などの特定のアクションを実行します。

実際には、これはアプリがインテントを公開するだけで、アクションを直接実行しないことを意味します。代わりに、すべてのビジネスロジックがラッパーに割り当てられます。

4. ACL

Permissionは、特定のアプリケーションインスタンス(そのアドレスによって識別される)でアクションを実行する(ロールによってグループ化される)能力として定義されます。

Permission インスタンスは、一定の許可を保持しているエンティティです。

4.1 Aragon AppのACL、インターフェース

まず、カーネルのベースACLインスタンスを以下のように定義する必要があります。

acl = ACL(kernel.acl())

次に、次のアクションを実行できます。

許可を作成する

acl.createPermission(address entity, address app, bytes32 role, address manager)

そのパーミッションに既存のパーミッションインスタンスがある場合、createPermission()は失敗します。

このアクションは、まだ存在しない場合に新しいパーミッションの作成を許可する以外は、grantPermission()と同じです。

この重要な機能は悪意のある方法で使用される可能性があるため、ACLの役割はcreatePermission()へのアクセスを保護します。カーネルが初期化されると、初期化アドレスに新しい権限を作成する権限が付与されます。

パーミッションの作成はACLによって必須となります。まだ作成されていないパーミッションを必要とするすべてのアクションは、デフォルトでは許可されていません。存在しないパーミッションのパーミッションチェックは自動的に失敗します。

権限を与える

acl.grantPermission(address entity, address app, bytes32 role)

Entityapproleを与えます。特定の許可のmanagerによってのみ呼び出すことができます。このentityは、権限マネージャがrevokePermission()でロールを取り消すまで、roleがその特定のappに対して実行できるすべてのアクションを呼び出すことができます。

grantPermission()アクションは、Entityが権限のmanagerである場合にのみパーミッションを変更できるので、ACLでの保護を必要としません。

権限を取り消す

acl.revokePermission(address entity, address app, bytes32 role)

Entityapproleを取り消します。特定の許可のmanagerによってのみ呼び出すことができます。

revokePermission()アクションは、ACLによって保護される必要はありません。エンティティは、与えられた権限のmanagerである場合にのみ変更を行うことができます。

権限の追加

アプリでは、ACLの背後にあるものを保護するアクションを選択できます。これは、一部のアクションが完全に公開されることがあります。ACLの背後にあるアクションを保護するには、スマートコントラクトで認証修飾子[auth()](https://github.com/aragon/aragonOS/blob/4f4e89abaac6c70243c8288b27272003ecb63e1d/contracts/apps/AragonApp.sol# L10)またはauthP()(パラメーターとして必要な役割を渡します)をアクションに追加します。アクションを実行すると、auth()/authP()修飾子は、呼び出しを実行するEntityが必要な役割を保持しているかどうかをカーネルでチェックします。

4.2 基本ACL

たとえば、次の手順では、基本的なアクセス許可セットを持つ新しいDAOを作成するためのユーザー「ルート」の完全なフローを示しているので、投票アプリケーション は、Vaultアプリに保存されている資金を管理できます。

  1. カーネルとACLを展開する
  2. 順番にacl.initialize(rootAddress)を呼び出す 。kernel.initialize(acl, rootAddress)を実行すると、フードの下で "permissions creator"パーミッションが作成される
    createPermission(rootAddress, aclAddress, CREATE_PERMISSIONS_ROLE, rootAddress)
  3. 投票アプリをデプロイする
  4. 投票アプリケーションにcreatePermission()を呼び出す機能を与える
    grantPermission(votingAppAddress, aclAddress, CREATE_PERMISSIONS_ROLE) (rootAddressによって実行されなければならない)
  5. Vaultアプリケーションをデプロイする。これにはtransferTokens()というアクションがある
  6. 投票アプリケーションを使用して新しい投票を作成し、TRANSFER_TOKENS_ROLE権限を作成
    createPermission(votingAppAddress, vaultAppAddress, TRANSFER_TOKENS_ROLE, votingAppAddress)
  7. 投票が成功すると、VotingアプリはVaultのすべてのアクションにアクセスできる。このアクションはTRANSFER_TOKENS_ROLEによって保護されている。この場合、transferTokens()だけ。
  8. 金庫からの資金移動は、投票アプリの投票を介して制御できるようになる。ユーザーが資金を移転するたびに、金庫の transferTokens()アクションの実行を提案するために、投票アプリから新しい投票を作成することができる。投票が成功した場合にのみ、transferTokens()アクションが実行される。

投票アプリケーションは、TRANSFER_TOKENS_ROLEパーミッションをvaultAppAddressのそのパーミッションのマネージャと同じように、取り消しまたは再調整できることに注意してください。

4.3 権限マネージャ

これまで見てきたように、権限が作成されると、その権限に権限マネージャが設定されます。権限マネージャは、その権限の権限インスタンスを付与または取り消すことができます。

権限マネージャは次の方法で変更できます。

acl.setPermissionManager(address newManager, address app, bytes32 role)

権限マネージャをnewManagerに変更します。特定の許可のマネージャによってのみ呼び出すことができます。

新しい権限マネージャが古い権限マネージャーを置き換えるため、古い権限マネージャーはその権限以上の管理権限を失います。

createPermission()は、このアクションの特殊なケースを実行して、新しく作成された権限の初期マネージャを設定します。 その時点から、マネージャはsetPermissionManager()でのみ変更できます。

権限マネージャのゲッターもあります。

acl.getPermissionManager(address app, bytes32 role)

4.4 パラメータの解釈

権限マネージャによってエンティティに権限が与えられると、エンティティがアクションを実行できるかどうかを調べるたびに評価されるパラメータの配列を割り当てることができます。

パラメータはアクションの実行可否を決定するために、ロールの引数を使用して特定の計算を実行することを可能にします。これにより、純粋にバイナリのアクセスリストであるACLが、より詳細な制御を可能にするより洗練されたシステムに移行します。

ACLパラメータは、3つの値を持つデータ構造で構成されます。

  • Argument Value (uint240): 引数に応じて比較する値です。これは、Ethereumメモリワードであり、2つの最も重要なバイト精度を失います。この理由は、パラメータを1つのストレージスロットに保存して、大幅なGasを節約できるためです。uint240が使用されていても、それは2^30 - 1までの任意の整数、アドレスとバイト32を格納するために使用できます(ハッシュを比較する場合、2バイトの精度を失うことは、 ハッシュアルゴリズムは安全です)。

  • Argument ID (uint8): 比較値の取り込み方法を指定します。0から199までは、ロールに渡される引数インデックス番号を参照します。 200から、いくつかの*特別な引数ID *があります。

    • id = 200 (BLOCK_NUMBER_PARAM_ID): 実行時のブロック番号との比較値を設定します。これにより、タイムロックを設定することができます
      on blocks.
    • id = 201 (TIMESTAMP_PARAM_ID): 実行時に比較値を現在のブロックのタイムスタンプに設定します。これにより、適時にタイムロックを設定することができます。
    • id = 202: 現在未使用
    • id = 203 (ORACLE_PARAM_ID): 引数値のアドレスにあるオラクルでチェックし、真か偽かを返します(argとの比較なし)
    • id = 204 (LOGIC_OP_PARAM_ID): 論理演算を評価し、結果に応じて真または偽を返します(argとの比較なし) - id = 205 (PARAM_VALUE_PARAM_ID): 戻り値としてvalueを使用します。 RETオペレーションでよく使われ、値を返すだけです。 paramの値が0より大きい場合はtrueと評価され、そうでない場合はfalseが返されます
  • Operation type (uint8): 引数IDまたは引数値を使用してフェッチされた値を比較するために実行される操作を決定します。すべての比較では、両方の値がargs[param.id] <param.op> param.valueの順に比較されます。
    したがって、より大きい演算の場合、param = {id: 0, op: Op.GT, value: 10}で、引数0が10より大きいかどうかを解釈します。

    • None(Op.NONE): パラメータや引数にかかわらず、常にfalseと評価されます - Equals (Op.EQ): すべてのバイトがargs [param.id]param.valueの間で一致する場合、trueに評価されます
    • Not equals (Op.NEQ): いずれかのバイトが一致しない場合はtrueに評価されます
    • Greater than (Op.GT): args [param.id] > param.valueの場合に真と評価します
    • Less than (Op.LT): args [param.id] < param.valueならば真と評価します
    • Greater than or equal (Op.GTE): args [param.id] >= param.valueならば真と評価されます
    • Less than or equal (Op.LTE): args [param.id] <= param.valueならば真と評価されます
    • Return (Op.RET): args [param.id]が1より大きい場合に真と評価されます
      PARAM_VALUE_PARAM_IDと一緒に使用すると、args [param.id] = param.valueとなり、パラメータ関連値を返します。

操作も表していますが、IDが「LOGIC_OP_PARAM_ID」の場合は、以下の操作だけが有効です。これらの操作では、パラメータの値を使用して、パラメータ配列の他のパラメータindexを指し示します。これらの値はuint32の数値としてエンコードされ、左にそれぞれ32ビット左シフトされます(例:2つの入力値をとる演算は0x00 .... 0000000200000001、入力1,1、 入力1,2、インデックス1と2のパラメータを参照)。
利用可能なロジック操作:
- Not (Op.NOT): 1つのパラメータインデックスを取得し、リンクされたパラメータが評価するものとは逆の評価をします
- And (Op.AND): 2つのパラメータインデックスを取得し、両方が真であると評価するとtrueに評価されます
- Or (Op.OR): 2つのパラメータインデックスを取得し、いずれかが真であると評価されるとtrueに評価されます
- Exclusive or (Op.XOR): 2つのパラメータインデックスを取得し、いずれかのパラメータのみが真であると評価されるとtrueに評価されます
- If else (Op.IF_ELSE): 3つのパラメータをとり、最初のパラメータを評価し、それが真であると評価する場合は、2番目のパラメータが評価するものを評価し、それ以外の場合は3番目のパラメータが何であっても評価します。

4.6 パラメータ実行

ルールを評価するとき、ACLは常に最初のパラメータの結果を評価します。この第1引数は、他のパラメータにリンクする演算であり、その評価はそれらのパラメータ評価に依存します。

実行は再帰的であり、結果は常に最初のパラメータの評価結果です。

4.7パラメータエンコーディング

他のパラメータにリンクするいくつかの論理演算(AND、OR、IF-ELSE)をエンコードするために、以下のヘルパーが用意されています。関数引数は常に、それらが属する Param配列のパラメータインデックスを参照します。

If-Else演算

encodeIfElse(uint condition, uint success, uint failure)

バイナリ演算(And、Or)

encodeOperator(uint param1, uint param2)

4.8 ルールの例

インタプリタは、複雑なルールのエンコーディングをプログラミング言語によく似た形でサポートしています。例えば、以下のテストケースを見てみましょう。

    function testComplexCombination() {
        // if (oracle and block number > block number - 1) then arg 0 < 10 or oracle else false
        Param[] memory params = new Param[](7);
        params[0] = Param(LOGIC_OP_PARAM_ID, uint8(Op.IF_ELSE), encodeIfElse(1, 4, 6));
        params[1] = Param(LOGIC_OP_PARAM_ID, uint8(Op.AND), encodeOperator(2, 3));
        params[2] = Param(ORACLE_PARAM_ID, uint8(Op.EQ), uint240(new AcceptOracle()));
        params[3] = Param(BLOCK_NUMBER_PARAM_ID, uint8(Op.GT), uint240(block.number - 1));
        params[4] = Param(LOGIC_OP_PARAM_ID, uint8(Op.OR), encodeOperator(5, 2));
        params[5] = Param(0, uint8(Op.LT), uint240(10));
        params[6] = Param(PARAM_VALUE_PARAM_ID, uint8(Op.RET), 0);

        assertEval(params, arr(uint256(10)), true);

        params[4] = Param(LOGIC_OP_PARAM_ID, uint8(Op.AND), encodeOperator(5, 2));
        assertEval(params, arr(uint256(10)), false);
    }

パーミッションに割り当てられている場合、このルールは、オラクルがそれを受け入れ、ブロック番号が前のブロック番号よりも大きく、オラクルが許可する場合(つまり、冗長性もテストする)、または ルールの最初のパラメータは10より小さい。organizations/DAppsガバナンスモデルのカスタマイズの可能性は、実際のSolidityを書く必要なく、真に無限です。

4.9 イベント

createPermission(), grantPermission(), そしてrevokePermission() はAragonのクライアントがキャッシュに格納され、ローカルに保存されたACLのバージョンに処理されると期待されるのと同じSetPermissionイベントを発生させます。

SetPermission(address indexed from, address indexed to, bytes32 indexed role, bool allowed)

setPermissionManager() は次のイベントを発生させます。

ChangePermissionManager(address indexed app, bytes32 indexed role, address indexed manager)

5. フォワーダーとEVMSスクリプト

フォワーダはAragonOSの最も重要な概念の1つです。投票の概念を各アプリケーションの機能とACLにハードコードするのではなく、フォワーディングインターフェイスを実装する一般的な投票アプリケーションを使用して、投票が成功した後で他のアプリにアクションを渡すことができます。投票アプリケーションがトークンの所有者のみが投票できるように設定されている場合、それはトークンの所有者によって承認されている必要があります。

5.1 転送およびトランザクションのルーティング

フォワーディングインタフェースは、アラゴンクライアントがaragon.jsを介してフォワーディングパスと呼ばれるものを計算することも可能にします。あなたがアクションを実行したいとクライアントがそれを行うための直接の権限を持っていないと判断した場合、実行のための代替パスを考えるでしょう。たとえば、トークンの転送を希望するVault Appに直接行くことができます。また、投票で作成された投票を作成するようクライアントから直接指示され、アニメーションに示すように、成功すると転送を実行します。

forwarding animation
(ガバナンスモデルとキャラクターは架空のものです)

EVMスクリプトと呼ばれる独自のスクリプト形式を設計し、複雑なアクションを別のエンティティによって保存され、後で実行される表現にエンコードします。AragonOS 3.0では、組織内に格納できる複数のスクリプトエグゼキュータを持つことができます

5.2 EVMScripts

スクリプトエグゼキュータは、スクリプトと入力を受け取り、実行後に出力を返すコントラクトです。最初のリリースでは、3人のスクリプトエグゼキュータを作成しました。

5.2.1 スクリプトエグゼキュータとEVMScriptRegistry

EVMScriptExecutorsはこのインターフェイスに従わなければなりません。


interface IEVMScriptExecutor {
    function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes);
}

スクリプトエグゼキュータはdelegatecallで呼び出されるので、自己破棄を防ぐために、IEVMScriptExecutor.execScript(...)は少なくとも32バイトを返さなければなりません。

5.2.1.1 CallsScript

複数の呼び出しを連結する簡単な方法で、いずれかの呼び出しが失敗した場合、操作をキャンセルします。

  • Script body: (ペイロードの仕様については、ソースコードファイルを参照してください)
  • Input: なし
  • Output: なし
  • Blacklist: ブラックリストのいずれかのアドレスへの呼び出しが実行されると、スクリプト全体が元に戻ります

5.3 アプリをフォワーダにする

フォワーダの例は、aragon-appsリポジトリにあります。投票とトークンマネージャの両方がフォワーダです。

5.3.1 Warnings

EVMSスクリプトは強力すぎる可能性があります。アプリに転送機能を提供します。

アプリを開発する際に留意すべき事項

例えば、トークンマネージャは、ブラックリストにトークンアドレスを持っています。それ以外の場合は、トークンマネージャを転送することが許可されているトークンホルダは、トークンマネージャと同じ方法で効果的に制御できます。後者の2つのスクリプトエグゼキュータはブラックリストに入れることでトークンアドレスを呼び出さないCallsScriptでのみ動作します。

6. Aragonパッケージマネージャー

6.1 AragonDAOとしてのAPM

Aragon Package Manager(APM)は、AragonOSの上に構築され、AragonOSの一部として統合されています。それは同じAragonで実行されているDAOです(アップグレード可能性とアクセス制御を利用しています)。これはAragon DAOを構築するために使用されています。

これにより、多くのAPMレジストリがパッケージ公開および新しいバージョンのリリースのための異なるガバナンスモデルと共に存在することが可能になります。Aragonの公式の公式のaragonpm.ethが公開され、非常に高い品質基準が厳しく制限されており、コアコンポーネントの公開に使用しています。

全員がパッケージを公開できる異なるAPMレジストリは、コミュニティによって作成される予定です。

この図は、APMレジストリDAOのアーキテクチャを示しています。

6.2 APMRegistry

6.2.1 ENSSubdomainRegistrar

APMRegistryが動作するENS名の所有権は、DAOの一部であるENSSubdomainRegistrarアプリケーションに移されなければなりません。

APMRegistryは、新しい名前を作るためにENSSubdomainRegistrarの権限を持っている必要があります。これは、リポジトリが作成されるたびに行われます。

6.2.2 APMRegistryガバナンス

APMRegistryの各インスタンスは、異なるガバナンスを持つことができます。 レジストリのガバナンスは、DAOのACLを使用して直接実施されます。

デフォルトでは、新しいリポジトリは作成者(またはdevパラメータ)をリポジトリの所有者に設定し、リポジトリに新しいバージョンを作成できる唯一のアドレスです。ただし、権限マネージャーとして、このアカウントは他のエンティティーにバージョンを作成する権限を与えることができます。これらのエンティティは、別の開発者からマルチシッグ、あるいは完全なDAOまで、何でもかまいません。

6.3 Repos

アプリケーションであるACL(2.3節* DAO *のアプリケーションを参照)を走査してDAO内のエンティティを発見した後、そのapp.appId()を取り出してENSを使ってRepoコントラクトを解決することができます。

repo = Repo(Resolver(ens.resolver(appId)).addr(appId))

ens.jsを使用する場合

repo = Repo.at(await ens.addr(appId))

個々のRepoはすべて、ACL用にAPM DAOを使用するAragonアプリです。各APMレジストリガバナンスによっては、リポジトリに新しいバージョンを作成するプロセスまたは所有権を移転するプロセスが異なる場合があります。

Repoはバージョン管理された状態を保持します。

  • Smart contract library code (contractAddress): アプリコードはアプリのデプロイされたバージョンのアドレスです。カーネルは、そのバージョンに関連付けられているアプリコードアドレスをポイントすることで、使用するアプリのバージョンを判断します。
  • Package content (contentURI): パッケージの他のコンポーネント(例えば、フロントエンド)がホストされているロケーションID(IPFS、Swarmなど)およびそれをフェッチするためのコンテンツハッシュによって定義されます。このパッケージの中にarapp.jsonファイルがあります。

Repoは2つのうちの1つのみをバージョンするように作成できます。そのように使用するのは問題ありませんが、以下のすべてのルールが適用されます。

アプリケーションコードのアドレスとパッケージの内容の両方をバージョニングすることによって、Reposの意味論的なバージョン管理のための追加の期待を加えることができます。

  • Patch:パッケージの内容(例: フロントエンド)のマイナーな変更。更新は、ユーザーに対してサイレントモードで実行できます。
  • Minor: パッケージの内容が大きく変更されたが、現在のスマートコントラクトコードでは引き続き機能します。ユーザーは更新プログラムの通知を受ける必要があります。
  • Major: フロントエンドのアップグレードを伴う、または伴わない、スマートコントラクトのアプリコードへの変更。アップグレードにはユーザーの操作が必要です。

6.3.1 バージョンのアップグレードルール

レポに新しいバージョンを作成する前に、エンティティが新しいバージョンを作成する権限を持っているかどうかを確認するACLチェックが実行されます。

ACLチェックの後、Repoロジックはバージョンのアップグレードが許可されているかどうかをチェックします。パッケージのバージョンバンプは、次の規則で定義されます。

  • バージョンの1つのメンバーだけが1だけ増えます。隆起メンバーの左側のバージョンコンポーネントは同じままでなければならず、右側のコンポーネントは0でなければなりません。
    • 例: 2.1.3からのバンプは3.0.0(メジャーバージョン)、2.2.0(マイナーバージョン)、2.1.4(パッチバージョン)です。
  • アプリコードアドレスの変更は、raiseがメジャーバージョンを変更した場合にのみ可能です(上記のルールでM.0.0にアップグレード)。

アプリの初期バージョンはバージョン 0.0.0からの有効なバンプでなければなりません。

このチェックをスマートコントラクトレベルで実行することで、アプリのインスタンスを見るだけで正しいバージョンのフロントエンドを読み込むことができます。 これはスマートコントラクトのバージョンがappIdappCodeを取得することによって特定のアプリにリンクされていることをチェックすることによって行われます(6.3.2.3最新のコントラクトアドレス別を参照)

6.3.2 Repoバージョンの取得

Reposは、バージョンを取得する複数の方法を提供しています。次のイベントのログを確認することで、Repoで作成されたすべてのバージョンが表示されます。

(Repo) NewVersion(uint256 versionId, uint16[3] semanticVersion);

バージョンを取得するためのすべての異なるメソッドは、次のタプルを返します。

repoVersion = (uint16[3] semanticVersion, address contractAddress, bytes contentURI)

6.3.2.1 バージョンID

すべてのバージョンは versionId1で始まり、それぞれのバージョンで1ずつインクリメント)で取り出すことができます。

repoVersion = repo.getByVersionId(versionId)

レポで作成されたバージョンの総数は、次のように照会できます。

count = repo.getVersionsCount()
lastVersionId = count - 1

6.3.2.2 セマンティックバージョン

正確なセマンティックバージョンを提供する。

repoVersion = repo.getBySemanticVersion([major, minor, patch])

6.3.2.3 最新のコントラクトアドレス

最新のバージョンを契約アドレスで取得すると、スマートコントラクトコードを最新バージョンにアップグレードしていない可能性のある組織の最新フロントエンドパッケージをクライアントが入手できるようになります。

repoVersion = repo.getLatestForContractAddress(contractCode)

6.3.2.4 最新バージョン

かなり自己説明的。

repoVersion = repo.getLatest()

7. Aragon app開発ガイド

これは、プラグイン可能なガバナンスを備えた完全にアップグレード可能なレジストリアプリを構築する方法を少しずつ説明したものです。

7.1 ツールの入手

あなたが必要とする最初のものはaragon-dev-cliです。これは、Aragonでアプリケーションを作成して公開するための主要な開発ツールです。

npm i -g @aragon/cli

また、Truffleのような好みのSolidity開発ツールをインストールする必要があります。

npm i -g truffle

7.2 ディレクトリ構造

次に、CLIを使用して新しいアプリを初期化します。

aragon init registry.aragonpm.eth bare

これは bare定型文から現在のディレクトリにregistryという名前のフォルダを作成します。bare定型文には、ディレクトリ構造、マニフェストファイルと2つの依存関係、@aragon/os@aragon/clientです。

- contracts/
- test/
- manifest.json
- arapp.json

ボイラプレートには、あなたのSolidityソースファイルの契約ディレクトリと、Truffleテスト用のテストディレクトリと、アラゴン固有の2つのファイル、manifest.jsonarapp.jsonがあります。

経験則として、manifest.jsonは人間が読めるアプリケーション名、説明、アイコンなど、ユーザーが見るすべてのものを定義します。arapp.jsonは、アプリケーション名(voting.aragonpm.ethなどの完全修飾されたENS名)、アプリケーションの現在のバージョン、 契約ソース、およびトランザクションの経路指定に使用される一連の役割が含まれます。

これらのファイルがどのように見えるかを確認するには、投票アプリケーションのマニフェストファイルここここにあります。

7.3 コントラクトを書く

私たちの契約は簡単なレジストリなので、get、set、removeという3つの操作があります。最初に通常の契約として書いておき、後でAragonOSをプラグインして、それがどれほどシンプルかを説明しましょう。

pragma solidity ^0.4.15;

/**
 * A generic registry app.
 *
 * The registry has three simple operations: `add`, `remove` and `get`.
 *
 * The registry itself is useless, but in combination with other apps to govern
 * the rules for who can add and remove entries in the registry, it becomes
 * a powerful building block (examples are token-curated registries and stake machines).
 */
contract RegistryApp {
    // The entries in the registry.
    mapping(bytes32 => bytes32) entries;

    // Fired when an entry is added to the registry.
    event EntryAdded(bytes32 id);
    // Fired when an entry is removed from the registry.
    event EntryRemoved(bytes32 id);

    /**
     * Add an entry to the registry.
     * @param _data The entry to add to the registry
     */
    function add(
        bytes32 _data
    ) public returns (bytes32 _id) {
        _id = keccak256(_data);
        entries[_id] = _data;

        EntryAdded(_id);
    }

    /**
     * Remove an entry from the registry.
     * @param _id The ID of the entry to remove
     */
    function remove(
        bytes32 _id
    ) public {
        delete entries[_id];
        EntryRemoved(_id);
    }

    /**
     * Get an entry from the registry.
     * @param _id The ID of the entry to get
     */
    function get(
        bytes32 _id
    ) public constant returns (bytes32) {
        return entries[_id];
    }
}

今はレジストリコントラクトがありますが、どのようにアップグレードするのでしょうか?それは、AragonAppインターフェースをインポートして継承するだけです。

// ...

import "@aragon/os/contracts/apps/AragonApp.sol";

// ...

contract RegistryApp is AragonApp {
  // ...
}

コントラクトはこれだけで完全にアップグレード可能です。ガバナンスはどうですか?

正確なガバナンスメカニズム(すなわちonlyOwner)を指定せずにガバナンスレイヤを追加すると、このコントラクトを使用してあらゆる種類の興味深いレジストリを作成できます。単純な民主主義によって管理されるレジストリ、淡い民主主義によって管理されるレジストリなど可能性は本当に無限です。

ガバナンスを取り入れるには、ACLについて学ぶ必要があります。

7.4 ACLの使用

ACL(アクセス制御リスト)は、どこでどのような状況で何ができるのかを定義します。誰が任意のアドレス:アプリ、あなたの犬、あなたの冷蔵庫やあなたがすることができます。これはフォワーダのコンセプト(詳細)と組み合わせて、小さなビルディングブロックから非常に複雑なガバナンス構造を作ることを可能にします。

フォワーダとアプリケーションをUNIXコマンドと考えることができます。彼らは小さく、彼らはうまくいくし、孤立していると便利かもしれませんが、パイプ演算子(|)を使ってそれらを組み合わせるとより強力になります。この類推では、アプリケーションはコマンドであり、フォワーダはパイプです。

これは少し複雑に思えるかもしれませんが、使い方はとても簡単です。

  1. あなたはあなたのアプリが持つ役割を定義します。これはロール識別子のkeccak256でなければなりません。たとえば、ADD_ENTRY_ROLEという名前のロールは次のように定義されていなければなりません。bytes32 public constant ADD_ENTRY_ROLE = keccak256("ADD_ENTRY_ROLE");

  2. AragonAppが提供するSolidityauthまたはauthP修飾子を使用します。

私たちのレジストリにこれを試してみましょう。ADD_ENTRY_ROLEREMOVE_ENTRY_ROLEの2つの役割を定義します。

// ...

contract RegistryApp is AragonApp {
  bytes32 public constant ADD_ENTRY_ROLE = keccak256("ADD_ENTRY_ROLE");
  bytes32 public constant REMOVE_ENTRY_ROLE = keccak256("REMOVE_ENTRY_ROLE");
  // ...
}

次に、auth修飾子でaddremove関数を保護します。

// ...

contract RegistryApp is AragonApp {
  // ...
  /**
   * Add an entry to the registry.
   * @param _data The entry to add to the registry
   */
  function add(
    bytes32 _data
  ) public auth(ADD_ENTRY_ROLE) returns (bytes32 _id) {
    // ...
  }

  /**
   * Remove an entry from the registry.
   * @param _id The ID of the entry to remove
   */
  function remove(
    bytes32 _id
  ) public auth(REMOVE_ENTRY_ROLE) {
    // ...
  }
}

7.5 フロントエンドの構築

まず、Aragon.jsをインストールする必要があります。

npm i @aragon/client

Aragon.jsのクライアント側は、あなたのアプリからラッパーにRPC呼び出しを送るのを担当します。これはセキュリティモデルの重要な部分です。これにより、サンドボックスアプリケーションが可能になるからです。これは、Web3への直接アクセス権がないことを意味し、Aragon.jsを使用してトランザクション、コール、およびイベントのフィルタリングを行う必要があります。

定義したイベントを単に聞き、アプリケーション状態を構築する単純なファイルを作成しましょう。

const Aragon = require('@aragon/client')

// Initialise app
const app = new Aragon()

// Listen for events and reduce state, much like in Redux
// This will listen for events, build the state using our reducer function
// and cache the state for later
app.store((state, event) => {
  // Define initial state
  if (state === null) state = []

  // Reduce based on events
  switch (event.event) {
    case 'EntryAdded':
      return state.concat(event.returnValues.id)
    case 'EntryRemoved':
      return state.filter((entryId) => entryId !== event.returnValues.id))
  }

  // Reducers **must** always return a state
  return state
})

Reducerのstateパラメータは現在の状態(状態が全くない場合はnull)で、eventパラメータは標準のWeb3イベントです。

これで、アプリケーション状態はエントリIDの配列になりました。app.call('get'、entryId)を使って実際のエントリを取得することもできます。これは、呼び出しの結果を出すオブザーバブルを返します。

このガイドの目的のために状態をログアウトしてみましょう。しかし、あなたのアプリをビルドしてReactのようなものにバンドルすることができます(私たちはAragon UIという美しいアプリのフロントエンドを構築するためのUIライブラリを持っています)。

// ...
app.state().subscribe((state) => console.log(state))

Webpackのようなものを使ってアプリケーションをビルドし、スクリプトをバンドルしてブラウザで実行できるようにします。

Sort:  

@ryugouさん、ウェルカムSTEEMITへ! ここで会えてよかったです。UPVOTEしましたね。 (これはWITNESSとして日本語のSTEEMITコミュニティへの少しの貢献です。)

ようこそ、steemitへ
日本のコミニティを応援している @steemit-jpです。@steemit-jpは日本の良い記事を推薦するJapan Daily Curationを実施しています。

また、新しいメンバーへのサポートを推進しています。レピテーション50になるまでタグに jp-newbie を使用していただくと支援のvoteをいたします。

その他イベントなども開催しておりますので、ぜひご参加ください。それでは楽しいsteemit生活をお過ごしください。

日本コミュニティのDiscordチャットで質問もOK!登録しよう!

はじめまして。 @yasu24と申します。よろしくお願いします。 @ryugouさん、エンジニアの方でしょうか?

私、Steemブロックチェーンを使った日本発のサードパーティが出るようになったら面白いかもと思っています。

Posted using Partiko iOS

Congratulations @ryugou! You received a personal award!

1 Year on Steemit

Click here to view your Board of Honor

Support SteemitBoard's project! Vote for its witness and get one more award!