# Chainlink Functions

> Chainlink Functionsは、ブロックチェーン上のスマートコントラクトが、ブロックチェーンの外（オフチェーン）にある信頼性の高い計算インフラへ安全にアクセスできるようにするサービスです。

- URL: https://cardene.dev/articles/790042a9bd6ceb
- 公開日: 2025-09-17
- 更新日: 2025-09-17
- トピック: Blockchain
- タグ: ブロックチェーン, solidity, ethereum, smartcontract

# はじめに

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

https://amzn.asia/d/gxvJ0Pw

https://www.avalabs.org/

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

https://twitter.com/cardene777

https://chaldene.net/

https://qiita.com/cardene

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

今回は『Chainlink Functions』についてまとめていきます。
以下の公式ドキュメントをもとにまとめていきます。

https://docs.chain.link/chainlink-functions

# 概要

## Chainlink Functionsとは

Chainlink Functionsは、ブロックチェーン上のスマートコントラクトが、ブロックチェーンの外（オフチェーン）にある信頼性の高い計算インフラへ安全にアクセスできるようにするサービスです。
これにより、スマートコントラクトは外部のAPIからデータを取得したり、複雑な計算を実行したりする能力を得ます。

仕組みとしては、まず開発者がスマートコントラクトから、実行したいJavaScriptコードをリクエストとして「**Decentralized Oracle Network (DON)**」と呼ばれる分散型ネットワークに送信します。
DONを構成する各コンピュータ（ノード）は、受け取ったコードをそれぞれ独立したサーバーレス環境で実行します。
すべてのノードの実行結果が集約され、単一の信頼できる最終結果としてスマートコントラクトに返されます。

この仕組みの大きな利点は、開発者自身がChainlinkノードを運用・管理する必要がなくなることです。
また、単一のノードではなく、複数のノードが分散して計算と検証を行うため、ネットワークの一部（少数派）が悪意を持って結果を操作しようとしても、全体の合意（コンセンサス）によって防がれる高い信頼性を確保しています。

さらに、APIキーや個人情報といった秘密にしたい値をリクエストに含めることも可能です。
これらのデータは「**閾値暗号化**」という技術で暗号化されて送信されます。
この暗号は、DONに参加する多数のノードが協力しなければ解読できないため、個々のノードが秘密情報を盗み見ることはできず、非常に高いセキュリティが保たれます。

サービスの利用料金は、LINKトークンで支払います。
開発者は事前にサブスクリプション用のアカウントにLINKを入金しておき、DONがリクエストを処理して結果を返すたびに、そこから料金が差し引かれる仕組みです。

## 利用上の注意

Chainlink Functionsは非常に強力なツールですが、利用にあたっては以下の点に注意が必要です。

Chainlink Functionsは、開発者が自己責任で利用する「**セルフサービス**」ソリューションです。
リクエストとして送信するJavaScriptコードや、そのコードが依存する外部APIの信頼性・安全性は、開発者自身が十分にレビューして判断する必要があります。
コミュニティが作成したサンプルコードも監査されていない可能性があるため、利用前に必ず内容を確認することが求められます。

このサービスは動作や結果について一切の保証はありません。
万が一、開発者のコードのバグや、利用する外部APIの問題によって意図しない結果が生じたとしても、Chainlink Labs、Chainlink Foundation、およびノードオペレーターは一切その責任を負いません。

また、利用するデータソースやAPIが、自身のユースケースに対して十分な品質と可用性（安定して稼働しているか）を持っているかを確認することも重要です。
接続先のデータプロバイダーが定めるライセンス契約や利用規約を遵守する必要があり、違反した場合はChainlink Functionsのアカウントが停止される可能性があります。

## 主なユースケース

Chainlink Functionsは、その柔軟性から非常に幅広い用途で活用できます。

