0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[ERC8436] 複数のAIエージェントが投票で作業の承認結果を決める仕組み

0
Posted at

はじめに

初めまして。
『DApps開発入門』という本や色々記事を書いているかるでねです。

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!

AIエージェントが作った成果物を、別のAIエージェントに評価させる場面が増えています。
ただし、1人の判定者へ任せると、その判定者の停止や偏りが作業の受入れ結果を決めてしまいます。

ERC8436は、複数の主体による投票、判断できなかった結果、異議申立て、業務処理への反映を1つのオンチェーン手続きとして記録する規格です。
誰が投票できるかと、誰が資産や作業状態を変更できるかを分けます。

2026年10月16日時点では Draft です。
仕様、全 API、公式参考実装、公式テストベクタ、導入時の安全性を解説します。

概要

ソフトウェア開発を依頼し、AIエージェントから成果物が提出された場面を考えます。
発注者は、1人の評価者ではなく、技術面を確認する5人のAIエージェントに受入れ判断を頼みたいとします。
3人以上が承認すれば成果物を受け入れ、3人以上が反対すれば拒否します。

単に投票結果だけを記録しても、発注者の作業コントラクトが投票結果に従うとは限りません。
評価者の評判や保有トークンは投票資格の根拠になっても、発注者の資産や作業状態を変更する権限にはならないためです。
必要票へ届かなかった場合を承認または拒否へ読み替えると、同じ投票から実装ごとに異なる結果も生まれます。

そこでERC8436は、参加者、必要票、制限時間、異議申立ての回数を投票前に固定します。
この固定した投票規則を Decision Policy と呼びます。
同じ成果物について、同じ質問を、固定した規則で判定する一連の記録が Issue です。

投票結果を業務処理へ反映すると事前に約束するコントラクトまたはアカウントを consumer と呼びます。
consumer は、承認時の処理、拒否時の処理、必要票へ届かなかった時の処理を投票前に固定します。
賛成でも反対でもない結果は NoDecision として残り、 consumer が事前に指定した既定処理へ渡されます。

投票では、各参加者が同じ Issue、同じ round、同じ判断グループへ1票だけ提出します。
必要票が集まるか投票時間が終了すると、そのグループの判断が決まります。
複数のグループを使う場合は、技術面と経済面の両方が賛成した時だけ承認する構成や、拒否権 Body が approve ballot を出した時に全体を止める構成も作れます。

最初の判断に異議申立てがあれば、新しい round を開始します。
異議申立て後に必要票へ届かなかった時、前回の判断を残すか、全体を NoDecision にするかも投票前の規則へ含めます。
最終結果が確定した後だけ、 consumer が自分の権限で成果物の受入れまたは拒否を実行します。

複数人で作業成果を判定する場面

5人中3人の承認を求める場合、同じ成果物と質問を5人へ渡します。
各参加者は、自分が確認した内容に基づいて賛成または反対を提出します。
3票の賛成が集まれば Yes、3票の反対が集まれば No です。

期限までに賛成が2票、反対が2票、未投票が1人なら、どちらの条件にも届きません。
ERC8436は、賛否どちらの必要票にも届かなかった投票を NoDecision として記録します。
未達を自動で拒否へ変換しないため、発注者は投票開始前に**NoDecision なら既存の納期処理へ戻す**と決められます。

結果を受け入れる相手

投票参加者は判断を作りますが、作業報酬や成果物の状態を変更しません。
作業コントラクトを操作できる consumer だけが、最終結果を業務処理へ反映します。

consumer は、 Issue を自分で作る、事前の受入れ条件を登録する、第三者が作った Issue を投票前に承認する、のいずれかで約束を固定します。
この投票前の約束を Consumer commitment と呼びます。
約束が成立した Issue は Binding です。
約束がない Issue は Advisory として判断記録を残せますが、業務処理を拘束しません。

投票開始から業務処理まで

発注者側の adapter は、作業コントラクトから成果物、期限、承認時と拒否時の処理を読み取って Issue を作ります。
Issue Registry は、固定済みの投票規則と consumer の約束を確認して最初の round を開始します。
参加者の投票、必要票の判定、異議申立てを経て、最終結果を保存します。

ERC8436で作業成果を複数のAIエージェントが判定しconsumerが業務処理へ反映する流れ

最終結果が Yes なら adapter が作業コントラクトの受入れ関数を呼び、 No なら拒否関数を呼びます。
呼び出しに失敗しても投票結果は変わりません。
consumer は失敗を実行記録へ残し、作業コントラクトの条件が許す範囲で再試行します。

動機

複数の署名を集める仕組みや、トークン保有者が提案へ投票する仕組みはすでにあります。
しかし、具体的な成果物と質問、結果に従う consumer、必要票へ届かなかった結果、異議申立て、業務処理の成否までを同じ記録へ結び付けるには、追加の約束が必要です。

1人の評価者だけを指定する作業コントラクトでは、評価者の停止がそのまま処理停止になります。
複数の投票者を置いても、投票者と業務実行者の権限を分けなければ、評価資格を持つ主体が発注者の資産まで動かせる設計になります。

ERC8436は、判断を作る手続きと判断を実行する権限を分離します。
投票規則と consumer の約束を先に固定し、投票後に都合のよい結果だけを採用する余地も狭めます。

既存の投票部品で欠ける記録

方式 質問と対象 NoDecision 複数グループ 異議申立て 業務処理との接続
multisig 呼び出し内容へ埋め込む 個別設計 単一の署名者集合 個別設計 署名成立で直接実行する
governor 提案ごとに持つ quorum 未達として扱う 通常は1つの選挙人集合 個別設計 提案の実行へ接続する
単一 evaluator 作業ごとに持つ evaluator の応答規則次第 持たない 個別設計 作業コントラクトが結果を読む
ERC8436 Issue へ固定する 独立した正式結果 Body ごとに分離する Policy へ固定する consumer が自分の権限で実行する

ERC8436が必要になるのは、投票数だけでなくどの質問を、誰の判断で、どの業務へ反映するかを再現したい場合です。

投票資格と実行権限の分離

投票資格は、固定された参加者一覧や、特定の報告者として Policy へ入ります。
一方、業務実行権限は consumer がすでに持つ権限です。
投票参加者が高い評価を持っていても、 consumer の約束なしに報酬や作業状態を変更できません。

この分離により、参加者の選定方法を将来の別仕様へ委ねられます。
身元、評判、利害関係の除外を追加しても、 consumer の権限モデルを変更せずに済みます。

決まらなかった結果の扱い

投票では、賛成にも反対にも届かないまま期限を迎える場合があります。
単一の報告者が期限までに応答しない場合もあります。
拒否権を持つグループが、拒否も許可も明示しない場合もあります。

ERC8436は、これらを NoDecision の理由として分けます。
consumer は NoDecision 時の処理を投票前に固定するため、結果を見てから承認扱いか拒否扱いかを選べません。

判断記録と業務実行の分離

Issue Registry は、投票と異議申立てから最終結果を作ります。
外部コントラクトの資産や作業状態は変更しません。

consumer は最終結果を読み、自分が操作できるコントラクトへ承認または拒否を送ります。
外部呼び出しが失敗した場合、最終結果を巻き戻さず、 Failed の実行記録を残します。
判断の確定と業務処理の成功を別々に監査できます。

仕様

ERC8436は、 Policy Registry と Issue Registry を同時にデプロイする構成を定めます。
Policy Registry は変更できない投票規則を保存し、 Issue Registry は具体的な判断案件、投票回、最終結果を保存します。

ACDFTypes

ACDFTypes ライブラリは、 Policy、 Issue、 round、結果で共有する Enum 型と構造体を定義します。

BodyKind

enum BodyKind { ROSTER_KOFN, AUTHORIZED_SUBMITTER }

BodyKind は、1つの判断グループが固定名簿の投票で決めるか、固定された報告者が結果を提出するかを表します。

値 入力 判断結果
ROSTER_KOFN 名簿の各 member が投票する K票の賛成またはN-K+1票の反対で決まる
AUTHORIZED_SUBMITTER 1つの address が結果を提出する Yes、 No、 NoDecision の提出値を使う

Acceptance

enum Acceptance { ON_CHAIN_TALLY, SIGNED_BALLOTS, AUTHORIZED_SUBMITTER }

Acceptance は、 Body が判断材料を受け取る方式です。

値 入力条件 後続の判定
ON_CHAIN_TALLY voter 自身が transaction を送る roster と重複を確認して票数へ加える
SIGNED_BALLOTS voter が署名し、任意の relayer が提出する 署名検証後に同じ票数判定へ進む
AUTHORIZED_SUBMITTER 固定報告者が Body 結果を送る 個別票を数えず提出値を採用する

Combinator

enum Combinator { BODY, ALL, ANY, KOFM, VETO }

Combinator は、 Body または子 Node の四値判断を合成する方法です。

値 条件 合成結果
BODY 1つの Body を参照する Body の値を返す
ALL すべての子が Yes Yes になる。1つでも No なら No になる
ANY 1つ以上の子が Yes Yes になる。すべて No なら No になる
KOFM M個の子のうちK個が Yes 閾値到達で Yes、到達不能で No になる
VETO target Node と拒否権 Body を別々に参照する 拒否権 Body の Yes で No、明示的な No で target を通す

VetoSilence

enum VetoSilence { PASS_THROUGH, REQUIRE_CLEARANCE }

VetoSilence は、拒否権 Body が期限までに結論を出さなかった時の扱いです。
拒否権 Body では、 approve ballot による Yes が veto を意味し、 No が明示的な clearance を意味します。
PASS_THROUGH は沈黙時に target の結果を通し、 REQUIRE_CLEARANCE は明示的な No がないため NoDecision にします。

値 入力または保存状態 後続の判定 例
PASS_THROUGH 拒否権 Body が期限まで沈黙する target の値を使う 技術 Body の Yes をそのまま root へ渡す
REQUIRE_CLEARANCE 拒否権 Body が期限まで沈黙する NoDecision(NO_CLEARANCE) にする 審査 Body の明示的な No を待つ

AppealStanding

enum AppealStanding { ANYONE, CONSUMER_OR_FILER }

AppealStanding は、異議申立てを開始できる主体を表します。
ANYONE は任意の address を許し、 CONSUMER_OR_FILER は consumer または Issue 作成者だけを許します。

値 caller 条件 後続の判定 例
ANYONE 任意の caller window と回数を確認する keeper が appeal transaction を送る
CONSUMER_OR_FILER consumer または filer それ以外を拒否する 発注者または Issue 作成者が再審を求める

AppealMode

enum AppealMode { PRESERVE_UNLESS_OVERTURNED, REQUIRE_FRESH_DECISION }

AppealMode は、 appeal round が NoDecision になった時に前 round の判断を残すかを表します。
PRESERVE_UNLESS_OVERTURNED は直近の Yes または No を採用し、 REQUIRE_FRESH_DECISION は Issue 全体を NoDecision にします。

値 appeal 結果 Final 結果 例
PRESERVE_UNLESS_OVERTURNED 新 round が NoDecision 直近の Yes または No を採用する round 1の Yes を残す
REQUIRE_FRESH_DECISION 新 round が NoDecision Issue 全体を NoDecision にする 再審で必要票へ届かず effect を選ばない

