> ## 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.

# Lọc tài khoản quy mô lớn bằng bộ lọc nén

> Đăng ký hàng trăm nghìn tài khoản Solana trong một luồng LaserStream gRPC bằng bộ lọc cuckoo nén — yêu cầu đăng ký nhỏ hơn khoảng 8 lần.

## Tổng quan

LaserStream hỗ trợ **lọc tài khoản nén bằng bộ lọc cuckoo**. Thay vì gửi danh sách pubkey tường minh trong yêu cầu đăng ký (32 byte cho mỗi tài khoản), bạn gửi một bộ lọc xác suất nhỏ gọn chỉ chiếm khoảng **3–4 byte cho mỗi tài khoản** khi truyền.

Điều này giúp việc đăng ký **hàng trăm nghìn tài khoản trong một luồng duy nhất** trở nên khả thi — không cần phân mảnh trên nhiều kết nối, không có yêu cầu đăng ký quá lớn.

Ví dụ: một bộ lọc theo dõi 500.000 tài khoản được tuần tự hóa thành khoảng 2,1 MB, so với 16 MB khi dùng danh sách pubkey thô — nhỏ hơn khoảng **7,6 lần**. Mức tiết kiệm chính xác phụ thuộc vào độ đầy của bộ lọc: càng gần đạt dung lượng tối đa thì số byte trên mỗi tài khoản càng ít.

### Khả dụng

| Máy khách                                                      | Phiên bản tối thiểu | Hỗ trợ cuckoo |
| -------------------------------------------------------------- | ------------------- | ------------- |
| LaserStream SDK — Rust (`helius-laserstream`)                  | 0.2.0               | ✅             |
| LaserStream SDK — JavaScript/TypeScript (`helius-laserstream`) | 0.4.0               | ✅             |
| LaserStream SDK — Go                                           | —                   | ❌ Chưa hỗ trợ |
| Yellowstone gRPC — Rust (`yellowstone-grpc-client`)            | 13.1.0              | ✅             |

## Khi nào nên dùng bộ lọc cuckoo

| Tài khoản được theo dõi | Phương pháp đề xuất                                                    |
| ----------------------- | ---------------------------------------------------------------------- |
| Tối đa khoảng 10.000    | Danh sách pubkey tường minh (`account: [...]`) — đơn giản và chính xác |
| Khoảng 10.000 trở lên   | Bộ lọc cuckoo qua `CompressedAccountFilterSet`                         |

Các trường hợp sử dụng điển hình: giám sát mọi người nắm giữ một token, theo dõi tất cả vị thế trong một giao thức cho vay hoặc theo dõi các tập hợp ví lớn cho hệ thống giao dịch hay phân tích.

## Cách hoạt động

1. **Tạo bộ lọc ở phía máy khách.** Chèn từng pubkey được theo dõi vào một `CompressedAccountFilterSet`. Seed băm được tạo ngẫu nhiên cho từng bộ lọc và được tuần tự hóa cùng bộ lọc, vì vậy máy chủ băm các tài khoản đến bằng cùng seed mà máy khách của bạn đã dùng.
2. **Đính kèm bộ lọc vào yêu cầu đăng ký.** `insert_into_subscribe_request()` đặt bộ lọc đã tuần tự hóa vào luồng tài khoản của một `SubscribeRequest` tiêu chuẩn.
3. **Máy chủ đối sánh theo xác suất.** Vì bộ lọc có tính xác suất, máy chủ có thể gửi các bản cập nhật cho những tài khoản bạn không theo dõi — tỷ lệ dương tính giả được giới hạn ở mức **dưới 1% khi đầy tải**. **Không bao giờ có âm tính giả**: mọi bản cập nhật cho tài khoản được theo dõi đều được gửi.
4. **Kiểm tra lại cục bộ từng bản cập nhật — bước này là bắt buộc.** Gọi `set.contains(pubkey)` trên mọi tài khoản đến trước khi xử lý. Phép kiểm tra này là chính xác (được hỗ trợ bởi một tập hợp băm nội bộ), vì vậy sau khi lọc cục bộ, bạn sẽ không còn dương tính giả.