*   **あらゆる公開データへの接続**: 天気予報APIから統計データを取得して保険商品を自動執行したり、スポーツの試合結果をリアルタイムで取得してNFTのデザインを変化させたりすることが可能です。
*   **データ取得と変換**: Twitter APIから特定のキーワードを含む投稿を取得し、その感情（ポジティブかネガティブか）を分析・計算してからスマートコントラクトで利用する、といったデータの加工ができます。
*   **認証が必要なデータソースへの接続**: パスワードなどで保護されたIoTデバイス（スマートウォッチなど）や、企業の基幹システム（ERP）が持つデータにも安全にアクセスできます。
*   **分散型データベースとの連携**: IPFSのような分散型ストレージと連携し、低コストなガバナンス投票システムを構築するなど、オフチェーンでのプロセスを効率化します。
*   **既存のWeb2アプリケーションとの連携**: 従来のWebサービスとスマートコントラクトを組み合わせた、より複雑で高機能な「ハイブリッドスマートコントラクト」を構築できます。
*   **主要なクラウドストレージからのデータ取得**: AWS S3、Firebase、Google Cloud Storageといった、広く使われているWeb2のシステムからデータを直接取得できます。

### コード例と解説

Chainlink Functionsを利用する際のコードは、主に2つの部分から構成されます。

- **Solidityコード**
    ブロックチェーン上で動作し、Chainlink Functionsにリクエストを送信するスマートコントラクト。
- **JavaScriptコード**
    DONの各ノードによってオフチェーンで実行される、実際の処理（APIへのアクセスなど）を記述したソースコード。

以下に、外部APIから現在のイーサリアム（ETH）の価格を日本円（JPY）で取得する簡単な例を示します。

### JavaScriptコード

このコードは、CoinGecko APIにリクエストを送り、ETHの価格を取得して返します。

```javascript
// CoinGecko APIからETHのJPY価格を取得する
const coinGeckoRequest = Functions.makeHttpRequest({
  url: "https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=jpy"
});

// HTTPリクエストを並行して実行
const [response] = await Promise.all([
  coinGeckoRequest
]);

// エラーチェック
if (!response.error) {
  // レスポンスのJSONから価格を取得
  const price = response.data.ethereum.jpy;
  // 結果を数値として返す (Solidity側で扱いやすいように)
  return Functions.encodeUint256(price);
} else {
  // エラーが発生した場合はエラーコードを返す
  return Functions.encodeUint256(0);
}
```

* `Functions.makeHttpRequest`
    Chainlink Functionsが提供する関数で、外部APIへのHTTPリクエストを定義します。
    `url`パラメータでアクセス先のAPIエンドポイントを指定します。
* `await Promise.all(...)`
    定義したリクエストを非同期で実行して応答を待ちます。
* `response.data.ethereum.jpy`
    APIからのレスポンスはJSON形式で返ってくるため、その中から`data`オブジェクト、`ethereum`オブジェクト、そして`jpy`プロパティをたどって価格データを取り出しています。
* `Functions.encodeUint256(price)`
    取得した価格（数値）を、Solidityのスマートコントラクトが解釈できる`uint256`というデータ形式に変換して返却します。
    エラーの場合は`0`を返します。

### Solidityコード

このスマートコントラクトは、上記JavaScriptコードの実行をDONに依頼して結果を受け取ります。

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import { FunctionsClient } from "@chainlink/contracts/src/v0.8/functions/v1_0_0/FunctionsClient.sol";
import { FunctionsRequest } from "@chainlink/contracts/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol";