BodySpec

struct BodySpec {
    BodyKind kind;
    Acceptance acceptance;
    address[] members;
    uint32 k;
    uint64 window;
}

BodySpec 構造体は、1つの判断グループの参加者、入力方式、閾値、期限をまとめます。
Policy 作成者が PolicySpec へ組み込み、 Policy Registry が保存し、 Issue Registry の Body 判定と bodyOf 関数が参照します。

  • フィールド
フィールド 詳細
kind 名簿投票または固定報告者を選びます。
acceptance オンチェーン投票、署名投票、報告者提出を選びます。
members zero 以外で重複しない参加者です。報告者方式では1件です。
k 名簿投票で必要な賛成票数です。
window round 開始から入力を受け付ける秒数です。

Node

struct Node {
    Combinator op;
    uint32 body;
    uint32 k;
    uint32 target;
    uint32 vetoBody;
    VetoSilence silence;
    uint32[] children;
}

Node 構造体は、 Body 結果をどの論理で合成するかを表します。
nodes[0] が最終判断を返す root です。
Policy 作成者が tree を組み、 Policy Registry が保存し、 Issue Registry の nodeStatus 関数が root まで評価します。

  • フィールド
フィールド 詳細
op Body 参照、 ALL、 ANY、 K-of-M、 VETO を選びます。
body BODY で参照する Body 番号です。
k KOFM で必要な Yes の数です。
target VETO で保護する Node 番号です。
vetoBody 拒否権を持つ Body 番号です。
silence 拒否権 Body が沈黙した時の扱いです。
children ALL、 ANY、 K-of-M で参照する子 Node 番号です。

PolicySpec

struct PolicySpec {
    bytes32 family;
    uint32 version;
    bytes32 previous;
    address updateAuthority;
    BodySpec[] bodies;
    Node[] nodes;
    uint32 maxAppeals;
    uint64 appealWindow;
    uint8 appealable;
    AppealStanding appealStanding;
    AppealMode appealMode;
    uint64 maxTotalDuration;
    uint64 ackWindow;
    bool allowAdvisory;
    bytes32 descriptorHash;
}

PolicySpec 構造体は、投票資格、閾値、合成、時間、異議申立て、受入れ条件を1つにまとめます。
全フィールドの ABI encoding から policyId を計算するため、説明用 JSON と実行規則の食い違いを検出できます。
Policy 作成者が registerPolicy 関数へ渡し、 Policy Registry が変更不能な値として保存し、 Issue Registry と各 read 関数が参照します。

  • フィールド
グループ フィールド 意味
版 family、 version、 previous、 updateAuthority 直前 Policy への参照、増加する版番号、公開主体です。
判断 bodies、 nodes 参加者、閾値、合成 tree です。
appeal maxAppeals、 appealWindow、 appealable、 appealStanding、 appealMode 異議申立ての回数、時間、主体、採用規則です。
時間 maxTotalDuration、 ackWindow 手続き全体と事後承認の上限です。
補助 allowAdvisory、 descriptorHash Advisory 開始と off-chain 説明の hash です。

ProcedureState

enum ProcedureState { None, Filed, Deciding, Provisional, Final, Withdrawn }

ProcedureState は、 Issue が受付から終了までのどこにあるかを保存します。

値 保存状態 許される処理
None Issue が存在しない 後続操作を拒否する
Filed 受付済みで投票開始前 承認、 Advisory 開始、期限切れ、取消を受け付ける
Deciding round が進行中 投票、 Body 結果提出、 settle を受け付ける
Provisional round 結果が決まり異議申立て待ち appeal または finalize を受け付ける
Final 最終結果を保存済み consumer が結果を実行できる
Withdrawn 投票前に取消済み terminal として後続操作を拒否する

OutcomeType

enum OutcomeType { None, Decided, NoDecision }

OutcomeType は、 Final 時に Yes または No が決まったか、判断を作れなかったかを表します。
None では effect を実行せず、 Decided では outcomeYes に対応する effect、 NoDecision では disposition を選びます。

値 保存状態 後続の判定 例
None Final 前または結果未保存 effect を実行しない Deciding 中の Issue
Decided Yes または No を Final へ保存済み outcomeYes から effect を選ぶ Yes なら accept を呼ぶ
NoDecision 判断不能を Final へ保存済み 事前の disposition を使う quorum 不足で既定処理へ戻る

AcceptanceMode

enum AcceptanceMode { CONSUMER_FILED, STANDING_ACCEPTANCE, POST_ACK }

AcceptanceMode は、 consumer が結果に従う約束を投票前に固定する方法です。

値 約束を作る操作 admission
CONSUMER_FILED consumer 自身が Issue を作る 直ちに Binding で開始する
STANDING_ACCEPTANCE consumer が事前条件を登録する 条件一致時に Binding で開始する
POST_ACK filer が作り、 consumer が期限内に承認する 承認後に Binding で開始する

EffectClass

enum EffectClass { Advisory, Binding }

EffectClass は、最終結果が consumer の事前の約束を持つかを表します。
Advisory は判断記録だけを残し、 Binding は固定済みの effects と obligation を持ちます。

値 保存状態 後続の判定 例
Advisory consumer commitment なし enactment を記録しない 未承認 Issue を参考判断として開始する
Binding consumer、 effects、 obligation を固定済み Final-Decided 後に effect を選べる Yes で作業を受け入れる

NodeStatus

enum NodeStatus { Pending, Yes, No, NoDecision }

NodeStatus は、 Body または合成 Node の現在の四値判断です。
例として、 K-of-N の必要票へ届けば Yes、 blocking threshold へ届けば No、期限前でどちらにも届かなければ Pending を返します。
Pending は入力で変わる状態、 Yes と No は実質判断、 NoDecision は期限内に判断条件が成立しなかった状態です。

Reason

enum Reason {
    NONE,
    QUORUM_NOT_MET,
    SUBMITTER_SILENT,
    VETOED,
    NO_CLEARANCE,
    NOT_REACHED,
    TOTAL_TIMEOUT,
    NO_ACCEPTANCE
}

Reason は、 NoDecision、拒否権、未承認が発生した理由を保存します。

値 発生条件
NONE 特別な失敗理由がない
QUORUM_NOT_MET 名簿投票が必要票へ届かない
SUBMITTER_SILENT 固定報告者が期限までに提出しない
VETOED 拒否権 Body が拒否する
NO_CLEARANCE 明示許可が必要な拒否権 Body が沈黙する
NOT_REACHED 子 Node から合成結果を作れない
TOTAL_TIMEOUT hard deadline までに round が終わらない
NO_ACCEPTANCE POST_ACK が承認されない

EnactmentStatus

enum EnactmentStatus { NotEnacted, Enacted, Failed }

EnactmentStatus は、 consumer が Final 結果を業務コントラクトへ反映できたかを表します。
Failed は後から Enacted へ更新できますが、 Enacted は terminal です。

値 保存状態 後続の判定 例
NotEnacted 実行報告なし 実行または再試行を待つ Final-Decided 直後
Failed target call 失敗を報告済み 条件が戻れば再試行できる 期限条件で accept が失敗する
Enacted effect 成功を報告済み 後続更新を拒否する accept 完了を記録する

Subject

struct Subject {
    uint256 chainId;
    address target;
    uint256 id;
    bytes32 dataHash;
}

Subject 構造体は、どの chain のどの target にある何を評価するかを固定します。
filer または adapter が作り、 Issue Registry が Issue 内へ保存し、 getIssue 関数と consumer adapter が照合します。

  • フィールド
フィールド 詳細
chainId 評価対象が属する chain です。
target 作業コントラクトや対象コントラクトです。
id task、 submission、 token の識別番号です。
dataHash 評価する具体的な内容を固定する hash です。

IssueInput

struct IssueInput {
    bytes32 policyId;
    AcceptanceMode mode;
    bytes32 acceptanceId;
    address consumer;
    Subject subject;
    bytes32 question;
    bytes32 effectYes;
    bytes32 effectNo;
    bytes32 disposition;
    bytes32 obligationId;
    uint64 consumerDeadline;
}

IssueInput 構造体は、 filer が Issue を作る時に渡す値をまとめます。
file 関数が acceptance mode 別に値を検証し、 admission 後は Issue Registry の Issue へ固定し、 getIssue 関数から読めます。

  • フィールド
グループ フィールド 意味
規則 policyId、 mode、 acceptanceId 適用 Policy と consumer の約束です。
主体と対象 consumer、 subject、 question 実行主体、評価対象、質問です。
結果処理 effectYes、 effectNo、 disposition Yes、 No、 NoDecision 時の処理です。
義務と期限 obligationId、 consumerDeadline 並行 Issue を防ぐ範囲と consumer 側期限です。

StandingAcceptanceInput

struct StandingAcceptanceInput {
    bytes32 policyId;
    address subjectTarget;
    bytes32 question;
    address[] filers;
    bytes32 effectYes;
    bytes32 effectNo;
    bytes32 disposition;
    bytes32 obligationId;
    uint64 validUntil;
}

StandingAcceptanceInput 構造体は、 consumer が将来の Issue へ事前に適用する条件をまとめます。
subjectTarget と question は zero で任意を表し、 filers は空配列で任意の作成者を許します。
consumer が registerStandingAcceptance 関数へ渡し、 Issue Registry が acceptance record として保存し、 file 関数が照合します。
obligationId == 0 の場合、 admission 時に exact Subject の ABI encoding から Issue 用 obligationId を導出するため、 filer は排他範囲を選べません。

  • フィールド
フィールド 詳細
policyId 受け入れる正確な Policy です。
subjectTarget zero なら任意、 zero 以外なら一致を求める target です。
question zero なら任意、 zero 以外なら一致を求める質問です。
filers Issue 作成を許す address です。空配列なら制限しません。
effectYes Yes 時に約束する effect です。
effectNo No 時に約束する effect です。
disposition NoDecision 時の処理です。
obligationId 排他範囲です。 zero なら exact Subject から導出します。
validUntil 事前登録の有効期限です。

Issue

struct Issue {
    bytes32 policyId;
    AcceptanceMode mode;
    EffectClass effectClass;
    ProcedureState state;
    address filer;
    address consumer;
    bytes32 acceptanceId;
    Subject subject;
    bytes32 question;
    bytes32 effectYes;
    bytes32 effectNo;
    bytes32 disposition;
    bytes32 obligationId;
    bytes32 obligationKey;
    uint64 filedAt;
    uint64 admittedAt;
    uint64 consumerDeadline;
    uint64 hardDeadline;
    uint64 finalAt;
    uint32 roundCount;
    OutcomeType outcomeType;
    bool outcomeYes;
    Reason reason;
    uint32 sourceRound;
    uint64 decidedAt;
}

Issue 構造体は、 admission で固定した値、期限、現在状態、採用結果を保存します。
Final 後は outcomeType、 sourceRound、 decidedAt から、どの round の判断を採用したか追跡できます。
Issue Registry が file、 acknowledge、 admitAdvisory で作成・更新し、 getIssue 関数が全保存値を返します。

  • フィールド
