> ## 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 支持**通过布谷鸟过滤器的压缩账户过滤**。与其在订阅请求中发送显式pubkey列表（每个账户32字节），不如发送一个紧凑的概率过滤器，其在网络上的成本大约为**每个账户3–4字节**。

这样一来，就可以实现**在单个流中订阅数十万个账户**——无需跨连接分片，也无需过大的订阅请求。

例如，跟踪 500,000 个账户的过滤器序列化后大约为 2.1 MB，而作为原始 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. **将其附加到订阅请求中。** `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` 返回布尔值（如果新添加则为 `true`），当过滤器饱和时抛出 `TableFullError`。可以将 pubkey 作为 base58 字符串、原始 32 字节或任何具有 `toBytes()` 方法的对象传递。

始终使用 `CompressedAccountFilterSet` 而不是其封装的原始 `CuckooFilter`。原始过滤器的 `remove()` 可能会静默移除错误的项目——这是布谷鸟过滤器的一个已知风险。此包装器将过滤器与精确哈希集合配对，因此插入、移除和包含检查始终正确。

## 容量大小调整

* 根据您期望通过 `with_capacity(n)` 跟踪的账户峰值来调整过滤器大小。
* 超过容量的插入操作会优雅地失败，并产生 `TableFullError`——过滤器永不损坏。在实际操作中，表在拒绝插入前可容忍轻微的超填充，但不要依赖于这种余量。
* 序列化大小由容量决定，而不是由您插入的账户数量决定——因此，过大的过滤器会浪费网络字节。选择接近真实峰值的容量。

## 更新跟踪集合

当您的跟踪集发生变化时（需跟踪的新账户、需移除的旧账户）：

1. 对 `CompressedAccountFilterSet` 调用 `insert()` / `remove()`。
2. 检查 `is_dirty()`（或使用 `take_dirty()` 消耗标志）以查看自上次发送以来过滤器是否更改。
3. 如果已更改，用 `insert_into_subscribe_request()` 重新构建请求。在 JavaScript 中，您可以使用 `stream.write(request)` 在同一流上重新发送它；在 Rust 中，使用重新构建的请求重新订阅。

## 常见问题解答

<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 目前尚不支持。请参见上面的[可用性表](#可用性)。
</Accordion>

<Accordion title="我还可以使用显式 pubkey 列表吗？">
  可以。标准的 `account: [...]` 过滤器不变，仍然是小型账户集（多达大约10,000个账户）中的最佳选择。请参阅[账户订阅指南](/docs/zh/laserstream/guides/account-subscription)。
</Accordion>

## 相关

<CardGroup cols={2}>
  <Card title="账户订阅" icon="user" href="/docs/zh/laserstream/guides/account-subscription">
    使用所有者、数据大小和 memcmp 过滤器的标准账户过滤。
  </Card>

  <Card title="客户和 SDKs" icon="code" href="/docs/zh/laserstream/clients">
    提供自动重放和重连的 TypeScript、Rust 和 Go SDKs。
  </Card>
</CardGroup>