contract MyFunctionsConsumer is FunctionsClient {
    using FunctionsRequest for FunctionsRequest.Request;

    bytes32 public latestRequestId;
    bytes public latestResponse;
    bytes public latestError;

    // 実行するJavaScriptコード
    string private source = "/* ここに上記のJavaScriptコードを文字列として記述 */";
    // サブスクリプションID
    uint64 private subscriptionId = 123;
    // ガスリミット
    uint32 private gasLimit = 300000;

    constructor() FunctionsClient(msg.sender) {}

    // Chainlink Functionsにリクエストを送信する関数
    function sendRequest() external returns (bytes32 requestId) {
        FunctionsRequest.Request memory req;
        req.initializeRequestForInlineJavaScript(source); // JavaScriptコードを初期化
        
        // リクエストを送信
        latestRequestId = _sendRequest(req.encodeCBOR(), subscriptionId, gasLimit, 0xac7a68e6488d5f309b43c5f2b801457884d34a4a49c8112e525164627a8103e3);
        return latestRequestId;
    }

    // DONから結果を受け取るためのコールバック関数
    function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override {
        latestResponse = response;
        latestError = err;
    }
}
```

* `import`
    Chainlinkが提供するコントラクトとライブラリをインポートして、Functionsの機能を使えるようにします。
* `source`
    DONに実行させたいJavaScriptコードを文字列として格納します。
* `subscriptionId`
    料金支払いのために事前に作成・入金しておいたサブスクリプションのIDです。
* `gasLimit`
    Functionsのリクエストが消費するガスの最大量を指定します。
* `sendRequest()`
    外部から呼び出すことができる関数で、リクエストの準備と送信を行います。
    * `req.initializeRequestForInlineJavaScript(source)`
        `source`に格納したJavaScriptコードを使ってリクエストを初期化します。
    * `_sendRequest(...)`
        `FunctionsClient`コントラクトから継承した内部関数で、実際にDONへリクエストを送信します。
* `fulfillRequest(...)`
    DONがリクエストの処理を完了した後に、結果を返すために呼び出される「**コールバック関数**」です。
    `response`パラメータにJavaScriptコードが返した実行結果（この例ではETH価格）が、`err`パラメータにエラー情報が格納されます。
    これらの値を`latestResponse`などの変数に保存することで、後から結果を確認できます。

# アーキテクチャ

## Chainlink Functionsのアーキテクチャ

### データのリクエストと受信の仕組み

Chainlink Functionsのデータリクエストと受信のモデルは、Chainlinkの基本的なリクエストモデルと似ています。
全体の流れは、まず開発者が作成した「**コンシューマーコントラクト**」がリクエストを開始することから始まります。
このリクエストは**FunctionsRouter**コントラクトに送られます。
その後、分散型オラクルネットワーク（DON）を構成する各オラクルノードが、ブロックチェーン上で発行されるイベントを監視し、オフチェーン（ブロックチェーンの外）で要求された計算処理を実行します。
最後に、全ノードの計算結果がChainlink OCRプロトコルという技術によって1つに集約され、信頼性の高い単一の応答として、コンシューマーコントラクトの「**コールバック関数**」に返されます。

この仕組みを構成する主要なコンポーネントは以下す。

* **Initiator（開始者）**
    Functionsへのリクエストを開始する主体です。
    これは人間が操作する通常のウォレットアカウント（EOA）であったり、定期的なタスク実行を自動化するChainlink Automationであったりします。
* **Consumerコントラクト**
    開発者がデプロイするスマートコントラクトで、**FunctionsRouter**コントラクトとやりとりしリクエストを開始する役割を持ちます。
* **FunctionsRouterコントラクト**
    サブスクリプションの管理を行い、コンシューマーからのリクエストを受け付ける窓口となるコントラクトです。
    開発者はこのコントラクトの`sendRequest`メソッドを呼び出してリクエストを開始します。
* **FunctionsCoordinatorコントラクト**
    分散型オラクルネットワーク（DON）とのインターフェースとして機能するコントラクトです。
    オラクルノードは、このコントラクトが発行するイベントを監視し、応答を送信する時にもこのコントラクトとやりとりします。
* **DON（分散型オラクルネットワーク）**
    Chainlink Functionsのコアであり、それぞれ独立したオラクルノードの集合体です。
    各ノードはリクエストで指定されたソースコードを実行し、その結果をOCRプロトコルを用いて1つに集約します。
    最終的に、DONの中から選ばれた1つのノードが集約された応答をコンシューマーコントラクトに届けます。
* **シークレットエンドポイント**
    APIキーなどの秘密情報をDONに安全に渡すためのサービスです。
    ユーザーはDONの公開鍵で秘密情報を暗号化し、このエンドポイントにアップロードします。
    これにより、暗号化された情報が安全に各ノードに共有されます。
    あるいは、ユーザー自身がサーバーを立て、暗号化された秘密情報を取得できるURLを提供する自己ホスト方式も選択できます。
* **サーバーレス環境**
    各オラクルノードは、計算を実行するために、他から隔離された安全なサンドボックス環境にアクセスします。
    この環境では、APIへのリクエストだけでなく、数学的な計算などモジュールのインポートを伴わない純粋なDenoコード（JavaScript/TypeScript実行環境）であれば、どのような計算でも実行可能です。
    ただし、DONを構成する全てのノードが全く同じ計算を同時に実行するため、アクセス先のAPIにリクエスト回数制限（スロットリング）がある場合は注意が必要です。

次に、これらのコンポーネントが連携してリクエストを処理する一連の流れを説明します。

![データのリクエストと受信の仕組みの図](https://storage.googleapis.com/zenn-user-upload/59214ed8fbcc-20250917.png)
https://docs.chain.link/chainlink-functions/resources/architecture

1. **シークレットの準備**
    リクエストに秘密情報（APIキーなど）を含める場合、ユーザーはまずDONのマスター公開鍵を使ってその情報を暗号化し、シークレットエンドポイントにアップロードします。
    これにより、暗号化された秘密情報がDONの各ノードに安全に配布されます。
2. **リクエストの開始**
    EOAまたはChainlink Automationが、コンシューマーコントラクトの特定の関数を呼び出し、リクエストプロセスを開始します。
3. **コンシューマーからルーターへ**
    コンシューマーコントラクトは、応答を受け取るために**FunctionsClient**コントラクトを継承している必要があります。
    リクエストを受け取った**FunctionsRouter**コントラクトは、まずリクエストの実行にかかるコストを見積もり、その分のLINKトークンをサブスクリプションの残高から一時的に確保（予約）します。
    その後、**FunctionsCoordinator**コントラクトを呼び出します。
4. **イベントの発行**
    **FunctionsCoordinator**コントラクトは、リクエストの詳細情報を含む`OracleRequest`というイベントを発行します。
5. **DONによる処理実行**
    DONの各ノードはこのイベントを検知します。
    シークレットが指定されている場合、ノード群は「**閾値復号**」という技術を用いて協力して秘密情報を復号します。
    その後、各ノードはそれぞれ独立したサーバーレス環境で、リクエストで指定されたソースコードを実行します。
6. **結果の集約**
    DONはOCRプロトコルを実行し、各ノードがソースコードを実行して得た個々の結果を単一の信頼できる値に集約します。
7. **集約結果の送信**
    DON内の1つのオラクルノードが、集約された応答を含む証明付きのレポートを**FunctionsCoordinator**コントラクトに送信します。
8. **最終的なコスト計算**
    **FunctionsCoordinator**コントラクトは、**FunctionsRouter**コントラクトの`fulfill`メソッドを呼び出し、リクエストの実行に実際にかかったコストを計算して課金を確定させます。
9. **応答の返却**
    最後に、**FunctionsRouter**コントラクトがコンシューマーコントラクトのコールバック関数（`handleOracleFulfillment`など）を呼び出し、集約された最終的な応答を届けます。

## サブスクリプションの管理

Chainlink Functionsでは、リクエストごとにLINKトークンを支払うのではなく、サブスクリプション方式を採用しています。
開発者は事前にサブスクリプションアカウントを作成し、そこにLINKトークンを入金しておくことで、自身のコンシューマーコントラクトはLINKを直接保有することなく、リクエストを発行できます。

### 主要な概念

* **Terms of service (ToS)**
    Chainlink Functionsを利用する前に、ユーザーは利用規約に同意する必要があります。
    同意したアカウントは許可リストに登録され、サブスクリプションの管理が可能になります。
* **Chainlink Functions Subscription Manager**
    利用規約への同意やサブスクリプションの作成・管理を行うためのウェブ上のユーザーインターフェースです。
* **Subscription account**
    リクエストの支払いに使われるLINKトークンを保持するためのアカウントです。
    各アカウントは固有のSubscription IDによって識別されます。
* **Subscription ID**
    サブスクリプションアカウントを一位に特定するための64ビットの整数値です。
* **Subscription owner**
    サブスクリプションアカウントを作成し、管理する権限を持つウォレットアドレスです。
    LINKの入金は誰でも可能ですが、コンシューマーの追加・削除や資金の引き出しはオーナーしか行えません。
* **Subscription balance**
    サブスクリプションアカウントに入金されているLINKの総量です。
* **Subscription reservation**
    現在処理中のリクエスト（in-flight requests）のために、残高から一時的に確保されているLINKの量です。
* **Effective balance**
    実際に新規リクエストに利用可能なLINKの残高です。
    これは「**Subscription balance**」から「**Subscription reservation**」を差し引いた額になります。
* **Subscription consumers**
    そのサブスクリプションアカウントの資金を使ってリクエストを行うことを許可されたコンシューマーコントラクトのリストです。

### サブスクリプションの操作

#### 利用規約への同意

![利用規約への同意の図](https://storage.googleapis.com/zenn-user-upload/2b44e05fa875-20250917.png)
https://docs.chain.link/chainlink-functions/resources/architecture

サブスクリプションを管理するアカウントは、まず利用規約への同意が必要です。
Subscription Managerを通じて署名を行うことで、そのアカウントは許可リストのコントラクトに登録されます。

#### サブスクリプションの作成

![サブスクリプションの作成の図](https://storage.googleapis.com/zenn-user-upload/1a19cac0de21-20250917.png)
https://docs.chain.link/chainlink-functions/resources/architecture

利用規約に同意したアカウントは、サブスクリプションを作成できます。
作成すると、一意のSubscription IDが割り当てられます。

#### サブスクリプションへの資金供給

![サブスクリプションへの資金供給の図](https://storage.googleapis.com/zenn-user-upload/42b2d04b8bd4-20250917.png)
https://docs.chain.link/chainlink-functions/resources/architecture

Subscription Managerを通じて、LINKトークンをサブスクリプションアカウントに送金します。
内部的には、LINKトークンコントラクトの`transferAndCall`関数が呼び出され、送金と同時にどのサブスクリプションに入金するかを指定しています。

#### コンシューマーの追加

![コンシューマーの追加の図](https://storage.googleapis.com/zenn-user-upload/23c992d82559-20250917.png)
https://docs.chain.link/chainlink-functions/resources/architecture

作成したコンシューマーコントラクトがサブスクリプションを利用できるように、そのコントラクトのアドレスをサブスクリプションに追加（許可リストに登録）する必要があります。

#### コンシューマーの削除

![コンシューマーの削除の図](https://storage.googleapis.com/zenn-user-upload/25abac1d3e03-20250917.png)
https://docs.chain.link/chainlink-functions/resources/architecture

特定のコントラクトからのリクエストを停止させたい場合は、サブスクリプションからそのコントラクトを削除します。
削除時点で処理中のリクエストがあったとしても、その処理は続行され、課金も行われます。

#### サブスクリプションのキャンセル

![サブスクリプションのキャンセルの図](https://storage.googleapis.com/zenn-user-upload/7592b14459da-20250917.png)
https://docs.chain.link/chainlink-functions/resources/architecture

不要になったサブスクリプションはキャンセルできます。
その時、残っているLINKトークンの送金先アドレスを指定します。
ただし、処理中のリクエストや、応答がないまま5分以上経過した「**期限切れ**」のリクエストが存在する場合はキャンセルできません。

#### 所有権の移転

サブスクリプションの所有権を別のアカウントに移転することも可能です。
現時点では、この操作は専用のコマンドラインツール（Hardhat Starter kitなど）を通じて行います。
移転先の新しいオーナーも、事前に利用規約に同意している必要があります。

# 処理フロー

```mermaid
sequenceDiagram
    participant Initiator as 開始者<br>(EOA/Automation)
    participant Consumer as コンシューマー<br>コントラクト
    participant Router as Functions<br>Router
    participant Coordinator as Functions<br>Coordinator
    participant DON as 分散オラクル<br>ネットワーク(DON)
    participant Secrets as シークレット<br>エンドポイント

    opt 準備フェーズ (秘密情報利用時)
        Initiator->>+Secrets: 1. 暗号化した秘密情報をアップロード
        Secrets-->>-DON: 2. 暗号化された秘密情報を配布
    end

    note over Initiator,Secrets: リクエストフェーズ
    Initiator->>+Consumer: 3. リクエスト関数を呼び出し
    Consumer->>+Router: 4. sendRequest() を実行
    Router->>Router: 5. コストを見積もり、<br>サブスクリプション残高を予約
    Router->>+Coordinator: 6. リクエストを転送
    Coordinator-->>DON: 7. OracleRequestイベントを発行
    deactivate Coordinator
    deactivate Router
    deactivate Consumer

    note over Initiator,Secrets: オフチェーン実行フェーズ
    DON->>DON: 8. (必要なら)閾値復号で秘密情報を復号
    DON->>DON: 9. 各ノードがソースコードを実行
    DON->>DON: 10. OCRプロトコルで結果を集約

    note over Initiator,Secrets: コールバックフェーズ
    DON->>+Coordinator: 11. 集約したレポートを送信
    Coordinator->>+Router: 12. fulfill() を呼び出し、最終コストを計算
    deactivate Coordinator
    Router->>Router: 13. 課金を確定
    Router->>+Consumer: 14. コールバック関数を呼び出し、<br>最終的な応答を渡す
    Consumer->>Consumer: 15. 受け取った応答を処理・保存
    deactivate Consumer
    deactivate Router