グループ フィールド 詳細
規則・状態 policyId、 mode、 effectClass、 state 固定 Policy、受付方式、拘束性、手続き状態です。
主体・対象 filer、 consumer、 acceptanceId、 subject、 question 作成者、実行主体、事前登録、評価対象、質問です。
処理・排他 effectYes、 effectNo、 disposition、 obligationId、 obligationKey 最終結果の処理と並行 Issue を止める key です。
時刻 filedAt、 admittedAt、 consumerDeadline、 hardDeadline、 finalAt 受付、開始、 consumer 期限、手続き上限、確定時刻です。
結果 roundCount、 outcomeType、 outcomeYes、 reason、 sourceRound、 decidedAt round 数、結果種別、賛否、理由、採用 round、判断形成時刻です。

RoundState

struct RoundState {
    uint64 startedAt;
    uint64 deadline;
    bool settled;
    NodeStatus status;
    Reason reason;
    uint64 at;
    uint64 appealOpenUntil;
}

RoundState 構造体は、1つの round の開始、結果形成、 settle、異議申立て期限を保存します。
at は settle transaction の時刻ではなく、票または期限によって root 値が変わらなくなった時刻です。
Issue Registry が round 開始時と settleRound 関数で更新し、 getRound 関数が履歴を返します。

  • フィールド
フィールド 詳細
startedAt round 開始時刻です。
deadline 最長 Body window から決まる終了時刻です。
settled root 結果を保存済みかを示します。
status settle した四値判断です。
reason 判断または NoDecision の理由です。
at 判断が変わらなくなった時刻です。
appealOpenUntil 異議申立てを受け付ける上限時刻です。

BodyState

struct BodyState {
    uint32 yes;
    uint32 no;
    bool submitted;
    NodeStatus submitted_;
    uint64 decidedAt;
}

BodyState 構造体は、 Body ごとの票数または固定報告者の提出値を保存します。
名簿投票では yes と no、固定報告者では submitted と submitted_ を読みます。
Issue Registry が投票受理または Body 結果提出時に更新し、 getBodyState 関数と bodyStatus 関数が参照します。

  • フィールド
フィールド 詳細
yes 受理済み approve ballot 数です。
no 受理済み block ballot 数です。
submitted 固定報告者が提出済みかを示します。
submitted_ 固定報告者が提出した Yes、 No、 NoDecision です。
decidedAt threshold 到達または提出時刻です。

Enactment

struct Enactment {
    EnactmentStatus status;
    address reporter;
    bytes32 ref;
    uint64 at;
}

Enactment 構造体は、 consumer が業務 effect を実行した結果、報告者、外部参照、報告時刻を保存します。
consumer が recordEnactment 関数で報告し、 Issue Registry が Issue ・ consumer ・ effect ID の組で保存し、 getEnactment 関数が返します。

  • フィールド
フィールド 詳細
status NotEnacted、 Failed、 Enacted の実行状態です。
reporter 実行結果を報告した consumer です。
ref 外部 transaction や失敗理由を結ぶ参照値です。
at 報告時刻です。

Result

struct Result {
    ProcedureState state;
    OutcomeType outcomeType;
    bool outcomeYes;
    Reason reason;
    uint64 decidedAt;
    uint64 finalAt;
    bytes32 policyId;
    uint32 roundCount;
    uint32 sourceRound;
    EffectClass effectClass;
    address consumer;
    bytes32 effectYes;
    bytes32 effectNo;
    bytes32 disposition;
    bytes32 obligationId;
    bool adoptedFromEarlierRound;
}

Result 構造体は、 client が現在状態、最終判断、採用 round、 consumer、業務 effect をまとめて読む値です。
state == Final と outcomeType == Decided を両方確認してから、 outcomeYes に対応する effect を選びます。
Issue Registry が保存済み Issue と round 履歴から組み立て、 getResult 関数が consumer adapter や client へ返します。

  • フィールド
グループ フィールド 詳細
手続き state、 outcomeType、 outcomeYes、 reason 現在状態、結果種別、賛否、理由です。
時刻・ round decidedAt、 finalAt、 roundCount、 sourceRound、 adoptedFromEarlierRound 判断形成、確定、 round 数、採用元、以前の round 採用 flag です。
規則・主体 policyId、 effectClass、 consumer Policy、拘束性、実行主体です。
処理 effectYes、 effectNo、 disposition、 obligationId Yes、 No、 NoDecision の処理と義務範囲です。

Conformance と署名の前提

client は registry address だけを信用せず、必要な interface ID へ対応するかを supportsInterface 関数で確認します。
この問い合わせ方法をERC165が定めています。
Policy Registry の interface ID は 0xb362eb4e、 Issue Registry は 0xd31aae30 です。

ERC165については以下の記事を参考にしてください。

署名投票では、 Issue、 round、 Body、 voter、賛否と、 chain ID、 registry address を同じ digest へ入れます。
ウォレットとコントラクトが同じ field 順で digest を作る形式がEIP712です。
これにより、 client は署名が別 round や別 registry 向けではないかを確認できます。

EIP712については以下の記事を参考にしてください。

voter がコントラクトアカウントの場合、 Issue Registry は isValidSignature 関数を staticcall し、 magic value を確認します。
この検証方法をERC1271が定めています。
contract wallet が現在の権限設定で署名を承認した時だけ、その ballot を受理します。

ERC1271については以下の記事を参考にしてください。

IACDFPolicyRegistry

IACDFPolicyRegistry は、変更できない Policy を登録し、 family の版、 Body、 Node、時間設定を読み出す interface です。

supportsInterface

function supportsInterface(bytes4 interfaceId) external view returns (bool);

指定した interface ID へ Policy Registry が対応するかを返す関数です。

  1. ID を受け取る

    client が利用したい API 集合の ID を渡す。

  2. 対応状況を確認する

    実装済みの ID と比較する。

  3. 真偽値を返す

    対応していれば true、未対応なら false を返す。

  • 引数
引数 詳細
interfaceId 確認する4-byteの interface ID です。
  • 戻り値
戻り値 詳細
bool 対応状況です。
  • 実行条件

  • view 関数であり、 storage を更新しません。

  • 未対応 ID は revert せず false を返します。

Timing

struct Timing {
    uint32 maxAppeals;
    uint64 appealWindow;
    uint8 appealable;
    T.AppealStanding appealStanding;
    T.AppealMode appealMode;
    uint64 maxTotalDuration;
    uint64 ackWindow;
    bool allowAdvisory;
    uint64 roundDuration;
}

Timing 構造体は、 Policy の round、異議申立て、全体期限、事後承認を client がまとめて読む値です。

  • フィールド
フィールド 詳細
maxAppeals 追加できる round 数です。
appealWindow 異議申立て受付秒数です。
appealable 異議申立て対象を示す bit です。
appealStanding 異議申立てできる主体です。
appealMode appeal round が NoDecision になった時の扱いです。
maxTotalDuration admission 後の全体上限秒数です。
ackWindow POST_ACK の承認秒数です。
allowAdvisory 未承認 Issue を Advisory へ移せるかを表します。
roundDuration 各 Body window の最大値から導出した round 秒数です。

PolicyRegistered

event PolicyRegistered(bytes32 indexed policyId, bytes32 indexed family, uint32 version, address indexed by);

新しい Policy を保存する時に発行するイベントです。

  • フィールド
フィールド 詳細
policyId Policy 全体から計算した識別子です。
family Policy family です。
version family 内の版番号です。
by Policy を登録した address です。

イベントから登録者と版を検索できますが、 Body と Node の全内容は read 関数で取得します。

policyIdOf

function policyIdOf(T.PolicySpec calldata spec) external pure returns (bytes32);

PolicySpec 全体の ABI encoding から policyId を計算する関数です。

  1. Policy を符号化する

    Body、 Node、時間、 authority、 descriptor hash を含む構造体を abi.encode する。

  2. hash を計算する

    符号化結果へ keccak256 を適用する。

  3. 識別子を返す

    登録前後で同じ Policy を照合できる bytes32 を返す。

  • 引数
引数 詳細
spec 識別子を計算する Policy 全体です。
  • 戻り値
戻り値 詳細
bytes32 PolicySpec から決まる policyId です。
  • 実行条件

  • pure 関数であり、登録状態や Policy の妥当性は確認しません。

  • client は登録結果と事前計算値を照合できます。

registerPolicy

function registerPolicy(T.PolicySpec calldata spec) external returns (bytes32 policyId);

Policy の構造、 family の版、時間条件を検証して保存する関数です。

  1. Policy を検証する

    Body、 Node、 role、 window、異議申立て、全体期限を確認する。

  2. family の版を検証する

    初版では family を claim し、次版では authority、 previous == latest policyId、 version > latest version を確認する。

  3. Policy を保存する

    PolicySpec 全体と導出した roundDuration を保存し、イベントを発行する。

  • 引数
引数 詳細
spec 登録する Policy 全体です。
  • 戻り値
戻り値 詳細
policyId 保存した Policy の識別子です。
  • 実行条件

  • Body は1件以上32件以下、 Node は1件以上64件以下です。

  • member は zero 以外で重複させません。

  • tree は循環参照や Body の role 重複を作れません。

  • 次版は family の update authority だけが登録できます。

  • 次版の previous は latest policy ID と一致させ、 version は最新版より大きくします。

  • latest + 1 の連番である必要はありません。

  • 検証失敗時は Policy と family 情報を保存しません。

policyExists

function policyExists(bytes32 policyId) external view returns (bool);

指定した Policy が登録済みかを返す関数です。

  1. 識別子を受け取る

    client が確認する policyId を渡す。

  2. 登録状態を読む

    Policy Registry の存在フラグを参照する。

  3. 真偽値を返す

    登録済みなら true を返す。

  • 引数
引数 詳細
policyId 確認する Policy です。
  • 戻り値
戻り値 詳細
bool 登録状態です。
  • 実行条件

  • 未登録 ID では false を返します。

  • storage を更新しません。

familyAuthority

function familyAuthority(bytes32 family) external view returns (address);

Policy family の次版を登録できる address を返す関数です。

  1. family を指定する

    client が監査する family ID を渡す。

  2. authority を読む

    最新 Policy が指定した現在の updateAuthority を取得する。

  3. address を返す

    次版の公開主体を返す。

  • 引数
引数 詳細
family Policy family の識別子です。
  • 戻り値
戻り値 詳細
address family の update authority です。
  • 実行条件

  • 未登録 family では zero address になります。

  • 既存 Policy の内容を変更する権限ではありません。

familyLatest

function familyLatest(bytes32 family) external view returns (bytes32 policyId, uint32 version);

Policy family の最新 ID と版番号を返す関数です。

  1. family を指定する

    更新履歴を確認する family ID を渡す。

  2. 最新値を読む

    最後に登録された policyId と version を取得する。

  3. 2つの値を返す

    client が新規 Issue へ採用する候補を判断できる形で返す。

  • 引数
引数 詳細
family Policy family の識別子です。
  • 戻り値
戻り値 詳細
policyId family の最新 Policy です。
version 最新版の番号です。
  • 実行条件

  • 既存 Issue は最新値へ自動更新されません。

  • consumer は許容する正確な policyId を固定します。

roundDurationOf

