> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 圧縮フィルタを用いた大規模アカウントフィルタリング

> 圧縮カッコウフィルタを使用して、単一のLaserStream gRPCストリームで何十万ものSolanaアカウントを購読 — ローカル検証で約8倍小さな購読リクエスト。

## 概要

LaserStreamは**カッコウフィルタを介した圧縮アカウントフィルタリング**をサポートしています。subscribeリクエストに明示的なpubkeyリスト（アカウントあたり32バイト）を送信する代わりに、コンパクトな確率的フィルタを送信し、ワイヤー上でアカウントあたり約**3〜4バイト**かかります。

これにより、**単一のストリームで何十万ものアカウントを購読することが実用的**になります。接続を分割する必要がなく、大きすぎる購読リクエストもありません。

例えば、50万アカウントを追跡するフィルタは、約2.1 MBにシリアル化されますが、raw pubkeyリストとしては16 MBにもなります — **約7.6倍小さい**です。正確な節約量はフィルタの満杯度に依存します。容量に近いほど、アカウントあたりのバイト数が少なくなります。

### 利用可能性

| クライアント                                                         | 最小バージョン | カッコウサポート |
| -------------------------------------------------------------- | ------- | -------- |
| LaserStream SDK — Rust (`helius-laserstream`)                  | 0.2.0   | ✅        |
| LaserStream SDK — JavaScript/TypeScript (`helius-laserstream`) | 0.4.0   | ✅        |
| LaserStream SDK — Go                                           | —       | ❌ まだ     |
| Yellowstone gRPC — Rust (`yellowstone-grpc-client`)            | 13.1.0  | ✅        |

## カッコウフィルタを使用する時

| 追跡するアカウント       | 推奨アプローチ                                    |
| --------------- | ------------------------------------------ |
| 約10,000アカウントまで  | 明示的なpubkeyリスト (`account: [...]`) — シンプルで正確 |
| \~10,000アカウント以上 | `CompressedAccountFilterSet`を介したカッコウフィルタ   |

一般的な使用例：トークンのすべての保有者の監視、貸付プロトコルのすべてのポジションの追跡、取引または解析システムのための大規模なウォレットセットの監視。

## 仕組み

1. **クライアント側でフィルタを作成します。** 各追跡pubkeyを`CompressedAccountFilterSet`に挿入します。ハッシュシードはフィルタごとにランダム化され、一緒にシリアル化されるため、サーバーはクライアントが使用したのと同じシードを使用して着信アカウントをハッシュします。
2. **subscribeリクエストに添付します。** `insert_into_subscribe_request()`はシリアル化されたフィルタを標準の`SubscribeRequest`のアカウントストリームに配置します。
3. **サーバーは確率的に一致します。** フィルタは確率的なため、追跡していないアカウントのアップデートをサーバーが配信する場合があります — フルロード時の偽陽性は**1%未満に制限されています**。**偽陰性は決してありません**：追跡されたアカウントのすべてのアップデートが配信されます。
4. **各更新をローカルで再確認 — このステップは必須です。** 処理する前に着信アカウントごとに`set.contains(pubkey)`を呼び出します。このチェックは正確（内部ハッシュセットによって裏付けられている）なので、ローカルフィルタリング後には偽陽性はゼロになります。

## クイックスタート（Rust）

プロジェクトにSDKを追加します。

```toml Cargo.toml theme={"system"}
[dependencies]
helius-laserstream = "0.2"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
```

フィルタを構築し、サブスクリプションに添付し、偽陽性をローカルでスクラブします。