```

## 準備フェーズ

このフェーズは、APIキーなどの秘密情報をリクエストに含めたい場合にのみ実行される、任意のステップです。

* **1. 暗号化した秘密情報をアップロード**
    開始者（ユーザーのウォレットや自動実行サービス）は、DONの公開鍵を使って秘密情報を暗号化し、その暗号データをシークレットエンドポイントにアップロードします。

* **2. 暗号化された秘密情報を配布**
    シークレットエンドポイントは、受け取った暗号化データをDONを構成する各オラクルノードに安全に配布します。

## リクエストフェーズ

スマートコントラクトが外部の計算やデータ取得を依頼する最初のステップです。

* **3. リクエスト関数を呼び出し**
    開始者が、Functionsへのリクエストを目的として開発者が作成したコンシューマーコントラクト内の特定の関数を呼び出します。
    これが一連のプロセスの起点となります。

* **4. sendRequest() を実行**
    コンシューマーコントラクトは、受け取った指示に基づきFunctions Routerコントラクトの `sendRequest` 関数を呼び出します。
    この時に、実行したいJavaScriptコードや参照する秘密情報などを引数として渡します。
* **5. コストを見積もり、サブスクリプション残高を予約**
    Routerコントラクトは、リクエストの実行にかかるおおよそのコスト（ガス代など）を見積もり、ユーザーのサブスクリプション残高からその金額を一時的に確保（予約）します。
* **6. リクエストを転送**
    RouterコントラTラクトは、リクエストの情報をFunctions Coordinatorコントラクトに転送します。
    CoordinatorはDONとの直接のやりとりを担当します。
* **7. OracleRequestイベントを発行**
    Coordinatorコントラクトは、リクエストの詳細（実行するコード、パラメータなど）を含む `OracleRequest` というイベントをブロックチェーン上で発行（emit）します。
    このイベントが、オフチェーンで待機しているDONに対して、タスク実行を開始するためのトリガーとなります。

## オフチェーン実行フェーズ

ブロックチェーンの外で、実際の計算処理が行われるコア部分です。

* **8. (必要なら)閾値復号で秘密情報を復号**
    DONの各ノードは `OracleRequest` イベントを常に監視しており、イベントを検知すると処理を開始します。
    リクエストに秘密情報が含まれている場合、各ノードが協力する「**閾値復号**」というプロセスを経て、安全に秘密情報を復号します。

* **9. 各ノードがソースコードを実行**
    各ノードは、それぞれ独立した安全な環境で、リクエストで指定されたJavaScriptコードを実行します。
    APIへのアクセスやデータの計算はここで行われます。

* **10. OCRプロトコルで結果を集約**
    各ノードが出した実行結果を、OCR（Off-Chain Reporting）プロトコルという仕組みを使って1つにまとめ上げます。
    これにより、単一のノードの誤りや不正を防ぎ、信頼性の高い単一の最終結果を生成します。

## コールバックフェーズ

オフチェーンで得られた結果を、ブロックチェーン上のスマートコントラクトに返す最終ステップです。

* **11. 集約したレポートを送信**
    DONは、集約された最終結果とその結果が正当であることを証明する電子署名を含んだレポートを作成し、Functions Coordinatorコントラクトに送信します。

* **12. fulfill() を呼び出し、最終コストを計算**
    レポートを受け取ったCoordinatorコントラクトは、その内容を検証してFunctions Routerコントラクトの `fulfill` 関数を呼び出します。
    この時点で、リクエストの実行に実際に掛かった正確なコストが計算されます。

* **13. 課金を確定**
    Routerコントラクトは、計算された最終コストを事前に予約していたサブスクリプション残高から正式に差し引きます。
    予約額より実際のコストが少なければ、差額はユーザーに返還されます。

* **14. コールバック関数を呼び出し、最終的な応答を渡す**
    Routerコントラクトは、リクエストの依頼元であるコンシューマーコントラクトに定義されたコールバック関数（例：`handleOracleFulfillment`）を呼び出し、DONから得られた最終的な応答（計算結果）を渡します。

* **15. 受け取った応答を処理・保存**
    コンシューマーコントラクトは、コールバック関数を通じて受け取ったデータを、コントラクト内の変数に保存したり、その結果をトリガーとして別の動作を開始したりするなど目的に応じて利用します。
    これで一連のフローが完了します。

# 秘密情報の管理

:::message
Chainlink Functionsはまだベータ版です。
リクエストで秘密情報を利用する機能は実験的なものであり、期待通りに動作しない可能性や将来的に仕様が変更される可能性があります。
この機能の利用は自己責任であり、予期せぬエラー、新バージョンのリリースに伴う秘密情報漏洩の可能性、その他の問題が発生するリスクがあります。
:::

## 秘密情報の管理

Chainlink Functionsは、HTTP(s) APIからデータを取得し、独自の計算を実行する機能を提供します。
APIの中には、アクセスするためにAPIキーやパスワードなどの認証情報を必要とするものがあります。
このようなAPIに対応するため、Chainlink Functionsでは暗号化された秘密情報をリクエストに含めることができます。

この秘密情報は、DON（分散型オラクルネットワーク）がリクエストを実行する時に利用されます。
秘密情報は「**閾値復号（threshold decryption）**」と呼ばれる、複数の参加者を必要とするプロセスでしか復号（元の情報に戻すこと）できません。
これは、DONを構成する個々のノードが単独で秘密情報を解読することはできず、他の多くのノードと協力して初めて解読できる仕組みであることを意味し、高いセキュリティを確保しています。

暗号化された秘密情報は、DONに直接アップロードするか、あるいはユーザーが管理するリモートのURLに配置してその場所への参照情報をリクエストに含める形で利用します。

## 閾値暗号化

もし、ユーザーの秘密情報を復号するために単一の秘密鍵を使用した場合、その鍵を持つノードが悪意を持ったり攻撃によって乗っ取られたりすると、アクセス可能なすべてのユーザーの秘密情報が解読されてしまうという非常に大きなセキュリティリスクが存在します。

この脆弱性に対処するため、Chainlink Functionsには**閾値暗号化**という仕組みが統合されています。
この強化されたセキュリティ対策には、以下のような特徴があります。

* **分散化されたノード**
    システムはN個のノードから成る分散型オラクルネットワーク（DON）上で動作します。
* **マスター秘密鍵の分割**
    秘密情報を復号するための「**マスター秘密鍵（MSK）**」を単一の場所に保管するのではなく、複数の断片（シェア）に分割します。
    DON内の各ノードは、それぞれ異なる断片を1つだけ保持します。

このシステムは、2つの重要な閾値（しきいち、基準となる値）を設けています。

- **可用性**
    システムが安定して稼働し続けるための耐障害性を保証します。
    この仕組みにより、最大でF個のビザンチンノード（悪意のある、あるいは予測不能な振る舞いをするノード）が存在しても、システム全体は正常に機能し続けます。
    これは `3F+1 ≤ N` という数式で表され、全体のノード数(N)は、障害ノード数(F)の3倍に1を加えた数以上でなければならないことを意味します。
- **復号の安全性**
    マスター秘密鍵（MSK）そのものを復元するためには、攻撃者はK個のノードを乗っ取る必要があります。
    このKは `F < K ≤ 2F+1` という関係を満たします。
    つまり、単に障害を起こすノード数(F)を超える数のノードを掌握しなければ、鍵自体を復元することはできません。

#### 暗号化と復号のプロセス

ユーザーは、マスター秘密鍵に対応する「**公開鍵**」を使って自身の秘密情報を暗号化して暗号文を生成します。
この暗号化された秘密情報がブロックチェーン上に保存されることは一切ありません。
この暗号文を復号するには、`F < K ≤ 2F+1` の関係を満たす、最低K個のノードが協力して復号処理を行う必要があります。
つまり、K個以上のノードが、同じ暗号文を含むリクエストを同時に処理した場合にのみ、復号が開始される仕組みです。

## ホスティング方法

暗号化された秘密情報をDONと共有するには、2つの方法があります。

- **DONホスト型 (DON-hosted)**
    ユーザーは、暗号化された秘密情報（暗号文）を、DONが提供する専用の「**シークレットエンドポイント**」に直接送信します。
    これは最も手軽な方法ですが、この機能を利用するためにはユーザーのアカウントが所有するサブスクリプションのいずれか1つが、常に一定額以上の残高を維持している必要があります。
- **ユーザーホスト型 (User-hosted)**:
    ユーザーによっては、リクエストが完了したらすぐに秘密情報を削除するなど、データの保管やライフサイクルを自身で厳密に管理したい場合があります。
    この方式では、ユーザーは暗号化された秘密情報（暗号文）を、自身が管理する公開アクセス可能なHTTP(s)サーバー上に配置します。
    そして、そのサーバーのURLをDONの公開鍵でさらに暗号化し、その暗号化されたURLをDONに渡してリクエストを行います。
    リクエストの処理が完了した後、サーバー上の暗号文を削除する責任はユーザー自身にあります。

# 最後に

今回は『Chainlink Functions』についてまとめてきました。

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

https://amzn.asia/d/gxvJ0Pw

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://qiita.com/cardene

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://chaldene.net/

https://twitter.com/cardene777

https://cardene.substack.com/