function roundDurationOf(bytes32 policyId) external view returns (uint64);

Policy 内の Body window から導出した一 round の最大秒数を返す関数です。

  1. Policy を指定する

    期限を確認する policyId を渡す。

  2. 保存済み時間を読む

    登録時に計算した roundDuration を取得する。

  3. 秒数を返す

    client が round deadline を予測できる値を返す。

  • 引数
引数 詳細
policyId 対象 Policy です。
  • 戻り値
戻り値 詳細
uint64 一 round の最大秒数です。
  • 実行条件

  • 登録済み Policy を指定します。

  • 未登録 ID は実装の拒否条件に従います。

timingOf

function timingOf(bytes32 policyId) external view returns (Timing memory);

Policy の時間設定と異議申立て設定をまとめて返す関数です。

  1. Policy を指定する

    client が期限を評価する policyId を渡す。

  2. 保存値と導出値を読む

    PolicySpec の時間 field と roundDuration を取得する。

  3. 構造体を返す

    round、 appeal、 hard deadline を同じ単位で比較できる値を返す。

  • 引数
引数 詳細
policyId 対象 Policy です。
  • 戻り値
戻り値 詳細
Timing Policy の時間と異議申立て設定です。
  • 実行条件

  • 登録済み Policy を指定します。

  • Issue Registry の clock mode と合わせて読みます。

policyHeader

function policyHeader(bytes32 policyId) external view returns (bytes32 family, uint32 version, bytes32 previous, address updateAuthority, bytes32 descriptorHash);

Policy family と off-chain 説明を照合する header を返す関数です。

  1. Policy を指定する

    版と説明文を確認する policyId を渡す。

  2. header を読む

    family、 version、 previous、 authority、 descriptor hash を取得する。

  3. 5つの値を返す

    client が previous による系譜、増加する version、説明文の hash を照合できる形で返す。

  • 引数
引数 詳細
policyId 対象 Policy です。
  • 戻り値
戻り値 詳細
family Policy family です。
version family 内の版番号です。
previous 直前の policyId です。
updateAuthority 次版を登録できる address です。
descriptorHash off-chain 説明文の hash です。
  • 実行条件

  • descriptorHash は情報用で、投票判定へ使いません。

  • 実行規則は保存済み PolicySpec から読みます。

bodyCount

function bodyCount(bytes32 policyId) external view returns (uint256);

Policy に登録された Body 数を返す関数です。

  1. Policy を指定する

    読み出す policyId を渡す。

  2. 件数を読む

    保存済み bodies の長さを取得する。

  3. 件数を返す

    有効な Body 番号を判断できる値を返す。

  • 引数
引数 詳細
policyId 対象 Policy です。
  • 戻り値
戻り値 詳細
uint256 Body の件数です。
  • 実行条件

  • 登録済み Policy を指定します。

  • Body 番号は0から件数未満です。

bodyOf

function bodyOf(bytes32 policyId, uint32 body) external view returns (T.BodySpec memory);

指定した Body の投票方式、 member、閾値、 window を返す関数です。

  1. Policy と Body 番号を指定する

    client が確認する位置を渡す。

  2. Body を読む

    保存済み BodySpec を取得する。

  3. 構造体を返す

    voter eligibility と判定条件を再現できる値を返す。

  • 引数
引数 詳細
policyId 対象 Policy です。
body 0始まりの Body 番号です。
  • 戻り値
戻り値 詳細
BodySpec 指定 Body の全設定です。
  • 実行条件

  • body は bodyCount 未満にします。

  • 範囲外では revert します。

nodeCount

function nodeCount(bytes32 policyId) external view returns (uint256);

Policy に登録された Node 数を返す関数です。

  1. Policy を指定する

    読み出す policyId を渡す。

  2. 件数を読む

    保存済み nodes の長さを取得する。

  3. 件数を返す

    合成 tree の有効範囲を判断できる値を返す。

  • 引数
引数 詳細
policyId 対象 Policy です。
  • 戻り値
戻り値 詳細
uint256 Node の件数です。
  • 実行条件

  • 登録済み Policy を指定します。

  • root は Node 0です。

nodeOf

function nodeOf(bytes32 policyId, uint32 node) external view returns (T.Node memory);

指定した Node の合成規則を返す関数です。

  1. Policy と Node 番号を指定する

    client が確認する位置を渡す。

  2. Node を読む

    保存済み Node 構造体を取得する。

  3. 構造体を返す

    Body 結果から root 判断を再計算できる値を返す。

  • 引数
引数 詳細
policyId 対象 Policy です。
node 0始まりの Node 番号です。
  • 戻り値
戻り値 詳細
Node 指定 Node の全設定です。
  • 実行条件

  • node は nodeCount 未満にします。

  • 範囲外では revert します。

Issue の作成と受入れ

consumer の約束を固定する方法は3種類です。

mode Issue を作る主体 commitment が成立する時点 未承認時
CONSUMER_FILED consumer file 関数の実行時 該当しない
STANDING_ACCEPTANCE 許可された filer 登録済み条件と一致した時 Issue 作成を拒否する
POST_ACK 任意または許可された filer consumer が acknowledge 関数を呼んだ時 expire または Advisory へ進む

ERC8436でconsumer commitmentを投票前に固定する3方式

Binding Issue では、 consumer、 obligationId、 question から obligationKey を作ります。
ここでObligationは、同じ consumer が同じ question を複数 panel へ同時に出してはならない業務上の義務です。
同じ key に未完了 Issue がある間は2件目を拒否し、複数 panel へ同じ義務を出して都合のよい結果だけを選ぶ行為を防ぎます。

admission では、 Policy、 Subject、 question、 effects、 disposition、 consumer、期限を固定します。
投票開始後に standing acceptance を取り消しても進行中 Issue は止まりません。
filer による withdrawal は、まだ Filed で投票が始まっていない Issue だけに限定されます。

投票 Body と合成 Tree

名簿投票はK票の賛成で Yes、N-K+1票の反対で No になります。
どちらにも届かず window が終了すると NoDecision です。
1人の member は Issue、 round、 Body ごとに1票だけ提出できます。

署名投票も同じ票数判定を使います。
署名には round 番号が入るため、遅れて届いた transaction を次 round の票として使えません。
batch 内に1件でも無効な署名があれば transaction 全体を revert します。

固定報告者方式では、 Policy へ登録した address が Yes、 No、 NoDecision を1回だけ提出します。
window 内に提出しなければ SUBMITTER_SILENT です。

ERC8436で独立したBody判断をALL・ANY・K-of-M・VETOで合成する構造

各 Body の票は別々に数えます。
この局所的な四値判断を Body decision と呼び、 composition tree の leaf または veto 入力に使います。
技術グループと経済グループの票を1つの票箱へ混ぜず、各 Body の四値判断を tree で合成します。
同じ Body を複数の BODY Node へ置くことや、拒否権 Body を通常 Body として再利用することは禁止されます。

ProcedureとFinality

Issue は Filed から Deciding へ進み、 round 結果が決まると Provisional になります。
異議申立てがあれば Deciding へ戻り、新しい round を開始します。
異議申立てがなければ Final へ進みます。

ERC8436でProcedureState・OutcomeType・EnactmentStatusを分けて追う状態遷移

異議申立て期限は、 settle transaction の時刻ではなく、必要票がそろった時刻または window 終了時刻から計算します。
この判断内容が変わらなくなった時刻が Formation instant です。
settle を遅らせても appeal window を延長できません。

maxTotalDuration から計算した hard deadline を過ぎた後、誰でも enforceHardDeadline 関数を呼べます。
Deciding で現在の root が Pending なら、進行中 round を NoDecision(TOTAL_TIMEOUT) として閉じます。
すでに Yes または No ならその判断を保存し、 Provisional なら保存済み round を使います。
最後に appeal mode の採用規則を適用して手続きを終了します。

Procedureの Final、判断の Decided、業務実行の Enacted は別々の値です。
Final-NoDecision や、 Final-Decided で実行が Failed という組み合わせもあります。

IACDFRegistry

IACDFRegistry は、 consumer の事前登録、 Issue、投票、 round、最終結果、実行報告を保存する interface です。

StandingAcceptanceRegistered

event StandingAcceptanceRegistered(bytes32 indexed acceptanceId, address indexed consumer, bytes32 indexed policyId);

consumer が事前の受入れ条件を登録する時に発行するイベントです。

  • フィールド
フィールド 詳細
acceptanceId 事前登録の識別子です。
consumer 結果を受け入れる主体です。
policyId 受入れ対象に固定した Policy です。

このログだけでは、 Subject 制約、 question 制約、 filer 一覧、 effects、期限を復元できません。
必要な場合は、登録トランザクションの入力、オフチェーンの索引、実装固有の取得関数で補います。

StandingAcceptanceRevoked

event StandingAcceptanceRevoked(bytes32 indexed acceptanceId, address indexed consumer);

consumer が事前の受入れ条件を取り消す時に発行するイベントです。

  • フィールド
フィールド 詳細
acceptanceId 取り消した事前登録です。
consumer 取消を実行した consumer です。

取消後の新規 Issue には使えませんが、すでに admit された Issue は継続します。

IssueFiled

event IssueFiled(bytes32 indexed issueId, bytes32 indexed policyId, T.AcceptanceMode mode, address indexed filer, address consumer);

新しい Issue を受け付ける時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId Issue の識別子です。
policyId 固定した Policy です。
mode consumer commitment の成立方法です。
filer Issue 作成者です。
consumer 想定または確定した consumer です。

このログで Issue の受付主体と mode を追えますが、 Subject、 question、 effects、 obligation、各期限は getIssue 関数で読みます。

IssueAdmitted

event IssueAdmitted(bytes32 indexed issueId, T.EffectClass effectClass, address indexed consumer, uint64 admittedAt, uint64 hardDeadline);

Issue を投票手続きへ入れ、 round 1を開始する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId admission した Issue です。
effectClass Advisory または Binding です。
consumer Binding 時の consumer です。
admittedAt 開始時刻です。
hardDeadline 手続き全体の終了上限です。

このログで投票開始と全体上限を追えますが、 round 1の deadline、 Body 設定、 Node tree は getRound 関数と Policy Registry の read API で補います。

IssueAcknowledged

event IssueAcknowledged(bytes32 indexed issueId, address indexed consumer);

POST_ACK の Issue を consumer が承認する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 承認した Issue です。
consumer effects と obligation を固定した主体です。

このログで誰が承認したかを追えますが、確定した effects、 disposition、 obligation、 consumer deadline は getIssue 関数で読みます。

IssueWithdrawn

event IssueWithdrawn(bytes32 indexed issueId, T.Reason reason);

投票前の取消または未承認期限切れで Issue を終了する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 終了した Issue です。
reason NONE または NO_ACCEPTANCE です。

このログで取消または未承認終了を追えますが、取消前に固定されていた Subject、 question、 consumer 提案値は getIssue 関数で補います。

BallotCast

event BallotCast(bytes32 indexed issueId, uint32 indexed round, uint32 indexed body, address voter, bool approve, bool signed);

1票を受理して Body の票数を更新する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 投票対象の Issue です。
round 1始まりの round 番号です。
body Body 番号です。
voter 投票者です。
approve 賛成なら true です。
signed relayer が署名票を提出した場合に true です。

