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

# Consumer

> Methods for requesting and performing CDR decryption.

## Consumer

The `Consumer` sub-client handles read requests, partial decryption collection from validators, and final decryption. Requires a `walletClient`.

```typescript theme={null}
const consumer = client.consumer;
```

<Note>
  The SDK also exposes `readVault` as an alias for `accessCDR`, and
  `readFileVault` as an alias for `downloadFile`.
</Note>

Partial decryptions are collected from the DATA Foundation API REST endpoint
(`/dkg/cdr_partials`), keyed by `(uuid, requesterPubKey)`. The keeper verifies
each validator's signature on ingress, so the SDK does not re-verify signatures
locally. For defense-in-depth, pass an `attestationConfig` to verify each
validator's SGX enclave before accepting their partials.

### Methods

* accessCDR
* downloadFile
* read
* collectPartials
* decryptDataKey
* prefetchRegistry

***

### accessCDR

High-level method that submits a read request, collects partial decryptions from validators, and combines them to recover the original data.

| Method      | Type                                                      |
| ----------- | --------------------------------------------------------- |
| `accessCDR` | `(params: AccessCDRParams) => Promise<AccessCDRResponse>` |

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.accessAuxData`: `` `0x${string}` `` - Auxiliary data passed to the read condition
* `params.requesterPubKey` *(optional)*: `` `0x${string}` `` - Uncompressed secp256k1 public key (65 bytes, `0x04` prefix). If omitted, the SDK generates an ephemeral keypair.
* `params.recipientPrivKey` *(optional)*: `Uint8Array` - 32-byte secp256k1 private key (for ECIES decryption of partials). If omitted, the SDK generates an ephemeral keypair and zeroes it after use.
* `params.globalPubKey` *(optional)*: `Uint8Array` - DKG global public key (from `observer.getGlobalPubKey()`). If omitted, the SDK queries it for you.
* `params.timeoutMs` *(optional)*: `number` - Timeout for collecting partials. `120_000` is a good starting point.
* `params.feeOverride` *(optional)*: `bigint` - Explicit read fee. Skips the `readFee()` auto-query (strict-equality semantics, not a way to pay a different amount).
* `params.onInvalidPartial` *(optional)*: `(event, error) => void` - Called when a validator's partial is excluded because its attestation failed the `attestationConfig` checks.
* `params.attestationConfig` *(optional)*: `AttestationConfig` - Verifies each validator's SGX enclave before accepting their partials.

```typescript Example theme={null}
const { dataKey, txHash } = await client.consumer.accessCDR({
  uuid: 42,
  accessAuxData: "0x",
  timeoutMs: 120_000,
});

const secret = new TextDecoder().decode(dataKey);
console.log(`Read tx: ${txHash}`);
console.log(`Decrypted: ${secret}`);
```

<Note>
  If you want the shortest high-level path, you can omit `requesterPubKey`,
  `recipientPrivKey`, and `globalPubKey` and let `accessCDR()` fill them in.
  The threshold is derived automatically from the partial-decryption bucket's
  DKG round.
</Note>

<Warning>
  Throws `EmptyVaultError` synchronously if the vault has never been written to.
  This is raised by a preflight chain read *before* the fee-bearing `read()`
  transaction is submitted, so no fee is spent.
</Warning>

```typescript AccessCDRResponse theme={null}
interface AccessCDRResponse {
  dataKey: Uint8Array; // the recovered plaintext
  txHash: `0x${string}`; // read request transaction hash
}
```

***

### downloadFile

High-level method that reads the encrypted file key through CDR, downloads the
encrypted blob from a `StorageProvider`, and returns the decrypted file bytes.

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.accessAuxData`: `` `0x${string}` `` - Auxiliary data passed to the read condition
* `params.storageProvider`: `StorageProvider` - Backend used to fetch the encrypted content
* `params.requesterPubKey` *(optional)*: `` `0x${string}` `` - Explicit requester public key for the read flow
* `params.recipientPrivKey` *(optional)*: `Uint8Array` - Explicit recipient private key for the read flow
* `params.globalPubKey` *(optional)*: `Uint8Array` - DKG global public key. Auto-queried if omitted.
* `params.timeoutMs` *(optional)*: `number` - Timeout for validator partial collection
* `params.feeOverride` *(optional)*: `bigint` - Explicit read fee. Skips the `readFee()` auto-query.
* `params.onInvalidPartial` *(optional)*: `(event, error) => void` - Called when a validator's partial is excluded because its attestation failed the `attestationConfig` checks.
* `params.attestationConfig` *(optional)*: `AttestationConfig` - Verifies each validator's SGX enclave before accepting their partials.
* `params.skipCidVerification` *(optional)*: `boolean` - Skip CID integrity verification of the downloaded encrypted file (default: `false`)

```typescript Example theme={null}
import { writeFile } from "node:fs/promises";

const { content } = await client.consumer.downloadFile({
  uuid: 42,
  accessAuxData: "0x",
  storageProvider,
  timeoutMs: 120_000,
});

await writeFile("./example.decrypted.pdf", Buffer.from(content));
console.log("Saved ./example.decrypted.pdf");
```

<Note>
  For DATA Foundation license-gated reads, `accessAuxData` should encode the caller's
  license token IDs as `abi.encode(uint256[] licenseTokenIds)`.
</Note>

<Note>
  `downloadFile()` inherits the same optional key auto-management behavior as
  `accessCDR()`, and returns `cid` and `txHash` alongside `content`.
</Note>

<Note>
  `content` is the raw decrypted file bytes. Decode it as text only if the
  original file was text-based.