```rust main.rs [expandable] theme={"system"}
use {
    futures::StreamExt,
    helius_laserstream::{
        cuckoo::{CompressedAccountFilterSet, Pubkey},
        grpc::{subscribe_update::UpdateOneof, SubscribeRequest},
        subscribe, LaserstreamConfig,
    },
    std::str::FromStr,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // The exact set of accounts you care about. In production this is
    // typically loaded from your database — hundreds of thousands of keys.
    let tracked: Vec<Pubkey> = [
        "So11111111111111111111111111111111111111112", // Wrapped SOL
        "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
        "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", // USDT
    ]
    .iter()
    .map(|s| Pubkey::from_str(s).unwrap())
    .collect();

    // Build the cuckoo filter. Size it for your peak tracked-set size.
    let mut set = CompressedAccountFilterSet::with_capacity(500_000)?;
    for pk in &tracked {
        set.insert(*pk)?;
    }
    println!(
        "Tracking {} accounts via cuckoo filter ({} bytes on the wire)",
        set.len(),
        set.to_proto().data.len()
    );

    // Attach the compressed filter to the accounts stream.
    let mut request = SubscribeRequest::default();
    set.insert_into_subscribe_request(&mut request, "tracked_accounts");

    let config = LaserstreamConfig::new(
        "https://laserstream-mainnet-ewr.helius-rpc.com".to_string(), // Choose your closest region
        "YOUR_API_KEY".to_string(), // Replace with your key from https://dashboard.helius.dev/
    );

    let (stream, _handle) = subscribe(config, request);
    tokio::pin!(stream);
    while let Some(message) = stream.next().await {
        match message {
            Ok(update) => {
                if let Some(UpdateOneof::Account(account_update)) = update.update_oneof {
                    if let Some(info) = account_update.account {
                        let pk = Pubkey::try_from(info.pubkey.as_slice()).ok();
                        // Re-check locally: drop server-side false positives.
                        match pk {
                            Some(pk) if set.contains(pk) => {
                                println!(
                                    "tracked account update: {pk} (slot {})",
                                    account_update.slot
                                );
                            }
                            Some(pk) => {
                                println!("(false positive, ignored): {pk}");
                            }
                            None => {}
                        }
                    }
                }
            }
            Err(e) => eprintln!("stream error: {e}"),
        }
    }

    Ok(())
}
```