このログで各 voter の受理票を検索できますが、 Body の累計票、 threshold 到達時刻、他 member の未投票状態は getBodyState 関数と bodyStatus 関数で読みます。

BodyResultSubmitted

event BodyResultSubmitted(bytes32 indexed issueId, uint32 indexed round, uint32 indexed body, address submitter, T.NodeStatus status);

固定報告者が Body 結果を提出する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 対象 Issue です。
round 対象 round です。
body 対象 Body です。
submitter Policy へ固定された報告者です。
status Yes、 No、 NoDecision の提出値です。

このログで報告者の提出を追えますが、提出済み flag、形成時刻、合成後の root 判断は getBodyState 関数と nodeStatus 関数で補います。

RoundSettled

event RoundSettled(bytes32 indexed issueId, uint32 indexed round, T.NodeStatus status, T.Reason reason, uint64 at, uint64 appealOpenUntil);

root Node の値を round 結果として保存する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 対象 Issue です。
round settle した round です。
status root の四値判断です。
reason NoDecision や VETO の理由です。
at 値が形成された時刻です。
appealOpenUntil 異議申立て期限です。

このログで settle 結果を追えますが、 round 開始・終了時刻、 Issue の現在 state、以前の round 履歴は getRound 関数と getIssue 関数で読みます。

Appealed

event Appealed(bytes32 indexed issueId, uint32 indexed newRound, address indexed by);

異議申立てを受け付け、新しい round を開始する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 対象 Issue です。
newRound 新しく開始した round 番号です。
by 異議申立てを送った address です。

このログで新 round の開始を追えますが、新 round の deadline、各 Body の票数、以前の判断を採用するかは getRound 関数、 getBodyState 関数、 getResult 関数で補います。

Finalized

event Finalized(bytes32 indexed issueId, T.OutcomeType outcomeType, bool outcomeYes, T.Reason reason, uint32 sourceRound);

Issue の最終結果を保存する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId Final になった Issue です。
outcomeType Decided または NoDecision です。
outcomeYes Decided 時の Yes または No です。
reason NoDecision または採用判断の理由です。
sourceRound 採用した判断を作った round です。

このログで最終判断と採用 round を追えますが、 consumer、 effects、 final 時刻、以前の round を採用した flag は getResult 関数で読みます。

EnactmentRecorded

event EnactmentRecorded(bytes32 indexed issueId, address indexed consumer, bytes32 indexed effectId, T.EnactmentStatus status, address reporter, bytes32 ref);

consumer が業務 effect の実行結果を報告する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 元の Issue です。
consumer effect を実行した主体です。
effectId Yes または No の effect 参照です。
status Enacted または Failed です。
reporter 報告 transaction の送信者です。
ref 外部実行を参照する値です。

このログで consumer の実行報告を追えますが、業務コントラクト側の現在ステートや実行 transaction の成功内容は registry だけでは復元できず、 getEnactment 関数と target 側の記録を照合します。

Evidence

event Evidence(bytes32 indexed issueId, address indexed party, string evidenceURI);

Issue へ証拠 URI を添付する時に発行するイベントです。

  • フィールド
フィールド 詳細
issueId 証拠を関連付ける Issue です。
party URI を提出した address です。
evidenceURI 証拠を参照する文字列です。

registry は URI の内容や真実性を検証しません。

supportsInterface

function supportsInterface(bytes4 interfaceId) external view returns (bool);

Issue Registry が指定 interface へ対応するかを返す関数です。

  1. ID を受け取る

    client が必要な interface ID を渡す。

  2. 対応状況を確認する

    実装済み ID と比較する。

  3. 真偽値を返す

    対応時は true を返す。

  • 引数
引数 詳細
interfaceId 確認する4-byte ID です。
  • 戻り値
戻り値 詳細
bool 対応状況です。
  • 実行条件

  • view 関数で storage を更新しません。

  • 未対応 ID では false を返します。

policies

function policies() external view returns (IACDFPolicyRegistry);

Issue Registry が参照する Policy Registry を返す関数です。

  1. 保存済み address を読む

    デプロイ時に固定した Policy Registry を取得する。

  2. interface 型で返す

    client が Policy 内容を読み出せる値を返す。

  • 戻り値
戻り値 詳細
IACDFPolicyRegistry 対応する Policy Registry です。
  • 実行条件

  • view 関数で storage を更新しません。

  • client は返された registry のERC165対応も確認します。

registerStandingAcceptance

function registerStandingAcceptance(T.StandingAcceptanceInput calldata input) external returns (bytes32 acceptanceId);

consumer が将来の Issue へ適用する受入れ条件を事前登録する関数です。

  1. 条件を検証する

    登録済み Policy と、 zero ではない2つの effect ID を確認する。

  2. consumer を固定する

    msg.sender を consumer として acceptance を保存する。

  3. 識別子を返す

    filer が file 関数で参照する acceptanceId を返す。

  • 引数
引数 詳細
input Policy、対象、質問、 filer、 effects、義務、期限です。
  • 戻り値
戻り値 詳細
acceptanceId 保存した事前登録の識別子です。
  • 実行条件

  • consumer 自身が呼び出します。

  • 登録済みの正確な policyId を指定します。

  • 未登録 policyId または zero effect を拒否します。

  • 期限と filer 一覧は、 file 関数が事前登録を使用する時に照合します。

revokeStandingAcceptance

function revokeStandingAcceptance(bytes32 acceptanceId) external;

consumer が未使用の将来分を含む事前登録を取り消す関数です。

  1. 登録を読む

    acceptanceId に対応する consumer を取得する。

  2. caller を確認する

    登録した consumer と msg.sender を比較する。

  3. 取消を保存する

    revoked 状態を記録してイベントを発行する。

  • 引数
引数 詳細
acceptanceId 取り消す事前登録です。
  • 実行条件

  • 登録した consumer だけが呼び出せます。

  • 取消前に admit された Issue は継続します。

  • 不明 ID は、保存済み consumer と一致しないため拒否します。

  • 同じ consumer による再取消は許可され、 revoked を再び保存してイベントを発行します。

file

function file(T.IssueInput calldata input) external returns (bytes32 issueId);

判断対象と consumer commitment を受け取り、新しい Issue を作る関数です。

  1. mode 別に入力を検証する

    consumer 自身の作成、事前登録、事後承認のどれを使うか確認する。

  2. Issue を保存する

    Subject、 question、 Policy、提案または確定済み effects を保存する。

  3. 開始または承認待ちにする

    commitment 成立済みなら admit し、 POST_ACK なら Filed で待つ。

  • 引数
引数 詳細
input Policy、 mode、 consumer、 Subject、 question、 effects、義務、期限です。
  • 戻り値
戻り値 詳細
issueId 作成した Issue の識別子です。
  • 実行条件

  • Policy は登録済みである必要があります。

  • CONSUMER_FILED では consumer と caller を一致させます。

  • STANDING_ACCEPTANCE では target、 question、 filer、期限を登録条件と照合します。

  • Binding Issue では同じ obligationKey の進行中 Issue を拒否します。

acknowledge

function acknowledge(bytes32 issueId, bytes32 effectYes, bytes32 effectNo, bytes32 disposition, bytes32 obligationId, uint64 consumerDeadline) external;

consumer が POST_ACK Issue の effects と義務を投票前に固定する関数です。

  1. Issue と期限を確認する

    Filed、 POST_ACK、 ack window 内であることを確認する。

  2. consumer の値を固定する

    caller、 effects、 disposition、 obligation、 consumer deadline を保存する。

  3. round 1を開始する

    Binding として admit し、 hard deadline を計算する。

  • 引数
引数 詳細
issueId 承認する Issue です。
effectYes Yes 時の effect です。
effectNo No 時の effect です。
disposition NoDecision 時の処理です。
obligationId consumer が固定する義務範囲です。
consumerDeadline consumer 側の実行期限です。
  • 実行条件

  • Issue に指定された consumer だけが呼び出せます。

  • ack window を過ぎた呼び出しを拒否します。

  • 同じ obligation の進行中 Issue があれば拒否します。

admitAdvisory

function admitAdvisory(bytes32 issueId) external;

未承認の POST_ACK Issue を業務拘束のない Advisory として開始する関数です。

  1. Policy 設定を確認する

    allowAdvisory が有効かを読む。

  2. Issue 状態を確認する

    Filed のまま ack window を過ぎたことを確認する。

  3. Advisory で開始する

    consumer effects を拘束せず round 1を開く。

  • 引数
引数 詳細
issueId Advisory として開始する Issue です。
  • 実行条件

  • Policy が Advisory を許可していることを確認します。

  • ack window 終了後は誰でも呼び出せます。

  • すでに acknowledge、 withdraw、 admit された Issue を拒否します。

  • Advisory は active obligation を作りません。

expireUnacknowledged

function expireUnacknowledged(bytes32 issueId) external;

ack window を過ぎた未承認 Issue を終了する関数です。

  1. Issue を確認する

    Filed の POST_ACK であることを確認する。

  2. 期限を確認する

    ack window が終了したかを clock で判定する。

  3. Withdrawn へ移す

    NO_ACCEPTANCE を記録して終了イベントを発行する。

  • 引数
引数 詳細
issueId 期限切れにする Issue です。
  • 実行条件

  • 誰でも呼び出せます。

  • 期限前または承認済みの Issue を拒否します。

  • Policy の allowAdvisory が false の場合だけ成功します。

  • allowAdvisory が true なら、期限後に admitAdvisory 関数を使います。

withdraw

function withdraw(bytes32 issueId) external;

filer が投票開始前の Issue を取り消す関数です。

  1. caller を確認する

    Issue の filer と msg.sender を比較する。

  2. 状態を確認する

    Issue が Filed であることを確認する。

  3. 取消を保存する

    Withdrawn へ移してイベントを発行する。

  • 引数
引数 詳細
issueId 取り消す Issue です。
  • 実行条件

  • filer だけが呼び出せます。

  • admission 後の Issue は取り消せません。

submitEvidence

function submitEvidence(bytes32 issueId, string calldata evidenceURI) external;

Issue へ証拠を参照する URI をイベントとして添付する関数です。

  1. Issue 状態を確認する

    Filed、 Deciding、 Provisional のいずれかであることを確認する。

  2. URI を受け取る

    caller が指定した証拠参照を受け取る。

  3. イベントを発行する

    Issue、 caller、 URI をイベントログへ記録する。

  • 引数
引数 詳細
issueId 証拠を関連付ける Issue です。
evidenceURI off-chain 証拠を参照する文字列です。
  • 実行条件

  • Final または Withdrawn では受け付けません。

  • registry は URI の内容を取得、検証、実行しません。

castBallot

function castBallot(bytes32 issueId, uint32 round, uint32 body, bool approve) external;

voter 自身が1票をオンチェーンで提出する関数です。

  1. 対象と資格を確認する

    Issue、現在 round、 Body、 member、 window を確認する。

  2. 重複を確認する

    同じ voter が同じ Issue、 round、 Body へ未投票かを確認する。

  3. 票数を更新する

    賛成または反対の count と投票済み key を保存し、イベントを発行する。

  • 引数