</Note>

***

### read

Submits a read request on-chain. The caller must satisfy the vault's read condition. This prompts validators to submit encrypted partial decryptions.

| Method | Type                                            |
| ------ | ----------------------------------------------- |
| `read` | `(params: ReadParams) => Promise<ReadResponse>` |

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.accessAuxData`: `` `0x${string}` `` - Auxiliary data passed to the read condition
* `params.requesterPubKey`: `` `0x${string}` `` - Your ephemeral uncompressed secp256k1 public key. Partials are indexed by this value.
* `params.feeOverride` *(optional)*: `bigint` - Explicit read fee. Skips the `readFee()` auto-query.

```typescript Example theme={null}
const { txHash } = await client.consumer.read({
  uuid: 42,
  accessAuxData: "0x",
  requesterPubKey,
});
```

```typescript ReadResponse theme={null}
interface ReadResponse {
  txHash: `0x${string}`;
}
```

***

### collectPartials

Polls the DATA Foundation API REST endpoint (`/dkg/cdr_partials`) until at least a
threshold's worth of partial-decryption submissions have been surfaced for the
given `(uuid, requesterPubKey)`.

| Method            | Type                                                                   |
| ----------------- | ---------------------------------------------------------------------- |
| `collectPartials` | `(params: CollectPartialsParams) => Promise<PartialDecryptionEvent[]>` |

Parameters:

* `params.uuid`: `number` - The vault UUID
* `params.requesterPubKey`: `` `0x${string}` `` - The uncompressed secp256k1 public key used in the matching `read()` request
* `params.timeoutMs` *(optional)*: `number` - Timeout in milliseconds. `120_000` is a good starting point.
* `params.pollIntervalMs` *(optional)*: `number` - Polling interval in milliseconds
* `params.onInvalidPartial` *(optional)*: `(event, error) => void` - Called when a validator's partial is excluded because its attestation failed the `attestationConfig` checks
* `params.attestationConfig` *(optional)*: `AttestationConfig` - Verifies each validator's SGX enclave and excludes partials from untrusted validators

The required threshold is derived from the partial-decryption bucket's own DKG
round (`observer.getThresholdAt(round)`), so a DKG rollover mid-poll does not
measure the bucket against the wrong round. The vault ciphertext is read once at
the start of the call and pinned for the rest of the poll loop.

<Warning>
  Throws `PartialCollectionTimeoutError` if the timeout is reached before enough
  partials are collected. Throws `EmptyVaultError` if the vault has never been
  written to.
</Warning>

```typescript Example theme={null}
const partials = await client.consumer.collectPartials({
  uuid: 42,
  requesterPubKey,
  timeoutMs: 120_000,
});

console.log(`Collected ${partials.length} partials`);
```

```typescript PartialDecryptionEvent theme={null}
interface PartialDecryptionEvent {
  validator: `0x${string}`;
  round: number;
  pid: number;                      // 1-based participant index
  encryptedPartial: `0x${string}`;  // AES-GCM encrypted
  ephemeralPubKey: `0x${string}`;   // 65 bytes, uncompressed secp256k1
  pubShare: `0x${string}`;          // 34 bytes, Ed25519 with curve-code prefix
  uuid: number;
  ciphertext: `0x${string}`;        // TDH2 ciphertext this partial decrypts
}
```

<Note>
  Every event returned from a successful `collectPartials` call shares the same
  `round` and `ciphertext`: the result is filtered to the bucket matching the
  vault's current ciphertext.
</Note>

***

### decryptDataKey

Decrypts the collected partial decryptions using ECIES, then combines them via TDH2 to recover the original plaintext.

| Method           | Type                                             |
| ---------------- | ------------------------------------------------ |
| `decryptDataKey` | `(params: DecryptParams) => Promise<Uint8Array>` |

Parameters:

* `params.ciphertext`: `TDH2Ciphertext` - The encrypted data (`{ raw, label }`)
* `params.partials`: `PartialDecryptionEvent[]` - Collected partial decryptions
* `params.recipientPrivKey`: `Uint8Array` - Your ephemeral secp256k1 private key (32 bytes)
* `params.globalPubKey`: `Uint8Array` - DKG global public key
* `params.label`: `Uint8Array` - 32-byte label (from `uuidToLabel(uuid)`)

The TDH2 combine threshold is taken implicitly as `partials.length`,
`collectPartials` already returns exactly the threshold count needed for
reconstruction. Pass exactly the partials you want combined.

<Warning>
  Throws `InsufficientPartialsError` if fewer partials are passed than the
  ciphertext requires. Throws `InvalidCiphertextError` if the ciphertext is
  empty or malformed.
</Warning>

```typescript Example theme={null}
import { uuidToLabel } from "@piplabs/cdr-sdk";

const label = uuidToLabel(uuid);
const dataKey = await client.consumer.decryptDataKey({
  ciphertext: { raw: ciphertextBytes, label },
  partials,
  recipientPrivKey,
  globalPubKey,
  label,
});

const secret = new TextDecoder().decode(dataKey);
```

***

### prefetchRegistry

Warms the validator `commPubKey` + attestation cache for the active DKG round.
The first `accessCDR()` / `downloadFile()` call after construction would
otherwise stall on this fetch. Frontends that know a read is imminent (for
example, right after wallet connection) can call this in the background.

| Method             | Type                  |
| ------------------ | --------------------- |
| `prefetchRegistry` | `() => Promise<void>` |

```typescript Example theme={null}
// Best-effort warm-up, safe to call repeatedly
client.consumer.prefetchRegistry().catch(() => {});
```