## Bắt đầu nhanh (Rust)

Thêm SDK vào dự án:

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

Tạo bộ lọc, đính kèm vào một gói đăng ký và loại bỏ dương tính giả cục bộ:

```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 đi kèm một phiên bản hoàn chỉnh có thể chạy được: [`rust/examples/cuckoo_account_filter.rs`](https://github.com/helius-labs/laserstream-sdk/blob/main/rust/examples/cuckoo_account_filter.rs).

## Bắt đầu nhanh (JavaScript/TypeScript)

Cài đặt SDK (hỗ trợ cuckoo yêu cầu `helius-laserstream` 0.4.0+):

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

Tạo bộ lọc, đính kèm bộ lọc và kiểm tra lại cục bộ từng bản cập nhật:

```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 đi kèm một phiên bản hoàn chỉnh có thể chạy được: [`javascript/examples/cuckoo-account-sub.ts`](https://github.com/helius-labs/laserstream-sdk/blob/main/javascript/examples/cuckoo-account-sub.ts).

## Tài liệu tham khảo API

`CompressedAccountFilterSet` đóng gói bộ lọc cuckoo thô cùng với một tập hợp băm chính xác, nhờ đó các thao tác thay đổi và kiểm tra tư cách thành viên luôn an toàn và chính xác:

| Phương thức                                            | Hành vi                                                                                                                              |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `with_capacity(n)`                                     | Tạo bộ lọc có kích thước phù hợp với `n` tài khoản được theo dõi. Đặt kích thước theo quy mô **cao nhất** của tập hợp được theo dõi. |
| `insert(pubkey)`                                       | Trả về `Ok(true)` nếu là mục mới, `Ok(false)` nếu bị trùng, `Err(TableFullError)` nếu bộ lọc đã đạt dung lượng tối đa.               |
| `remove(pubkey)`                                       | Xóa tài khoản. An toàn và chính xác.                                                                                                 |
| `contains(pubkey)`                                     | Kiểm tra tư cách thành viên chính xác — dùng phương thức này để loại bỏ dương tính giả phía máy chủ.                                 |
| `insert_into_subscribe_request(&mut request, "label")` | Đính kèm bộ lọc vào luồng tài khoản của một `SubscribeRequest`.                                                                      |
| `to_account_filter()` / `to_proto()`                   | Các phép chuyển đổi cấp thấp hơn để tự lắp ráp yêu cầu.                                                                              |
| `is_dirty()` / `take_dirty()`                          | Cho biết tập hợp có thay đổi kể từ lần gần nhất được đặt vào một yêu cầu hay không — hữu ích cho các chu kỳ đăng ký lại.             |

Tên phương thức ở trên tuân theo quy ước Rust. SDK JavaScript/TypeScript cung cấp cùng một giao diện ở dạng camelCase — `new CompressedAccountFilterSet(capacity)` thay cho `with_capacity`, `insertIntoSubscribeRequest`, `isDirty`, `takeDirty`, `toProto`, v.v. Trong JavaScript, `insert` trả về một giá trị boolean (`true` nếu mới được thêm) và phát sinh `TableFullError` khi bộ lọc đã bão hòa. Có thể truyền pubkey dưới dạng chuỗi base58, 32 byte thô hoặc bất kỳ đối tượng nào có phương thức `toBytes()`.

Luôn dùng `CompressedAccountFilterSet` thay vì `CuckooFilter` thô mà nó đóng gói. `remove()` của bộ lọc thô có thể âm thầm xóa nhầm mục — một cạm bẫy đã được ghi nhận của bộ lọc cuckoo. Lớp bọc ghép bộ lọc với một tập hợp băm chính xác, nên các thao tác chèn, xóa và kiểm tra chứa luôn chính xác.

## Định cỡ dung lượng

* Đặt kích thước bộ lọc theo số lượng tài khoản **cao nhất** mà bạn dự kiến theo dõi qua `with_capacity(n)`.
* Việc chèn vượt quá dung lượng sẽ thất bại an toàn với một `TableFullError` — bộ lọc không bao giờ bị hỏng. Trên thực tế, bảng chịu được mức vượt dung lượng nhẹ trước khi từ chối các lần chèn, nhưng đừng phụ thuộc vào phần dung lượng dự phòng đó.
* Kích thước được tuần tự hóa do dung lượng quyết định, không phải số lượng tài khoản bạn đã chèn — vì vậy bộ lọc quá lớn sẽ lãng phí byte khi truyền. Hãy chọn dung lượng gần với mức cao nhất thực tế.

## Cập nhật tập hợp được theo dõi

Khi tập hợp được theo dõi thay đổi (có tài khoản mới cần theo dõi, tài khoản cũ cần loại bỏ):

1. Gọi `insert()` / `remove()` trên `CompressedAccountFilterSet`.
2. Kiểm tra `is_dirty()` (hoặc đọc và xóa cờ bằng `take_dirty()`) để xem bộ lọc có thay đổi kể từ lần gửi gần nhất hay không.
3. Nếu đã thay đổi, hãy tạo lại yêu cầu bằng `insert_into_subscribe_request()`. Trong JavaScript, bạn có thể gửi lại yêu cầu trên cùng luồng bằng `stream.write(request)`; trong Rust, hãy đăng ký lại bằng yêu cầu đã tạo lại.

## Câu hỏi thường gặp

<Accordion title="Can I miss updates for accounts in my filter?">
  Không. Bộ lọc cuckoo tạo ra dương tính giả (các bản cập nhật bổ sung cho tài khoản không được theo dõi) nhưng **không bao giờ tạo ra âm tính giả**. Mọi bản cập nhật cho tài khoản được theo dõi đều được gửi.
</Accordion>

<Accordion title="How many extra (false-positive) updates will I receive?">
  Dưới 1% khi đầy tải và thường thấp hơn khi bộ lọc chưa đạt dung lượng tối đa. Một lần gọi `contains()` cục bộ cho mỗi bản cập nhật sẽ lọc chúng ra một cách chính xác.
</Accordion>

<Accordion title="Which clients support cuckoo filters?">
  Rust SDK (`helius-laserstream` 0.2.0+), JavaScript/TypeScript SDK (`helius-laserstream` 0.4.0+) và máy khách Yellowstone Rust (`yellowstone-grpc-client` 13.1.0+) hỗ trợ bộ lọc cuckoo. Go SDK chưa hỗ trợ. Xem [bảng khả dụng](#khả-dụng) ở trên.
</Accordion>

<Accordion title="Can I still use explicit pubkey lists?">
  Có. Các bộ lọc `account: [...]` tiêu chuẩn hoạt động không thay đổi và vẫn là lựa chọn phù hợp cho các tập hợp tài khoản nhỏ (tối đa khoảng 10.000 tài khoản). Xem [hướng dẫn đăng ký tài khoản](/docs/vi/laserstream/guides/account-subscription).
</Accordion>

<Accordion title="Do compressed filters work with matchMints?">
  Có. Khi một bộ lọc nén được đính kèm vào gói đăng ký giao dịch và `matchMints: true` được thiết lập, máy chủ cũng kiểm tra các mint số dư token trước/sau của giao dịch đối với bộ lọc, cùng với các khóa tài khoản của giao dịch. Xem [Lọc mint token](/docs/vi/laserstream/mint-filtering).
</Accordion>

## Liên quan

<CardGroup cols={2}>
  <Card title="Account Subscriptions" icon="user" href="/docs/vi/laserstream/guides/account-subscription">
    Lọc tài khoản tiêu chuẩn bằng các bộ lọc chủ sở hữu, kích thước dữ liệu và memcmp.
  </Card>

  <Card title="Clients & SDKs" icon="code" href="/docs/vi/laserstream/clients">
    SDK TypeScript, Rust và Go với khả năng tự động phát lại và kết nối lại.
  </Card>

  <Card title="Token Mint Filtering" icon="coins" href="/docs/vi/laserstream/mint-filtering">
    Đối sánh giao dịch theo mint token bằng `matchMints`, bao gồm cả bên trong bộ lọc nén.
  </Card>
</CardGroup>