引数 詳細
issueId 投票対象の Issue です。
round 1始まりの round 番号です。
body Policy 内の Body 番号です。
approve 賛成なら true、反対なら false です。
  • 実行条件

  • Body は ON_CHAIN_TALLY である必要があります。

  • caller は Body member に含まれる必要があります。

  • 期限後、重複投票、確定済み Body を拒否します。

submitSignedBallots

function submitSignedBallots(bytes32 issueId, uint32 body, address[] calldata voters, bool[] calldata approves, bytes[] calldata signatures) external;

複数のEIP712署名票を任意の relayer がまとめて提出する関数です。

  1. 配列と現在 round を確認する

    三配列の長さ、 Issue 状態、 Body 方式、 window を確認する。

  2. 各署名票を検証する

    voter、 roster、重複、 digest、 EOA またはERC1271署名を確認する。

  3. batch 全体を保存する

    全件が有効な場合だけ各票を count し、イベントを発行する。

  • 引数
引数 詳細
issueId 投票対象の Issue です。
body 署名投票を受け付ける Body です。
voters 各署名者です。
approves 各署名者の賛否です。
signatures 各EIP712署名です。
  • 実行条件

  • Body は SIGNED_BALLOTS である必要があります。

  • 署名 digest には現在 round が入ります。

  • 1件でも無効なら batch 全体を revert し、票数を変更しません。

submitBodyResult

function submitBodyResult(bytes32 issueId, uint32 round, uint32 body, T.NodeStatus status) external;

固定報告者が Body の Yes、 No、 NoDecision を提出する関数です。

  1. 報告者を確認する

    Policy の Body member と caller を比較する。

  2. round と window を確認する

    現在 round、未提出、期限内であることを確認する。

  3. 結果を保存する

    status、提出済み flag、時刻を保存してイベントを発行する。

  • 引数
引数 詳細
issueId 対象 Issue です。
round 対象 round です。
body 固定報告者方式の Body です。
status Yes、 No、 NoDecision です。
  • 実行条件

  • Body は AUTHORIZED_SUBMITTER 方式である必要があります。

  • Pending の提出、再提出、期限後提出を拒否します。

settleRound

function settleRound(bytes32 issueId) external;

現在 round の root Node を評価し、 round 結果を保存する関数です。

  1. root status を計算する

    Body の票数、提出値、 window と合成 tree を評価する。

  2. formation instant を計算する

    結果が変わらなくなった時刻を票または期限から求める。

  3. 状態を進める

    appeal 可能なら Provisional、対象外なら Final へ移す。

  • 引数
引数 詳細
issueId settle する Issue です。
  • 実行条件

  • 誰でも呼び出せます。

  • Deciding で root が Pending ではないことを確認します。

  • settle 済み round を拒否します。

appeal

function appeal(bytes32 issueId) external;

Provisional 結果へ異議を申し立て、新しい round を開始する関数です。

  1. standing と期限を確認する

    caller、 appeal window、対象 outcome、最大回数を確認する。

  2. 前判断の扱いを記録する

    Policy の appeal mode に従い、後の採用候補を保持または失効させる。

  3. 新しい round を開く

    round count を増やし、開始時刻と deadline を保存する。

  • 引数
引数 詳細
issueId 異議申立て対象の Issue です。
  • 実行条件

  • Issue は Provisional である必要があります。

  • standing が限定される Policy では consumer または filer だけが呼び出せます。

  • appeal window 後、最大回数到達後、 hard deadline 超過後を拒否します。

finalize

function finalize(bytes32 issueId) external;

異議申立て期間が終了した Provisional Issue を Final へ移す関数です。

  1. 期限を確認する

    appeal window が終了したかを clock で判定する。

  2. 採用 round を決める

    最終 round と appeal mode から採用する Yes、 No、 NoDecision を選ぶ。

  3. Final 結果を保存する

    outcome、 reason、 source round、時刻を保存して obligation を解放する。

  • 引数
引数 詳細
issueId Final にする Issue です。
  • 実行条件

  • 誰でも呼び出せます。

  • Issue は Provisional である必要があります。

  • appeal window 終了前は拒否します。

enforceHardDeadline

function enforceHardDeadline(bytes32 issueId) external;

手続き全体の上限時刻を過ぎた Issue を、現在までに形成された判断と appeal mode に基づいて Final へ移す関数です。

  1. hard deadline を確認する

    現在 clock が保存済み上限を過ぎたかを判定する。

  2. 進行中 round の結果を保存する

    Deciding なら現在の root を読み、 Yes または No はその判断を保存する。
    Pending の場合だけ NoDecision(TOTAL_TIMEOUT) を保存する。
    Provisional なら保存済み round をそのまま使う。

  3. Issue を Final にする

    appeal mode の採用規則で Decided または NoDecision を選び、 active obligation を解放する。

  • 引数
引数 詳細
issueId 強制終了する Issue です。
  • 実行条件

  • 誰でも呼び出せます。

  • hard deadline 以前は拒否します。

  • Final または Withdrawn では実行できません。

recordEnactment

function recordEnactment(bytes32 issueId, bytes32 effectId, T.EnactmentStatus status, bytes32 ref) external;

consumer が Final decision を業務コントラクトへ反映した結果を記録する関数です。

  1. Issue と consumer を確認する

    Binding かつ Final であり、 caller が consumer かを確認する。

  2. effect と status を確認する

    登録済みの effectYes または effectNo と一致する effect ID と、 Enacted または Failed を確認する。

  3. 実行報告を保存する

    status、 reporter、 ref、時刻を保存してイベントを発行する。

  • 引数
引数 詳細
issueId 元の Issue です。
effectId 実行した Yes または No effect です。
status Enacted または Failed です。
ref 外部 transaction や業務記録の参照です。
  • 実行条件

  • consumer だけが呼び出せます。

  • Advisory では記録できません。

  • Issue Registry は outcomeType と effect ID の対応を検証せず、 Final-NoDecision も一律には拒否しません。

  • 採用結果に対応する effect ID の選択と、 NoDecision 時に disposition を適用する処理は consumer が担います。

  • Failed から Enacted へ更新できますが、 Enacted 後の変更は拒否します。

getResult

function getResult(bytes32 issueId) external view returns (T.Result memory);

Issue の現在状態と、 Final 後の判断・ effects を返す関数です。

  1. Issue を読む

    保存済み state、 outcome、 consumer、 effects を取得する。

  2. 採用情報を組み立てる

    source round と以前の round 採用 flag を計算する。

  3. Result を返す

    client が effect 実行条件を判定できる構造体を返す。

  • 引数
引数 詳細
issueId 読み出す Issue です。
  • 戻り値
戻り値 詳細
Result procedure、 outcome、 Policy、 consumer、 effects です。
  • 実行条件

  • view 関数で storage を更新しません。

  • Final 前は outcomeType が None です。

getIssue

function getIssue(bytes32 issueId) external view returns (T.Issue memory);

Issue へ固定した全値と現在状態を返す関数です。

  1. 識別子を受け取る

    client が確認する Issue を指定する。

  2. 保存値を読む

    Subject、 question、 Policy、 consumer、期限、結果を取得する。

  3. Issue を返す

    admission 時の固定値と現在状態を同じ構造体で返す。

  • 引数
引数 詳細
issueId 読み出す Issue です。
  • 戻り値
戻り値 詳細
Issue Issue の保存内容です。
  • 実行条件

  • view 関数で storage を更新しません。

  • 不明 ID の扱いは実装の read 規則に従います。

getRound

function getRound(bytes32 issueId, uint32 round) external view returns (T.RoundState memory);

指定 round の開始、期限、 settle 結果、異議申立て期限を返す関数です。

  1. Issue と round を指定する

    履歴を確認する位置を渡す。

  2. RoundState を読む

    保存済み時刻、 status、 reason を取得する。

  3. 構造体を返す

    client が形成時刻と settle 時刻を混同せず確認できる値を返す。

  • 引数
引数 詳細
issueId 対象 Issue です。
round 1始まりの round 番号です。
  • 戻り値
戻り値 詳細
RoundState 指定 round の保存内容です。
  • 実行条件

  • 作成済み round を指定します。

  • view 関数で storage を更新しません。

getBodyState

function getBodyState(bytes32 issueId, uint32 round, uint32 body) external view returns (T.BodyState memory);

指定 Body の票数または固定報告者の提出値を返す関数です。

  1. 位置を指定する

    Issue、 round、 Body 番号を渡す。

  2. 保存値を読む

    yes、 no、提出 flag、提出 status、決定時刻を取得する。

  3. 構造体を返す

    client が Body 判断を再計算できる値を返す。

  • 引数
引数 詳細
issueId 対象 Issue です。
round 対象 round です。
body 対象 Body です。
  • 戻り値
戻り値 詳細
BodyState Body の保存内容です。
  • 実行条件

  • 有効な round と Body 番号を指定します。

  • view 関数で storage を更新しません。

bodyStatus

function bodyStatus(bytes32 issueId, uint32 round, uint32 body) external view returns (T.NodeStatus status, T.Reason reason, uint64 at);

保存票と clock から Body の現在判断を計算する関数です。

  1. Body 入力を読む

    票数または固定報告者の提出値と window を取得する。

  2. 四値を判定する

    threshold、 blocking threshold、期限切れを評価する。

  3. 判断を返す

    status、 reason、値が形成された時刻を返す。

  • 引数
引数 詳細
issueId 対象 Issue です。
round 対象 round です。
body 対象 Body です。
  • 戻り値
戻り値 詳細
status Pending、 Yes、 No、 NoDecision です。
reason NoDecision または VETO の理由です。
at 値が形成された時刻です。
  • 実行条件

  • view 関数で storage を更新しません。

  • 期限前で threshold 未達なら Pending です。

nodeStatus

function nodeStatus(bytes32 issueId, uint32 round, uint32 node) external view returns (T.NodeStatus status, T.Reason reason, uint64 at);

指定 Node から下の Body と子 Node を評価し、現在の合成判断を返す関数です。

  1. Node を読む

    Policy から combinator と参照先を取得する。

  2. 子の四値を合成する

    ALL、 ANY、 K-of-M、 VETO の規則を適用する。

  3. 判断を返す

    status、 reason、 formation instant を返す。

  • 引数
引数 詳細
issueId 対象 Issue です。
round 対象 round です。
node 対象 Node です。
  • 戻り値
戻り値 詳細
status 合成後の四値判断です。
reason 合成結果の理由です。
at 合成値が形成された時刻です。
  • 実行条件

  • 有効な Node 番号を指定します。

  • view 関数で storage を更新しません。

hasVoted

function hasVoted(bytes32 issueId, uint32 round, uint32 body, address voter) external view returns (bool);

指定 voter が同じ Issue、 round、 Body へ投票済みかを返す関数です。

  1. 1票 key を作る

    Issue、 round、 Body、 voter を組み合わせる。

  2. 保存 flag を読む

    受理済み投票の mapping を参照する。

  3. 真偽値を返す

    投票済みなら true を返す。

  • 引数
引数 詳細
issueId 対象 Issue です。
round 対象 round です。
body 対象 Body です。
voter 確認する address です。
  • 戻り値