完全実行可能なバージョンはSDKに付属しています：[`rust/examples/cuckoo_account_filter.rs`](https://github.com/helius-labs/laserstream-sdk/blob/main/rust/examples/cuckoo_account_filter.rs)。

## クイックスタート（JavaScript/TypeScript）

SDKをインストールします（カッコウサポートには`helius-laserstream` 0.4.0以上が必要です）：

```bash theme={"system"}
npm install helius-laserstream
```

フィルタを作成し、添付し、各更新をローカルで再確認します。

```typescript [expandable] theme={"system"}
import {
  subscribe,
  CommitmentLevel,
  CompressedAccountFilterSet,
  SubscribeUpdate,
  LaserstreamConfig,
} from 'helius-laserstream';

async function main() {
  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // Replace with your key from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // Choose your closest region
  };

  // The accounts you want to track. In production this is typically loaded
  // from your database — hundreds of thousands of keys.
  const addresses = [
    'So11111111111111111111111111111111111111112', // Wrapped SOL
    'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
    'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', // USDT
  ];

  // Build a compact cuckoo filter instead of sending the full pubkey list.
  // Size capacity for your peak tracked-set size.
  const tracked = new CompressedAccountFilterSet(500_000);
  for (const address of addresses) {
    tracked.insert(address);
  }

  // Attach the filter to the request (no explicit account list needed).
  const request: any = { accounts: {}, commitment: CommitmentLevel.CONFIRMED };
  tracked.insertIntoSubscribeRequest(request, 'tracked-accounts');

  const stream = await subscribe(
    config,
    request,
    async (update: SubscribeUpdate) => {
      const pubkey = update.account?.account?.pubkey;
      if (!pubkey) return;
      // Re-check locally: drop server-side false positives. This is exact.
      if (tracked.contains(pubkey)) {
        console.log('tracked account update:', update.account);
      }
    },
    (error: Error) => {
      console.error('Stream error:', error);
    }
  );

  process.on('SIGINT', () => {
    stream.cancel();
    process.exit(0);
  });
}

main().catch(console.error);
```

完全実行可能なバージョンはSDKに付属しています：[`javascript/examples/cuckoo-account-sub.ts`](https://github.com/helius-labs/laserstream-sdk/blob/main/javascript/examples/cuckoo-account-sub.ts)。

## APIリファレンス

`CompressedAccountFilterSet`は生のカッコウフィルタと正確なハッシュセットを一緒にラップし、操作とメンバーシップチェックが常に安全で正確です。

| メソッド                                                   | 振る舞い                                                                                 |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `with_capacity(n)`                                     | `n`の追跡アカウントのためにサイズを調整してフィルタを作成します。**ピークトラッキングセットサイズ**のためにサイズを設定します。                  |
| `insert(pubkey)`                                       | 新規の場合は`Ok(true)`を返し、重複の場合は`Ok(false)`を返し、フィルタが容量に達している場合は`Err(TableFullError)`を返します。 |
| `remove(pubkey)`                                       | アカウントを削除します。安全で正確です。                                                                 |
| `contains(pubkey)`                                     | 正確なメンバーシップチェック — サーバー側の偽陽性を除去するために使用します。                                             |
| `insert_into_subscribe_request(&mut request, "label")` | フィルタを`SubscribeRequest`のアカウントストリームに添付します。                                            |
| `to_account_filter()` / `to_proto()`                   | カスタムリクエストアセンブリ用の低レベル変換。                                                              |
| `is_dirty()` / `take_dirty()`                          | セットがリクエストに最後に置かれた以降に変更されたかどうかを報告します — 再購読サイクルに便利です。                                  |

上記のメソッド名はRustの慣習を使用しています。JavaScript/TypeScript SDKは同じ表面をキャメルケースで公開しています — `new CompressedAccountFilterSet(capacity)`は`with_capacity`ではなく、`insertIntoSubscribeRequest`、`isDirty`、`takeDirty`、`toProto`などを使用しています。JavaScriptでは`insert`がBoolean（新たに追加された場合は`true`）を返し、フィルタがいっぱいの時に`TableFullError`をスローします。pubkeyはbase58文字列、生の32バイト、または`toBytes()`メソッドを持つ任意のオブジェクトとして渡すことができます。

必ず`CompressedAccountFilterSet`を使用し、生の`CuckooFilter`を使用しないでください。生のフィルタの`remove()`は誤ったアイテムを静かに削除する可能性があります — カッコウフィルタのドキュメント化された注意点です。このラッパーはフィルタを正確なハッシュセットとペアリングしているため、挿入、削除、含有は常に正しいです。

## 容量のサイズ調整

* フィルタのサイズは、`with_capacity(n)`を介して追跡することが期待されるアカウントの**ピーク**数に基づいて調整します。
* 容量を超えて挿入すると、安全に`TableFullError`が返され、フィルタは破損しません。実際には、テーブルは挿入を拒否する前にわずかなオーバーフィルを許容しますが、その余地に依存しないでください。
* シリアル化されたサイズは、挿入したアカウント数ではなく容量によって決まるため、過剰サイズのフィルタはワイヤーバイトを無駄にします。実際のピークに近い容量を選択してください。

## 追跡セットの更新

追跡セットが変更された場合（フォローすべき新しいアカウント、削除すべき古いアカウント）：

1. `insert()` / `remove()`を`CompressedAccountFilterSet`で呼び出します。
2. フィルタが最後に送信されて以来変更されたかどうかを確認するために`is_dirty()`をチェックします（または`take_dirty()`を使用してフラグを消費します）。
3. 変更があれば、`insert_into_subscribe_request()`でリクエストを再構築します。JavaScriptでは、`stream.write(request)`で同じストリームで再送信できます。Rustでは、再構築されたリクエストを使用して再購読します。

## FAQ

<Accordion title="フィルタ内のアカウントの更新を見逃すことはありますか？">
  いいえ。カッコウフィルタは偽陽性（追跡していないアカウントの追加の更新）を生成しますが、**偽陰性はありません**。追跡されたアカウントのすべての更新が配信されます。
</Accordion>

<Accordion title="何件の追加（偽陽性）更新を受け取りますか？">
  フルロード時で1%未満、通常はフィルタが容量以下の場合は少なくなります。ローカルの`contains()`呼び出しを各更新に行うことで、正確にフィルタリングします。
</Accordion>

<Accordion title="どのクライアントがカッコウフィルタをサポートしていますか？">
  Rust SDK (`helius-laserstream` 0.2.0+)、JavaScript/TypeScript SDK (`helius-laserstream` 0.4.0+)、Yellowstone Rust クライアント (`yellowstone-grpc-client` 13.1.0+) がサポートしています。Go SDKはまだサポートしていません。[利用可能性テーブル](#availability)を参照してください。
</Accordion>

<Accordion title="明示的なpubkeyリストを利用できますか？">
  はい。標準の`account: [...]`フィルタは変更なしで機能し、少数のアカウントセット（約10,000アカウントまで）には適した選択のままです。[アカウントサブスクリプションガイド](/ja/laserstream/guides/account-subscription)を参照してください。
</Accordion>

## 関連情報

<CardGroup cols={2}>
  <Card title="アカウントサブスクリプション" icon="user" href="/ja/laserstream/guides/account-subscription">
    所有者、サイズ、memcmpフィルタによる標準アカウントフィルタリング。
  </Card>

  <Card title="クライアントとSDK" icon="code" href="/ja/laserstream/clients">
    自動リプレイと再接続を備えたTypeScript、Rust、Go SDK。
  </Card>
</CardGroup>
