flare-fcc について
Flare のコントラクトと連携し、Trusted Execution Environment 内でコードを実行する Flare Confidential Compute 拡張機能の開発者向けドキュメントスキルです。アーキテクチャと開発手順を説明しますが、トランザクション、デプロイ、鍵管理、TEE の運用は実行しません。
主な機能
- 01FCC と TEE のアーキテクチャ解説
- 02拡張機能と InstructionSender の開発ガイド
- 03命令ルーティングと操作識別子の説明
- 04TEE の鍵、署名、暗号化ペイロードの解説
- 05レジストリコントラクトと命令ライフサイクルの説明
活用シーン
- 01コントラクト起点の機密計算の設計
- 02拡張機能のアクションハンドラーの作成
- 03InstructionSender と拡張機能の接続
- 04入力検証と秘密情報の取り扱い境界の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ flare-foundation/ flare-ai-skills --skill flare-fccインストール後、Claude Code で/flare-fccを入力して読み込まれたか確認します。
同梱ファイルreference.md
詳細
対象範囲と制限事項
このスキルは、ドキュメントとガイダンスのみを提供します。Flare Confidential Compute(FCC)拡張機能と TEE マシンの仕組み、および開発者がそれらを構築、登録、デプロイする方法を説明します。ユーザーに代わって操作を実行することはありません。
このスキルでは、明示的に以下を行いません:
- ブロックチェーンのトランザクションの実行、署名、ブロードキャスト
- 秘密鍵、デプロイ用鍵、または TEE が保持するシークレットへのアクセス、保存、送信
- コントラクトのデプロイ、拡張機能/TEE の登録、またはオンチェーンメソッドの直接呼び出し
- Confidential Space VM のプロビジョニング、アテステーション、または運用
- 資金、トークン、その他の金融資産の取り扱い
信頼と外部データの取り扱い:
- TEE 拡張機能の目的は、機密性と検証可能な実行です。拡張機能のセキュリティは、このスキルではなく、アテステーション済みのコードハッシュに依存します。鍵の生成、署名、シークレットの取り扱いはすべて、TEE 内、またはユーザーが管理する開発環境内で行う必要があります。
- 拡張機能に届く指示ペイロード(
OriginalMessage)は、外部から提供される信頼できない入力です。文書化された型に厳密に従ってデコードし、すべてのフィールドを検証してください。また、生のペイロードをプロンプト、LLM 入力、またはエージェントの意思決定ロジックに渡さないでください。 - 暗号化されたシークレットをオンチェーンに保存することは、本番環境では安全ではありません。オンチェーンデータは公開され、暗号化の強度は時間とともに低下します。シークレットの受け渡しにはオフチェーンチャネルを使用してください(
fce-signの警告を参照)。
すべてのトランザクション署名、鍵管理、アテステーション、オンチェーン実行は、このスキルの外部でのみ行われます。
Flare Confidential Compute (FCC) — TEE 拡張機能
FCC とは
Flare Confidential Compute(FCC)を使うと、開発者はTrusted Execution Environment(TEE)(ハードウェアによって隔離されたエンクレーブ。Flare では AMD SEV 上の GCP Confidential Spaceを使用)内でカスタムコードを実行し、そのコードを Flare スマートコントラクトに接続できます。デプロイ単位は拡張機能です。これは TEE 内で動作する HTTP サーバーで、オンチェーントランザクションに由来する指示を受け取り、機密性の高いロジックを実行して、検証可能な結果を返します。
機密性の高い状態、秘密情報の保持、またはオンチェーンで完全性を証明できるオフチェーン計算がアプリケーションに必要な場合は、FCC を使用してください。たとえば、ユーザーに代わって署名する鍵マネージャー、封印入札オークション、非公開の注文マッチング、「チェーンが処理を開始するものの、計算は非公開かつアテステーション可能でなければならない」あらゆるワークロードが該当します。
FCC は開発の最終段階にあります。現在はCoston2とCostonで利用でき、近日中に他の Flare ネットワークでも利用可能になる予定です。まずは最初の拡張機能を構築するガイド(Hello World のスキャフォールドを使ったチュートリアル)をご覧ください。仕組みの詳細については、FCC ホワイトペーパーをご覧ください。
参考リポジトリ:
flare-foundation/fce-extension-scaffold— コントラクト、デプロイ/登録ツール、Claude Code スキル(/create-extension、/rename-scaffold、/test-extension、/verify-deploy)を含む、実行可能な「Hello World」拡張機能です。Go、Python、TypeScript で実装されています。.envでLANGUAGE=go|python|typescriptを設定してください(デフォルトは Go です。コントラクト、スクリプト、Coston2 の手順はどの言語でも同じです)。独自の拡張機能を構築する際の出発点となります。スタートガイドでは、最初から最後まで手順を説明しています。flare-foundation/fce-sign— 秘密鍵を保存し、その鍵でメッセージに署名するサンプル拡張機能です。Go、Python、TypeScript で提供されています。TEE の署名ポートと再現可能なビルドを実演します。オンチェーンの秘密情報に関する部分は、明示的にデモ専用です。flare-foundation/fce-weather-insurance— パラメトリック降雨保険を実演する、FCC を使った完全なアプリケーションです。保険契約者はオンチェーンで補償を購入し、TEE は OpenWeatherMap のデータを取得して精算結果に署名します。誰でもsettle()を呼び出して、結果を検証し、保険金を支払えます。公開ポリシーと ECIES で暗号化された非公開ポリシーに対応しています。Next.js の dApp フロントエンドも含まれています。
指示のライフサイクル
拡張機能が制御するのは、オンチェーンコントラクト(ステップ 1)とアクションハンドラー(ステップ 6)の 2 つだけです。その間の処理はすべて TEE インフラストラクチャが担います。
1. User calls your InstructionSender contract (on-chain)
2. Contract routes through TeeExtensionRegistry.sendInstructions() → emits TeeInstructionsSent
3. TEE proxy picks up the instruction from the chain
4. TEE node fetches the instruction from the proxy
5. TEE node forwards it as POST /action to your extension server (inside the TEE)
6. Your extension decodes, validates, executes, and returns a result
7. TEE node returns the (optionally cosigned) result to the proxy
8. Caller polls the proxy for the result
オンチェーンの構成要素
2 つのプロトコルコントラクト(flare-smart-contracts-v2 由来)がシステムの入口となります。
TeeExtensionRegistry— 拡張機能のレジストリであり、指示を送信する唯一の経路です。主なインターフェースは次のとおりです。sendInstructions(address[] _teeIds, TeeInstructionParams _params) payable returns (bytes32 instructionId)— 唯一のエントリーポイントです。TeeInstructionParams={ bytes32 opType; bytes32 opCommand; bytes message; address[] cosigners; uint64 cosignersThreshold; address claimBackAddress; }。nextPublicExtensionId()およびgetTeeExtensionInstructionsSender(uint256 extensionId)— 拡張機能の ID を特定するために使用します。パブリック拡張機能の ID は0x10000(65536)から始まります。それ未満の ID はシステム拡張機能用に予約されているため、setExtensionId()は 0 からではなく、0x10000からnextPublicExtensionId()までを走査します。- アクセス制御: 拡張機能を登録するとき、1 つの InstructionSender アドレスに紐付けます。レジストリは、
msg.senderがそのアドレスでないsendInstructions呼び出しを拒否します。EOA も、ほかのコントラクトも呼び出せません。
TeeMachineRegistry— 拡張機能にサービスを提供する TEE マシンを紐付けます。getRandomTeeIds(uint256 _extensionId, uint256 _count)は、指示のルーティング先となるマシンアドレスを選択します(1 つの指示を複数の TEE に送るには、_count > 1を指定します)。
オンチェーンでは、どちらも 1 つの EIP-2535 ダイヤモンドである FlareTeeManager の背後にあります。ダイヤモンドのアドレスを呼び出してください。IFlareTeeManager は集約インターフェースです。前述の TeeExtensionRegistry のインターフェースはダイヤモンドの IExtensionManager ファセット、TeeMachineRegistry のインターフェースは IMachineManager ファセットです。日常的な利用では、ほかに 2 つのファセットが重要です。IVerification(アテステーション/可用性チェックのリクエスト、コサイナー、ChallengeExpired/InvalidAttestation エラー)と、IOperationFees(指示ごとの手数料照会 — getOperationFee、calculateFeeByTeeIds)です。IOwnerAllowlist は、拡張機能、マシン、ウォレットプロジェクトのオーナーを登録できるユーザーを制限します。ITeeCommonErrors には、前述のもの(TooMany、ChallengeExpired、InvalidGovernanceHash)を含む、各ファセットで共有されるエラーが含まれています。完全な ABI リファレンスは FCC Reference を参照してください(IFlareTeeManager、IExtensionManager、IMachineManager、IVerification、IOperationFees、IOwnerAllowlist、ITeeCommonErrors)。メインネットへのデプロイ後に FlareTeeManager のアドレスをハードコードしないよう、実行時に FlareContractRegistry を通じて解決してください。現在は Coston2、Coston、Songbird にデプロイされています。
InstructionSender の契約
これはあなたの契約であり、拡張機能への指示の送信が許可されている唯一のアドレスです。最低限、次の要件を満たす必要があります。
- 拡張機能 ID を把握する — 指示を処理する TEE マシンを検索するために必要です。スキャフォールドの
setExtensionId()は登録後にレジストリをスキャンし(最初の公開拡張機能 ID である0x10000から開始)、ID をキャッシュします(一度だけ設定可能)。 TeeExtensionRegistryのsendInstructionsを呼び出す — 少なくとも 1 つのteeId、opType/opCommandのbytes32識別子、空でないmessage、および(通常は空の)コサイナーを指定します。payableにしてmsg.valueを転送する — レジストリは指示ごとに手数料を請求します。- 登録前に存在している — デプロイ済みの InstructionSender アドレスを渡して登録します。
スキャフォールドの HelloWorldInstructionSender は、そのまま使えるテンプレートです。bytes32 の操作定数と、各アクションに対応する payable の送信関数(sendSayHello、sendSayGoodbye)を定義しています。カスタムのアクセス制御、オンチェーン検証、複数 TEE へのルーティング、コサイナーのワークフロー、バッチ処理が必要な場合は、最小限のカスタム sender を作成することもできます。レジストリが確認するのは、登録済みのアドレスが有効なパラメーターを指定して sendInstructions を呼び出すことだけです。
OPType/OPCommand ルーティングモデル
コントラクトと拡張機能のコードは、3 つのレイヤーすべてで完全に一致させる必要がある 2 階層の識別子によって連携します。
| レイヤー | 操作タイプ | コマンド |
|---|---|---|
| Solidity | bytes32 OP_TYPE_GREETING = bytes32("GREETING") | bytes32 OP_COMMAND_SAY_HELLO = bytes32("SAY_HELLO") |
| Go config | OPTypeGreeting = "GREETING" | OPCommandSayHello = "SAY_HELLO" |
| Go router | dataFixed.OPType == teeutils.ToHash(config.OPTypeGreeting) | df.OPCommand == teeutils.ToHash(config.OPCommandSayHello) |
OPType は操作グループを選択し、OPCommand はそのグループ内のサブルートを指定します。OPType が一致しない場合は「unsupported op type」に、OPCommand が一致しない場合は「unsupported op command」になります。bytes32("...") に格納できるのは最大 31 バイトです。識別子は短くしてください。
予約済みの名前: OPType の F_ プレフィックスは Flare システム操作用として予約されています(F_REG、F_WALLET、F_GET、F_POLICY、F_GOVERNANCE、F_XRP、F_BTC、F_FDC2)。独自の OPType に F_ を付けないでください。コマンド名も予約されています(TEE_ATTESTATION、TEE_INFO、TEE_BACKUP、KEY_GENERATE、KEY_INFO、KEY_PROOF、KEY_DELETE、KEY_DIRECT_BACKUP、KEY_DIRECT_RESTORE、KEY_DATA_PROVIDER_RESTORE、INITIALIZE_POLICY、UPDATE_POLICY、SET_MACHINE_PATH_LIST、PAY、REISSUE、VRF、PROVE)。これらのコマンド名を独自の F_ 以外の OPType で再利用すると、ローカル検証はエラーなく通過しますが、指示は拡張機能に届きません(エラーも発生しません)。そのため、両方にドメイン固有の名前を選んでください。
TEE キーと署名モデル
各 TEE マシンは、起動時にID キーペアを生成します(Ethereum 形式のキーに対応する secp256k1 上の ECDSA)。秘密鍵はエンクレーブの外に出ることはありません。
- 公開鍵 — 検証可能な ID です。オンチェーンに保存するか、プロキシの
/infoエンドポイントから提供できます。誰でも ECDSA 署名を検証し、ECIES ペイロードを暗号化して送信できます。 - 秘密鍵 — エンクレーブ内にのみ存在します。そのマシンに送信された結果の署名(ECDSA)とペイロードの復号(ECIES)に使用します。
ECDSAは送信元を証明します(公開メッセージに押すスタンプ)。ECIESは内容を隠します(一致する秘密鍵だけが開ける鍵付きの箱)。この 2 つを混同しないでください。秘密鍵で暗号化するのは逆です。
シークレットには、オフチェーンで TEE の公開鍵宛てに暗号化し、エンクレーブ内でのみ復号するパターンを使います。拡張機能自体が ID 秘密鍵を保持することはありません。TEE ノードをローカルで呼び出し(署名ポート上の POST /decrypt、localhost 限定)、ノードがエンクレーブ内に保持されたキーを使って復号します。暗号化はバイト列を隠しますが、以下のコンセンサスチェックに代わるものではありません。
データプロバイダーとコサイナーは、TEE に処理を実行させる権限を制御します。オンチェーン命令は、Flare Systems Protocol のデータプロバイダーが中継し、現在の署名ポリシーの重みの 50% 超で署名した後にのみ実行されます(FTSO/FDC を支えるものと同じ重み付きセットです)。プロキシだけで処理を承認することはできません。コサイナーは、機密性の高い操作(支払い、キー管理)に対して、独立した第 2 の承認ゲートを追加します。プロバイダーの重みとコサイナーのしきい値、両方の署名が揃わなければ、TEE は実行しません。
仕組みの詳細は、TEE の公開鍵と秘密鍵とデータプロバイダーとコサイナーをご覧ください。
拡張機能(Go)— アクションハンドラー
TEE 内の拡張機能は HTTP サーバーです。TEE ノードは各命令を POST /action として送信します。実装する processAction は、アクションから OPType、OPCommand、生の OriginalMessage を含む instruction.DataFixed を解析し、まず OPType、次に OPCommand に基づいて処理を振り分けます。
各ハンドラーは、同じ4 ステップのパターンに従います。
func (e *Extension) processSayHello(action teetypes.Action, df *instruction.DataFixed) teetypes.ActionResult {
// 1. DECODE the raw OriginalMessage (JSON here; use structs.DecodeTo for ABI-encoded)
var req types.SayHelloRequest
dec := json.NewDecoder(bytes.NewReader(df.OriginalMessage))
dec.DisallowUnknownFields()
if err := dec.Decode(&req); err != nil {
return buildResult(action, df, nil, 0, fmt.Errorf("decoding request: %w", err))
}
// 2. VALIDATE every field — this is untrusted external input
if req.Name == "" {
return buildResult(action, df, nil, 0, fmt.Errorf("name must not be empty"))
}
// 3. EXECUTE your confidential logic (guard shared state with the mutex)
e.mu.Lock()
e.greetingCount++
n := e.greetingCount
e.mu.Unlock()
// 4. BUILD the result: status 1 = success (data returned), status 0 = error (err logged)
data, _ := json.Marshal(types.SayHelloResponse{Greeting: "Hello, " + req.Name, GreetingNumber: n})
return buildResult(action, df, data, 1, nil)
}
開発者が変更するファイル(スキャフォールドでは ★ が付いており、/rename-scaffold を使うと Hello World のプレースホルダーを自動で名前変更できます):
go/internal/config/config.go—OPType/OPCommandの文字列定数とバージョンgo/pkg/types/types.go— リクエスト/レスポンス/状態の構造体go/internal/extension/extension.go— ルーティングのケースとハンドラー(主なカスタマイズ箇所)contracts/InstructionSender.sol— 対応するbytes32定数と送信関数tools/cmd/run-test/main.go— E2E テストのペイロードとアサーション
コントラクトを編集した後、./scripts/generate-bindings.sh を実行して Go バインディングを再生成します。
TEE 署名ポート
TEE 管理のキーで署名、アテステーション、または暗号化を行う必要がある拡張機能は、拡張機能内から TEE の署名ポート(例:localhost:7701/SIGN_PORT。ドキュメントによっては 9090 と記載されています。スキャフォールドの設定を確認してください)を呼び出します。これにより、fce-sign はキーをエンクレーブの外に出すことなくメッセージに署名できます。
fce-sign:秘密鍵を扱う拡張機能の例
fce-sign リポジトリでは、ECIES で暗号化した秘密鍵を保存し、任意のメッセージに署名するという、TEE のワークフロー全体を紹介しています。Go、Python、TypeScript で利用できます(.env で LANGUAGE を設定してください)。
Docker サービスのアーキテクチャ
3 つのコンテナーが Docker サービスとして動作します。
extension-tee— 拡張機能のコードです。プロキシからデコード済みの命令を受け取り、結果を返します。ext-proxy— 新しい命令を求めてチェーンを監視し、ハンドラーに転送して、結果をオンチェーンに送信します。redis— プロキシが使用するインメモリストアです。
ポートリファレンス(fce-sign)
| サービス | コンテナポート | ホストポート |
|---|---|---|
| ext-proxy 内部 | 6663 | 6673 |
| ext-proxy 外部 | 6664 | 6674 |
| redis | 6379 | 6382 |
ngrok はホストポート 6674(ext-proxy 外部)をインターネットに公開します。内部ポート 6673 は、Docker 内で拡張機能コンテナとプロキシ間の通信に使用されます。
Coston2 への fce-sign のデプロイ(ローカルのシミュレートされた TEE)
前提条件:Docker Desktop、Foundry、Go、ngrok、資金をチャージした Coston2 ウォレット。
ステップ 0 — ローカルのシミュレーションモードを有効化:
./scripts/use-chain.sh local coston2 go # or python / typescript
SIMULATED_TEE=true と LOCAL_MODE=false(実際の Coston2 チェーン、シミュレートされたアテステーション)を設定します。./scripts/use-chain.sh --list を実行すると、すべてのオプションを確認できます。
ステップ 1 — デプロイ用のキーを設定(.env.local.coston2 内):
DEPLOYMENT_PRIVATE_KEY="<funded-coston2-private-key-hex-no-0x>"
INITIAL_OWNER="0x<your-address>"
編集後に use-chain.sh を再実行すると、変更が .env に反映されます。
ステップ 2 — 公開プロキシ URL を確保(別のターミナル):post-build.sh、start-services.sh、test.sh はすべて .env から EXT_PROXY_URL を読み込むため、コントラクトをデプロイするか Docker サービスを起動する前に設定してください。
セキュリティ:
ngrok http 6674を実行すると、ローカルの ext-proxy が公開されます。ポート 6674 ではプロキシの HTTP API が公開され、URL を知っている人なら誰でも呼び出せます。ngrok は Coston2 テストネットでのみ使用し、作業が終わったらトンネルを停止してください。
ngrok http 6674
ngrok の Forwarding 行に表示される HTTPS URL をコピーして、.env.local.coston2 の EXT_PROXY_URL に設定し、use-chain.sh を再実行してください。プロキシはまだ起動していません。ステップ 5 で ext-proxy を起動すると、ngrok がトラフィックを転送します。ngrok の無料プランでは、再起動後も URL が維持されるため、通常この手順は一度だけで済みます。
ステップ 3 — コントラクトをデプロイして拡張機能を登録:
./scripts/pre-build.sh
Solidity をコンパイルし、InstructionSender をデプロイして、拡張機能をオンチェーンに登録します。EXTENSION_ID と INSTRUCTION_SENDER を config/extension.env に書き込みます。
警告:
config/extension.envが存在すると、pre-build は再実行を拒否します。新しい拡張機能を作成する場合に限り--forceを使用してください。このオプションを使うと、新しいInstructionSenderがデプロイされ、新しい拡張機能 ID が登録されます。以前の拡張機能 ID で古い TEE マシンがまだ登録されている場合、MachineManager.TooMany()が発生します。
ステップ 4 — インデクサー DB を設定:
cp config/proxy/extension_proxy.coston2.docker.toml.example \
config/proxy/extension_proxy.coston2.docker.toml
[db] ブロックを Coston2 インデクサーの認証情報で編集します(ホスト 34.38.42.208、ポート 3306、データベース indexer)。認証情報は、サポート または @flare_network からリクエストすると提供されます。
ステップ 5 — 拡張機能スタックを起動:
./scripts/start-services.sh
拡張機能のイメージをビルドしてから、redis、ext-proxy、extension-tee を起動します。スクリプトは EXT_PROXY_URL/info を待機します。ステップ 2 で ngrok を起動済みであれば、このチェックは公開トンネル経由でローカルのプロキシに到達します。ローカルでプロキシが起動するまで待ちます:until curl -sf http://localhost:6674/info >/dev/null 2>&1; do sleep 2; done。6674 でリッスンしているのが拡張機能プロキシだけであることを確認します:lsof -i :6674。
ステップ 6 — プロキシを検証:
curl -s "$EXT_PROXY_URL/info" | jq '.machineData'
シミュレートされた TEE の場合、codeHash は 0x194844cf…、extensionId は config/extension.env の値と一致し、initialOwner は自分のアドレスと一致します。
ステップ 7 — TEE マシンを登録:
./scripts/post-build.sh
オンチェーンで 3 つの手順を実行します:allow-tee-version(コードハッシュをホワイトリストに登録)、set-governance(拡張機能の TEE ガバナンス署名者セットとしきい値を登録。.env で GOVERNANCE_SIGNERS/GOVERNANCE_THRESHOLD を設定しない限り、デプロイヤーが唯一の署名者となり、しきい値は 1 です)、register-tee -command rRap(マシンを登録し、新しいアテステーションを発行して FTDC チェックを実行し、本番環境へ昇格)。ガバナンスセットは、extension-tee ノードコンテナに渡したものと一致している必要があります。一致しない場合、登録は InvalidGovernanceHash で失敗します。
ステップ 8 — エンドツーエンドテストを実行:
./scripts/test.sh
手順:setExtensionId() → TEE の公開鍵を取得 → テスト用秘密鍵を ECIES で暗号化 → オンチェーンで updateKey を実行 → 処理を待機 → オンチェーンで sign を実行 → ECDSA 署名が一致することを検証。
Python および TypeScript のハンドラーフレームワーク
Python と TypeScript では、(opType, opCommand) の組み合わせでハンドラーを登録する Framework クラスを使用します。
def register(framework: Framework) -> None:
framework.handle(OP_TYPE_KEY, OP_COMMAND_UPDATE, handle_key_update)
framework.handle(OP_TYPE_KEY, OP_COMMAND_SIGN, handle_key_sign)
各ハンドラーは、16 進数エンコードされた originalMessage を受け取り、(data, status, error) を返します。
data: オンチェーンに書き戻される 16 進数エンコードされた戻りデータ、またはNonestatus:0= エラー、1= 成功、>=2= 保留中error: ハンドラーが失敗した場合のエラーメッセージ文字列
base/ パッケージ(Python/TypeScript)は、hex_to_bytes/bytes_to_hex と Framework HTTP サーバーを提供します。base/ は変更しないでください。これはフレームワークの基盤です。
fce-sign プロジェクトの構成
sign/
├── contracts/ # Solidity contracts (shared)
├── config/
│ ├── extension.env # Generated by pre-build.sh
│ ├── coston2/deployed-addresses.json
│ └── proxy/ # ext-proxy TOML configs
├── scripts/ # use-chain.sh, pre-build.sh, start-services.sh, post-build.sh, test.sh
├── go/internal/extension/ # Go business logic (modify)
├── go/tools/cmd/ # Deploy CLIs (Go-only, shared across all languages)
├── python/app/ # Python business logic (modify)
├── python/base/ # Framework infrastructure (do not modify)
├── typescript/src/app/ # TypeScript business logic (modify)
├── typescript/src/base/ # Framework infrastructure (do not modify)
├── docker-compose.yaml
├── .env.example
└── .env.local.coston2
fce-sign から独自の拡張機能を構築する
- リポジトリをクローンして、言語を選択します。
- Solidity コントラクトとハンドラーの両方で
opType/opCommand定数を定義します。 contracts/InstructionSender.solを変更して、定数とパラメーターを設定します。go/internal/extension/(Go)またはapp/(Python/TypeScript)にハンドラーを記述します。- 上記のデプロイ手順に従います。
go/tools/のデプロイ CLI は、言語にかかわらず共通で使用できます。
クリーンアップ
スタックを停止します。
./scripts/stop-services.sh
完全にリセット(最初からやり直す場合):
./scripts/stop-services.sh
docker compose down --rmi local
rm -f .env config/extension.env config/proxy/extension_proxy.coston2.docker.toml
その後、ステップ 0 から再開してください。注:オンチェーンの状態(デプロイ済みコントラクト、登録済み拡張機能、TEE)はリセットできません。pre-build.sh を実行するたびに、新しいコントラクトがデプロイされます。
fce-sign のトラブルシューティング
- プロキシが起動しない/DB 同期エラー —
docker compose logs ext-proxyを確認し、config/proxy/extension_proxy.coston2.docker.tomlの DB 認証情報を確認してください。 - トランザクションがリバートする — C2FLR が不足しています。Coston2 faucet を使用してください。
MachineManager.TooMany()—config/extension.envの拡張機能 ID がオンチェーンの TEE レコードと一致していません(通常はpre-build.sh --forceの実行後に発生します)。完全にリセットするか、既存のextension.envを維持したままpost-build.shとtest.shのみを再実行してください。Verification.ChallengeExpired—post-build.shを再実行してください。InvalidGovernanceHash—set-governanceで使用したGOVERNANCE_SIGNERS/GOVERNANCE_THRESHOLDが、TEE ノードによって署名されたガバナンスハッシュと一致していません。デフォルトのデプロイ実行者のみの設定では両方を未設定のままにするか、.envとノードコンテナーの設定を一致させてからpost-build.shを再実行してください。code hashes do not match—SIMULATED_TEEとコンテナーのMODEが一致していません。SIMULATED_TEE=trueとMODE=1を使用してください(Docker Compose によって設定されます)。- TEE の登録がタイムアウトする —
docker compose restart ext-proxyを試してください。FDC アテステーションには、Coston2 でアクティブなリレープロバイダーが必要です。 - ngrok URL が変更された —
.env.local.coston2のEXT_PROXY_URLを更新し、use-chain.shを再実行してください。必要に応じて ngrok トンネルを再起動し(ngrok http 6674)、Docker スタックを再起動してから、post-build.shとtest.shを再実行してください。
fce-weather-insurance:パラメトリック保険の例
fce-weather-insurance リポジトリは、完全な FCC アプリケーションです。エンクレーブ内で OpenWeatherMap のデータを使って精算する降雨量に基づくパラメトリック保険を実装しています。オフチェーンデータを取得して結果に署名し、オンチェーンコントラクトがその署名を検証してから資金を移動する TEE ワークロードのパターンを示しています。これは、オラクルを利用したあらゆる精算に共通する構成です。flare-foundation/fce-weather-insurance-x402-agent では、x402 payments を使ってこの拡張機能に対する保険契約の購入と精算を行う AI エージェントを確認できます。
フロー
- 保険契約者は、日付と場所を指定して降雨補償を購入し、ERC-20 の保険料(
payToken)を支払います。コントラクトはプールの流動性から保険金を確保します。 settlementUnlockAt(補償対象日の翌日 00:00 UTC)以降に、キーパーがrequestSettlementを呼び出し、WEATHER/SETTLEの指示をエクステンションに送ります。- エクステンションはその日の降水量を エンクレーブ内で OpenWeatherMap から取得し、精算結果に署名します。
- 誰でも署名済みの
ActionResultを指定してsettle()を呼び出せます。コントラクトは結果ハッシュをドメイン分離されたペイロードでラップし、署名者をecrecoverで復元して、登録済みのteeAddressと一致することを確認します。降雨量がしきい値に達していた場合、保険金を支払います。
OP 識別子(OP_TYPE_WEATHER = bytes32("WEATHER"))
| コマンド | 目的 |
|---|---|
FETCH | 都市の現在の天気 JSON を返します(テスト/dApp での表示用)。 |
SETTLE | 保険契約の1日の降雨量を取得し、署名済みの精算ペイロードを返します。 |
BUY | ECIES で暗号化された非公開保険契約を復号し、証明済みの条件を返します。 |
常に、定数は WeatherInsurance.sol と internal/config/config.go で完全に一致している必要があります。
公開保険契約と非公開保険契約
- 公開購入(
buyPolicy):すべての条件がオンチェーンに記録されます。呼び出し元は事前に保険料をapproveします。 - 非公開購入(
buyPolicyPrivate):購入者は、エクステンションの公開鍵を使って ABI エンコードされたPrivateBuyParamsを ECIES で暗号化します。暗号文はWEATHER/BUYの指示として送信されます。TEE はノードの/decryptエンドポイント経由で復号し、しきい値をtermsCommitmentをキーとしてエンクレーブメモリに保持します。その後、購入者はrelayPrivateBuyで購入を確定します(TEE の署名を検証します)。精算までは、オンチェーンに記録されるのはコミットメントのみです。
TEE 署名の検証
relayPrivateBuy と settle はどちらも同じ方法で TEE の結果を検証します。ノードが署名したハッシュを再構築し、署名者を復元して teeAddress と一致することを確認します。
bytes32 resultHash = keccak256(abi.encodePacked(
keccak256(_resultData), _actionId, keccak256(bytes(_submissionTag)), _status));
// Domain-separated payload — must match go-flare-common signing.TEEActionResult
bytes32 payloadHash = keccak256(abi.encode(bytes32("TEE_ACTION_RESULT"), block.chainid, resultHash));
address signer = _recover(_ethSigned(payloadHash), _signature); // EIP-191 personal-sign
require(signer == teeAddress, "bad TEE signature");
ノードは EIP-191 プレフィックスを付けて keccak256(abi.encode(TEE_ACTION_RESULT_PREFIX, chainId, ActionResult.Hash())) に署名します。ActionResult.Hash() のみに署名するわけではありません。成功した結果(status == 1)のみが受け入れられます。生の resultHash に対して検証すると、現在の TEE ノードの署名とは一致せず、失敗します。SettlementTime(ライブラリ)は、settlementUnlockAt を補償対象日の翌日 00:00 UTC として計算します。
デプロイに関する注意(scaffold/fce-sign とは異なります)
./scripts/use-chain.sh local coston2で有効化します(言語の引数は不要です。アプリは Go です)。.env.local.coston2にOPENWEATHERMAP_API_KEYとPAY_TOKENを設定します。Coston2 には0x53192e788991AD96bC180249B15AefB94E597dD1にモックの ERC-20 があります。または独自にデプロイしてください。./scripts/pre-build.shを実行してから./scripts/extension-setup.shでデプロイします(後者はデプロイ済みのコントラクトでsetPayTokenを呼び出します)。その他の手順(ポート6674での ngrok、post-build.sh、アテステーション、コードハッシュのホワイトリスト登録)は、以下の scaffold のライフサイクルと同じです。
アテステーションと再現可能なビルド
TEE の信頼性はリモートアテステーションによって確保されます。Confidential Space VM は実行中のイメージを測定し、コードハッシュを報告します。Flare のデータプロバイダー(FTDC)は、その拡張機能についてコードハッシュがオンチェーンでホワイトリストに登録されたTEE からの結果のみを受け入れます。そのため、ビルドはセキュリティ上きわめて重要です。
MODE=0は本番用のアテステーションバックエンドです。MODE=1は シミュレーション アテステーションを生成し、FTDC はこれを拒否します。テストネット/メインネットでは、イメージにMODE=0を組み込み、.envでLOCAL_MODE=false/SIMULATED_TEE=falseを設定する必要があります。- 再現性:
SOURCE_DATE_EPOCH(例:最終コミット時刻)を設定すると、同じソースから同じコードハッシュを生成できます。Go のビルドは、マシン間でビット単位まで再現可能です(単一の静的バイナリ)。Python/TypeScript は同一マシン上で決定性を実現できますが、マシン間でビット単位まで一致することはベストエフォートです(pip ホイール/node_modulesにホストのパスが埋め込まれます)。別のマシンで再ビルドするとコードハッシュが変わり、再登録が必要になる場合があります。fce-signは.envのLANGUAGE=go|python|typescriptで言語を選択します。
デプロイのライフサイクル(Coston / Coston2)
スキャフォールドスクリプトは 4 つのフェーズを連続して実行します(./scripts/full-setup.sh --chain coston2 --test はすべてのフェーズを一度に実行します。各フェーズを個別に実行することもできます)。
- pre-build(
pre-build.sh)—InstructionSenderをコンパイルしてデプロイし、拡張機能をTeeExtensionRegistryに登録して、EXTENSION_IDとINSTRUCTION_SENDERをconfig/extension.envに書き込みます。再実行時は実行を拒否せず、既存のconfig/extension.envを再利用します。新しい拡張機能を作成する場合にのみ--forceを指定してください。 - サービスの起動(
docker compose up -d --build)—redis、ext-proxy、およびextension-tee(お客様のコード)をコンテナーとして実行します。ローカルではLOCAL_MODE=trueにするとアテステーションがスキップされます。 - post-build(
post-build.sh)—allow-tee-versionでコードハッシュをホワイトリストに登録し、set-governanceで拡張機能の TEE ガバナンス署名者セット/しきい値を登録します(GOVERNANCE_SIGNERS/GOVERNANCE_THRESHOLDが設定されていない場合、デプロイヤーが署名者となり、しきい値は 1 です)。続いてregister-teeで TEE マシンをオンチェーンに登録します。再実行時に新しいアテステーションチャレンジを発行してVerification.ChallengeExpiredを回避するには、register-tee -command rRapを使用してください(大文字のR)。 - テスト(
test.sh)— デプロイ済みの TEE に指示を送り、往復処理を検証します。
実際のテストネットでは、これに加えて、資金を入金したデプロイヤーキー(Coston2 のフォーセット)、外部から到達可能なプロキシ URL(ポート 6674 への任意の HTTPS トンネル。例:ngrok または cloudflared)、プロキシ用のインデクサーデータベース認証情報、およびイメージを実行する GCP Confidential Space VM が必要です。プロキシの /info に curl でアクセスしてデプロイを確認し、machineData が次の条件を満たすことを確認してください。platform が 0x4743505f414d445f534556…(GCP_AMD_SEV)で始まること、codeHash が実際に測定されたハッシュであること(シミュレーション用の 0x194844cf… ではないこと)、extensionId/initialOwner が config/extension.env と一致することです。FlareTeeManager ダイヤモンドを再デプロイすると、すべての登録が消去されます。新しい EXTENSION_ID を取得するために pre-build を再実行し、VM オペレーターに新しい ID で再起動してもらった後、post-build とテストを再実行してください。
よくある失敗の原因
Verification.TeeNotFound—NORMAL_PROXY_URLが別のチェーンの FTDC プロキシを指しています。Verification.ChallengeExpired— post-build を再実行してください。register-teeが-command rRapを使用していることを確認してください。InvalidGovernanceHash—GOVERNANCE_SIGNERS/GOVERNANCE_THRESHOLDが、TEE ノードの署名に使用されたガバナンスハッシュと一致していません。デプロイヤーのみを使うデフォルト設定にするには未設定のままにするか、両方を一致させて post-build を再実行してください。code hashes do not match—SIMULATED_TEEとイメージのMODEが一致していません。どちらも「実際の」設定である必要があります(SIMULATED_TEE=false、MODE=0)。- ext-proxy からの
connect: connection refused— Flare のインデクサーデータベースへの VPN/ルートが停止しています。
詳細なトラブルシューティング(専用ガイド:FCC Troubleshooting)
問題の多くは、ローカルスタックが現在のオンチェーンデプロイメントからずれているか、登録済みの URL が解決できなくなっていることが原因です。ネットワークに問題があると判断する前に、まず次の点を確認してください。
- TEE がいつまでも本番環境にならない(可用性チェックがいつまでも
404を返す、マシンキューが空、getRandomTeeIdsが何も返さない、呼び出し元がTooMany()でリバートする):まずSIMULATED_TEE=trueで実行していないか確認してください。シミュレートされたアテステーションには、実際の Coston2 データプロバイダーが共同署名できません。そのため、可用性チェックへの投票が行われず、マシンは永久にINITIALIZEDのままとなり、そのマシンへのポーリングはすべて404を返します。これはローカル開発では想定どおりの動作であり、バグではありません。マシンがオンチェーンでPRODUCTIONになることはありません。さらに、scaffold のquery-teeツールを使って、実際に登録した内容を読み戻してください —go run ./tools/cmd/query-tee -reg <FlareTeeManager address> <your TEE id>(ツールに組み込まれたデフォルトではなく、config/coston2/deployed-addresses.jsonからレジストリアドレスを取得してください)。登録済みのurlと現在配信している URL を、文字単位で比較してください。クイックトンネル(ngrok/Cloudflare クイックトンネル)は再起動のたびに新しいホスト名を割り当てるため、トンネルを再起動するとオンチェーン登録が気づかないうちに使えなくなります。代わりに、名前付き Cloudflare トンネルか、予約済みの ngrok ドメインを使用してください。query-teeでteeIdが空の場合は、想定とは異なるデプロイメントに接続しています。最新のmainを取得し、config/coston2/deployed-addresses.jsonからすべてのアドレスを改めて取得して、pre-build/post-build を再実行してください(再デプロイすると既存の登録は消去されます)。 - プロキシが起動時にパニックを起こす、または TEE の応答を拒否する(
hex string without 0x prefix、verifying response signature: invalid signature、error signing: could not sign):tee-nodeとtee-proxyのバージョンがずれています。FCC の開発中は、両者のワイヤーフォーマットが変更されることがあります。片方だけを更新しないでください。Go モジュールの固定バージョンとproxy/DockerfileのTEE_PROXY_VERSIONは同時に更新する必要があります。固定バージョンではなく、ディスク上のローカルな兄弟リポジトリからビルドすると、通常はこのずれが発生します。 - 指示 ID があり、ほかにエラーもないのに、指示が拡張機能に届かない — カスタム
OPTypeで、予約済みのF_接頭辞付きOPTypeまたは上記の予約済みコマンド名を再利用している可能性があります。これはローカルのペア検証をエラーなしで通過するため、指示は通知されないまま届きません。 - インデクサー DB の認証/接続に失敗する(
Access denied for user 'indexer-reader'、VPN 専用ホストへの接続がタイムアウトする) — ホスト/ポート/データベース名はすでにドキュメントに記載されています(上記の手順 4)。不足しているのは読み取り専用のユーザー名とパスワードのみです。これらはリクエストごとに発行され、定期的にローテーションされます。最新の認証情報はテクニカルサポートまたは X からリクエストしてください。コミュニティチャットで共有された認証情報はドキュメントより早く古くなります。また、古いリポジトリのドキュメントには Coston2 ではなく Coston のインデクサーが記載されていることがあります。Coston のインデクサーは異なる認証情報を使用し、VPN が必要ですが、Coston2 には不要です。 - Web2Json 検証リクエストが Weather Insurance の例にある OpenWeatherMap URL に対してのみ失敗する — 検証ツールに問題があると判断する前に、OpenWeatherMap のプランを確認してください。Weather Insurance Extension の例では、有料の OpenWeatherMap プランが必要なエンドポイントを呼び出すため、同じ検証ツールがほかの URL では正しく動作していても、無料プランのキーでは失敗します。
- 問い合わせる際は、拡張機能 ID、TEE マシンアドレス、失敗した指示 ID、
query-teeの出力を含めてください。この 4 つの情報があれば、通常はすぐに原因を特定できます。
別のスキルを使用する場合
- FTSO の価格フィード、FDC のアテステーション、FAssets のミント/償還、または Smart Accounts を扱う場合は、それぞれの専用スキルを使用してください。
- ネットワーク全般の情報(チェーン ID、RPC、フォーセット、エクスプローラー)については、
flare-generalを使用してください。
機密性の高い/TEE での実行に関するタスク(拡張機能の構築やデプロイ、InstructionSender/レジストリパターン、アテステーションとコードハッシュのホワイトリスト登録、TEE 上でのパラメトリック保険、または fce-extension-scaffold/fce-sign/fce-weather-insurance リポジトリ)には、このスキルを使用してください。公式の Flare Developer Hub FCC ドキュメントについては、dev.flare.network/fcc も参照してください。