戻り値 詳細
bool 投票済みかを表します。
  • 実行条件

  • 受理済み票だけが true になります。

  • view 関数で storage を更新しません。

activeIssueOf

function activeIssueOf(address consumer, bytes32 obligationKey) external view returns (bytes32);

consumer の同じ義務について、最後に保存した Binding Issue を返す関数です。

  1. consumer と key を指定する

    並行 Issue を確認する組を渡す。

  2. active mapping を読む

    admission で保存し、後続の admission まで残る値を取得する。

  3. Issue ID を返す

    未登録なら zero、登録後は終端済みを含む最後の Issue ID を返す。

返却された Issue が Final または Withdrawn なら排他制約は解放済みで、後続の admission が新しい Issue ID へ上書きします。
呼出側は返却値だけで進行中と判断せず、 getIssue 関数で state も確認します。

  • 引数
引数 詳細
consumer 結果を実行する主体です。
obligationKey 義務と question から作る key です。
  • 戻り値
戻り値 詳細
bytes32 終端済みを含む最後の Binding Issue ID です。未登録なら zero です。
  • 実行条件

  • Advisory Issue は active mapping へ入りません。

  • view 関数で storage を更新しません。

obligationKeyOf

function obligationKeyOf(address consumer, bytes32 obligationId, bytes32 question) external pure returns (bytes32);

同じ consumer の同じ質問に対する義務を識別する key を計算する関数です。

  1. 3つの値を符号化する

    consumer、 obligation ID、 question を abi.encode する。

  2. hash を計算する

    符号化結果へ keccak256 を適用する。

  3. key を返す

    activeIssueOf で使う bytes32 を返す。

  • 引数
引数 詳細
consumer 結果を実行する主体です。
obligationId consumer が固定した義務範囲です。
question 判定する質問の hash です。
  • 戻り値
戻り値 詳細
bytes32 obligation key です。
  • 実行条件

  • pure 関数で storage を読み書きしません。

  • Binding Issue では zero 以外の obligationId を使います。

getEnactment

function getEnactment(bytes32 issueId, address consumer, bytes32 effectId) external view returns (T.Enactment memory);

consumer が報告した effect 実行結果を返す関数です。

  1. 検索 key を指定する

    Issue、 consumer、 effect ID を渡す。

  2. 実行記録を読む

    status、 reporter、 ref、時刻を取得する。

  3. Enactment を返す

    実行成功、失敗、未報告を区別できる構造体を返す。

  • 引数
引数 詳細
issueId 元の Issue です。
consumer effect を実行する主体です。
effectId Yes または No の effect です。
  • 戻り値
戻り値 詳細
Enactment 実行報告の保存内容です。
  • 実行条件

  • 記録がなければ NotEnacted 相当を返します。

  • view 関数で storage を更新しません。

ballotDigest

function ballotDigest(bytes32 issueId, uint32 round, uint32 body, address voter, bool approve) external view returns (bytes32);

署名投票で wallet と registry が共有するEIP712 digest を返す関数です。

  1. ballot field を符号化する

    Issue、 round、 Body、 voter、賛否を型付き data へ入れる。

  2. domain を加える

    chain ID と Issue Registry address を domain へ入れる。

  3. digest を返す

    voter が署名し、 registry が検証する32-byte値を返す。

  • 引数
引数 詳細
issueId 投票対象の Issue です。
round 署名対象 round です。
body 署名対象 Body です。
voter 署名者です。
approve 賛成または反対です。
  • 戻り値
戻り値 詳細
bytes32 EIP712署名 digest です。
  • 実行条件

  • view 関数で registry address と chain ID を読みます。

  • 署名の有効性や投票資格はこの関数だけでは判定しません。

clock

function clock() external view returns (uint48);

Issue Registry が期限計算に使う現在値を返す関数です。

  1. clock source を読む

    実装が採用した timestamp または block 数を取得する。

  2. 48-bit値へ変換する

    ERC6372互換の型で表す。

  3. 現在値を返す

    client が deadline と比較できる値を返す。

  • 戻り値
戻り値 詳細
uint48 現在の contract clock です。
  • 実行条件

  • view 関数で storage を更新しません。

  • 単位は CLOCK_MODE 関数で確認します。

CLOCK_MODE

function CLOCK_MODE() external view returns (string memory);

clock 関数の単位と mode を文字列で返す関数です。

  1. mode を読む

    実装が固定した clock mode を取得する。

  2. 文字列を返す

    client が deadline を同じ単位で比較できる形式を返す。

  • 戻り値
戻り値 詳細
string ERC6372の clock mode です。
  • 実行条件

  • 参考実装は timestamp 秒を使います。

  • block 数と秒を暗黙に変換しません。

期限計算に使う clock 値と単位を公開する interface がERC6372です。
client は clock 関数と CLOCK_MODE 関数を読み、 Body window、 appeal window、 hard deadline を同じ単位で比較します。

ERC6372については以下の記事を参考にしてください。

Errors

規格は、拒否原因を5つの分類に分け、 reason string または同等の custom error で区別することを推奨します。
文字列そのものは相互運用要件ではありません。
参考実装の revert strings は、これらの分類を追跡するための実装上の手掛かりです。

分類 拒否する例 失敗時
Policy validation Body 数超過、 member 重複、循環 tree、時間上限不足 Policy を保存しない
Issues and acceptance 未登録 Policy、不一致な事前登録、期限切れ、 active obligation Issue を admit しない
Voting 非 member、重複票、期限後、誤 round、無効署名 票数を変更しない
Rounds and finality root が Pending、 appeal 期限外、最大回数超過、 hard deadline 前 procedure state を変更しない
Enactment Final 前、 NoDecision、誤 consumer、誤 effect、 Enacted 後の更新 実行記録を変更しない

どの分類でも revert した transaction の storage 更新とイベント発行はすべて戻ります。

ClockとProfile

最小 profile は、固定 roster の K-of-N 投票、オンチェーン集計、1つの Body、異議申立てなしです。
署名投票、固定報告者、複数 Body の合成、 VETO、異議申立ては任意 profile として追加できます。

実装は、対応 interface ID、 Policy 内の Body 方式、 clock mode を公開します。
client はこれらを読み、必要な profile へ対応できない registry や Policy を採用候補から外します。

参加資格の算出、ランダム選出、重み付き投票、二値以外の結果、手数料、 executor、 privacy、 multi-consumer、 cross-chain receipt の検証は予約領域です。
ERC8436の kernel は、これらを実装済みとして扱いません。

補足

実行する規則そのものを hash 化する理由

Policy Registry は、説明用ファイルの hash だけでなく、実際に実行する PolicySpec 全体から policyId を計算します。
参加者、閾値、合成 tree、時間、異議申立てのどれかが変われば、別の policyId になります。

コントラクト address や code hash だけでは、 Upgradeable proxy の実装差し替えや、外部 storage にある閾値変更を検出できません。
ERC8436の参考実装は non-upgradeable で、実行 parameters を Policy Registry へ保存します。

プロキシの implementation、 beacon、 admin を標準 slot から読む方法がERC1967です。
デプロイ監査では、 slot だけでなく、実装を交換できる authority と delegatecall 経路も確認します。

ERC1967については以下の記事を参考にしてください。

subject と obligation を分ける理由

Subject は何を評価するかを表します。
一方、 obligation は consumer のどの業務上の義務について、同時に1件だけ Binding Issue を許すかを表します。

成果物ごとに nonce を変えて obligation を毎回別物にすると、同じ質問を複数 panel へ出せます。
反対に、全作業へ同じ obligation を割り当てると、独立した成果物の判定まで止まります。
adapter は作業コントラクトの task ID、 submission ID、 version から安定した範囲を作ります。

四値合成と appeal mode

Yes と No だけで合成すると、入力受付中と判断不能を区別できません。
ERC8436は Pending、 Yes、 No、 NoDecision を保ったまま、 ALL、 ANY、 K-of-M、 VETO を評価します。

異議申立て後も同じ区別を使います。
PRESERVE_UNLESS_OVERTURNED では、 appeal round の NoDecision が前判断を消しません。
REQUIRE_FRESH_DECISION では、 appeal 開始時に前判断を採用候補から外すため、新 round の NoDecision が Issue 全体の NoDecision になります。

decision と execution の境界

Final decision は、 consumer へ新しい authority を与えません。
consumer は、元から操作できる業務コントラクトの current state と期限を確認し、自分の権限で effect を呼び出します。

effect call が失敗しても、 Issue Registry の decision は変更しません。
Failed enactment を記録し、再試行、既定処理、手動回復のどれを使うかは consumer 側の規則で決めます。

規格が予約する拡張

agent identity や reputation は、 roster 候補を選ぶ入力にできます。
ERC8004は agent identity と trust signal を表しますが、ERC8436の投票資格や consumer authority を自動で作りません。

ERC8004については以下の記事を参考にしてください。

weighted vote、 sortition、 non-binary outcome、 privacy、 fee、 executor、 cross-chain result carriage を追加する場合は、 Policy ID へ影響する parameters と検証規則を別 profile で固定します。

テストケース

公式 vector で固定する計算

公式 assets には、3種類の Policy ID、 ballot digest、83行の K-of-N 判定、192行の composition 判定、2つの interface ID があります。
実装者は、自分の ABI encoding、 EIP712 domain、四値合成、ERC165 ID を公式値と比較できます。

vector 確認する内容
policy-id.json PolicySpec 全体から同じ policyId を作れるか
ballot-digest.json chain、 registry、 Issue、 round、 Body、 voter、賛否から同じ digest を作れるか
kofn-tally.json Yes、 No、 Pending、 NoDecision の閾値境界
composition.json ALL、 ANY、2-of-3の四値合成
interface-ids.json 二 interface の selector XOR

integration evidence と非保証範囲

公式 discussion の Revision 3では、現行の Body role validation、 VETO と ALL の合成、オンチェーン投票、 reject path を Sepolia で実行した記録があります。
参考実装の Issue Registry は IR pipeline で23,514 bytes となり、EIP170の24,576-byte上限内ですが余裕は1,062 bytes です。

EIP170については以下の記事を参考にしてください。

これらは encoding と確認済み経路の証拠です。
第三者による本番採用、全 profile の public network 検証、 executor の稼働、 roster の独立性、投票内容の真実性までは保証しません。

互換性

既存 contract への adapter 接続

単一 evaluator や arbitrator address を受け取る既存コントラクトは、その address へERC8436 adapter を指定できます。
adapter が Issue を開き、 Final 結果を既存の accept または reject API へ変換するため、既存コントラクトの interface を変更せず接続できます。

ERC8183は、 client、 provider、 evaluator が作業を進める agent commerce の状態を定めます。
その単一 evaluator slot へ adapter を置くと、複数 Body の decision を1つの評価結果へ変換できます。
この読み替えにより、対応 API を選びます。

ERC8183については以下の記事を参考にしてください。

関連規格から読む値

client はERC165 ID から対応 API、 EIP712 domain から署名対象、ERC1271 magic value から contract voter の署名、ERC6372 clock mode から期限単位を確認します。
これらを1つでも暗黙値にすると、別 registry 向け署名や異なる時間単位を誤って受理します。

ERC8033は、特定 query へ情報 agent と judge を配置する council oracle を定めます。
複数の異なる Body、 consumer commitment、 NoDecision、異議申立てを1つの procedure record へ固定する用途ではERC8436を使い分けます。

Draft と policy version の固定

ERC8436は Draft であり、 interface や Policy layout が変わる可能性があります。
consumer は**family の最新版**だけを条件にせず、監査した registry instance と正確な policyId を固定します。

新しい Policy version を登録しても、既存 Issue と standing acceptance は自動移行しません。
移行時は、新旧 Policy の Body、 Node、 timing、 appeal mode を比較し、 consumer が新しい policyId を明示的に受け入れます。

参考実装

2つの registry は decision record を保存し、 consumer adapter だけが業務コントラクトへ effect を呼び出します。

ERC8436のPolicy Registry・Issue Registry・consumer adapter・業務コントラクトの責任境界

二 registry の保存・評価境界

ACDFPolicyRegistry コントラクトは、検証済み PolicySpec と family version を保存します。
ACDFRegistry コントラクトは、その正確な Policy を参照して Issue、 round、 ballot、 result、 enactment を保存します。
後者の保存領域を、以下では ACDFRegistry acceptance and issue mappings として整理します。

署名検証の境界

SignatureChecker ライブラリは、ERC1271と canonical ECDSA の署名妥当性だけを返します。
member 資格、現在 round、期限、重複票は ACDFRegistry コントラクトが別に確認します。

consumer adapter の実行境界

ACDFTaskTenderAdapter コントラクトは、 Final-Decided を作業コントラクトの accept または reject へ変換します。
target call の失敗を catch して Failed を記録しますが、 decision は変更しません。

off-chain JSON Schema と authoritative registry の境界

公式の JSON Schema は、 Policy document と procedure receipt を off-chain tool 間で交換する形式です。
receipt は Filed、 Deciding、 Provisional、 Final、 Withdrawn を運べ、 Final 前は outcomeType が None になります。
effect 実行には別途 Final を確認します。

receipt は chain ID、 Policy Registry、 Issue Registry、 Issue ID を持ちます。
named registry の record を照合する carrier であり、 receipt 自体がオンチェーンの正本になるわけではありません。

ACDFPolicyRegistry

policy の検証と保存

ACDFPolicyRegistry._validate は、 Body 数、 Node 数、 member、K値、 window、 tree、 Body role、 appeal、 duration を確認します。
全検証が終わった後だけ、 _store 関数が Policy 全体、存在 flag、 round duration、 family authority、 latest version を保存します。
この入口と保存処理をまとめた経路が ACDFPolicyRegistry.registerPolicy/_store です。

次版では caller、 previous、 version を現在の family 情報と照合します。
途中で revert すると、どの mapping も更新されません。
検証条件は規格要件ですが、 private helper の分け方と mapping layout は参考実装固有です。

policy の読み取り

policyHeader、 timingOf、 bodyOf、 nodeOf の各関数から、実行に使う全 parameters を復元できます。
off-chain descriptor は descriptorHash で結び付きますが、投票判定へは使いません。

ACDFRegistry

issue の admission

file、 acknowledge、 admitAdvisory の各関数は、 acceptance mode に応じて consumer values を選びます。
_admit 関数は Policy、 active obligation、 consumer deadline、 hard deadline を確認し、 Issue と round 1を同じ transaction で保存します。
参考実装の file/acknowledge/_admit 経路では、受付方法が違っても同じ admission 検証へ合流します。

Issue ID を nonce から作る方法と private mapping の配置は参考実装固有です。
consumer commitment、 admission で固定する値、 Binding obligation の排他性は規格要件です。

ballot と四値評価

_castBallot 関数は member、 round、 window、重複を確認して票数を更新します。
_bodyStatus、 _nodeStatus、 _vetoStatus の各関数は、保存票と clock から四値判断を計算します。
以下では、この評価経路を _castBallot/_bodyStatus/_nodeStatus/_vetoStatus と表記します。

無効票で revert した場合、投票済み flag、票数、イベントのすべてが残りません。
K-of-N と composition semantics は規格要件です。

round と finalization

settleRound 関数は root status と formation instant を保存します。
appeal 関数は standing、 window、最大回数を確認して新 round を開きます。
finalize 関数と enforceHardDeadline 関数は appeal mode と hard cap から採用結果を決めます。
これらの状態遷移と内部確定処理を合わせた経路が settleRound/appeal/enforceHardDeadline/_finalize です。

enactment の記録

recordEnactment 関数は Final、 Binding、 consumer、 effect、 status を確認します。
Failed から Enacted への更新は許しますが、 Enacted 後の変更は拒否します。
外部 effect の実行自体は行いません。

SignatureChecker

contract 署名と EOA 署名

SignatureChecker.isValid は、署名者に code がある場合、ERC1271の isValidSignature 関数を staticcall します。
call 成功、32 bytes 以上の戻り値、 0x1626ba7e の magic value を確認します。

EOA 署名では65-byte長、 low-s、 v が27または28、 recover 結果が zero ではないことを確認します。
malformed signature や revert は bool の false になり、 batch 側が transaction 全体を revert します。
参考実装の revert strings は診断用であり、文字列自体は相互運用条件ではありません。

ACDFTaskTenderAdapter

issue を開く処理

ACDFTaskTenderAdapter.open は、 caller 入力だけで Subject や effects を作りません。
作業コントラクトから submission、 result hash、 task version、提出期限、支払期限、 accept と reject の effect ID を読み取ります。

adapter authority、 pending submission、 consumer deadline を確認した後、 consumer-filed Binding Issue を作ります。
作業 state が変わっていれば Issue を開かず revert します。

effect を実行する処理

ACDFTaskTenderAdapter.execute は、 Issue Registry から Final-Decided を読み、現在の作業 state と期限を再確認します。
Yes なら accept、 No なら reject を try / catch で呼び出します。

成功時は adapter state を enacted へ更新し、 Issue Registry へ Enacted を報告します。
target call 失敗時は Failed と reason event を残し、再試行を許します。

JSONSchema

policy document と result receipt

policy-spec.schema.json は、 PolicySpec の全 field を off-chain tool 間で交換する形式です。
tool は schema validation 後に全 field から policyId を再計算し、 Policy Registry から同じ policyId と parameters を読み、実行規則を照合します。

result-receipt.schema.json は、全 procedure state、 outcome、 Policy、 Subject、 consumer、 effects、 enactment を運びます。
Final 前の receipt 作成も許されますが、不可逆な effect を実行できるのは registry が Final-Decided を返した時だけです。
この2形式をまとめて参照する時は policy-spec.schema.json / result-receipt.schema.json と表記します。

セキュリティ

consumer authority と obligation scope

caller が自己申告した consumer address だけで Binding Issue を作ると、第三者が権限外の effect を約束できます。
consumer 自身の file、事前登録、事後承認のいずれかを確認し、 adapter は業務コントラクトの state から Subject と effects を導出します。
POST_ACK で投票後の承認を許すと、 consumer は有利な結果だけを Binding へ変えられます。
そのため、 acknowledgment を admission と投票開始より前に確定し、未承認 Issue は ack window 終了後に Advisory または Withdrawn へ進めます。

obligationId を毎回変えると panel shopping を止められません。
広すぎる値を使うと独立した Issue まで塞ぐため、同じ業務義務を安定して識別する粒度を選びます。
さらに、 admission 後の withdrawal を許すと、不利な途中経過を捨てて別 panel へ出し直せます。
withdrawal は未 admission の Filed に限定し、投票開始後は Final または hard deadline まで同じ Issue を維持します。

appeal と期限

PRESERVE_UNLESS_OVERTURNED では、 appeal round を意図的に NoDecision へすると前判断が残ります。
求める安全性が再審で新しい判断を必須にするなら REQUIRE_FRESH_DECISION を選びます。

permissionless な finalize や hard deadline enforcement があっても、自動 executor ではありません。
期限後に transaction を送る keeper、 consumer、利用者の経路を運用で用意します。
admission 時は、全 round と appeal が consumer deadline 内に収まるよう hardDeadline <= consumerDeadline を確認します。
ただし、 Final が consumer deadline の直前に形成されると effect 実行の余裕はほとんど残らないため、 adapter の arrival deadline と consumer deadline の間に運用上の buffer を設けます。

署名と replay

EIP712 domain は別 chain と別 registry への署名移動を防ぎます。
Issue、 round、 Body、 voter、賛否を署名対象へ含め、1票 key で再利用を止めます。

ERC1271の有効性は contract state で変わります。
registry が受理した票を後から無効化しないため、署名検証時点の state と transaction 確定性を監視します。
前提は、攻撃者が有効な署名を別の Issue、 round、 Body、 chain、 registry へ再送できることです。
EIP712 domain と ballot field のどれかが欠けると、意図しない案件へ同じ賛否が転用され、 threshold へ到達する可能性があります。
domain、全 ballot field、投票済み key を同時に照合し、ERC1271の staticcall が revert または不正な magic value を返した票は batch 全体で拒否します。
ただし、署名鍵の漏えい、 contract wallet の権限変更、確定前の chain reorg は registry だけでは防げません。

roster と evidence

異なる address が同じ operator に支配されていても、 kernel は検出しません。
identity、 operator cap、 conflict exclusion、 stake、 reputation は roster を作る外部 profile で確認します。

Evidence event の URI は信頼できない data として扱います。
取得した文書やAI向け prompt を命令として実行せず、 content type、 size、 hash、発行者を consumer 側で検証します。

registry と chain の信頼境界

consumer は、 registry instance、デプロイ済み bytecode、管理権限、正確な Policy version を固定します。
同じ interface ID でも、不正な実装が正しい semantics を保証するわけではありません。

Issue の Final は、対象 chain 上の procedure finality です。
source chain の reorg 耐性、 bridge、 cross-chain receipt の証明までは含みません。
攻撃者が同じ interface ID を返す偽 registry を配備すると、 consumer は存在しない投票や改変された結果を正規の decision として読まされます。
また、 cross-chain receipt を証明なしで受け入れると、別 chain の Final や古い fork の結果で effect が実行されます。
deployment address、 runtime code、 proxy 管理権限、 Policy Registry、 Issue Registry、 chain ID を allowlist へ固定し、 receipt は対象 registry の read 結果と照合します。
この対策後も、 source chain の finality assumption、 bridge validator、 consumer 自身の upgrade authority は信頼境界として残ります。

最後に

ERC8436は、複数のAIエージェントによる判断を、具体的な Subject、 question、 Policy、 consumer commitment へ結び付けます。
必要票へ届かなかった結果を NoDecision として残し、異議申立て後の採用規則も投票前に固定します。

Policy Registry は実行規則を保存し、 Issue Registry は投票、 round、最終結果、実行報告を保存します。
consumer だけが自分の authority 内で業務 effect を実行するため、投票資格と資産を動かす権限を分離できます。

導入時は、 roster の独立性、 obligation の粒度、 appeal mode、 clock 単位、 keeper、 registry instance、 effect 失敗後の回復を別々に確認してください。

他でも色々記事を書いているのでぜひよろしければ読んでいってください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?