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

> CDR 복호화 요청 및 수행을 위한 메서드.

## Consumer

`Consumer` 하위 클라이언트는 읽기 요청, 검증자로부터의 부분 복호화 수집, 그리고 최종 복호화를 처리합니다. `walletClient`가 필요합니다.

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

<Note>
  SDK는 또한 `readVault`를 `accessCDR`의 별칭으로,
  `readFileVault`를 `downloadFile`의 별칭으로 노출합니다.
</Note>

부분 복호화는 `(uuid, requesterPubKey)`로 키가 지정된 DATA Foundation API REST 엔드포인트
(`/dkg/cdr_partials`)에서 수집됩니다. keeper는 ingress에서 각 검증자의 서명을 검증하므로,
SDK는 로컬에서 서명을 다시 검증하지 않습니다. 심층 방어를 위해, `attestationConfig`를 전달하여
각 검증자의 부분 복호화를 수락하기 전에 SGX enclave를 검증하세요.

### 메서드

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

***

### accessCDR

읽기 요청을 제출하고, 검증자로부터 부분 복호화를 수집하며, 이를 결합하여 원본 데이터를 복구하는 고수준 메서드.

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

매개변수:

* `params.uuid`: `number` - 볼트 UUID
* `params.accessAuxData`: `` `0x${string}` `` - 읽기 조건에 전달되는 보조 데이터
* `params.requesterPubKey` *(선택)*: `` `0x${string}` `` - 압축되지 않은 secp256k1 공개 키 (65 bytes, `0x04` 접두사). 생략하면 SDK가 일회용 키 쌍을 생성합니다.
* `params.recipientPrivKey` *(선택)*: `Uint8Array` - 32바이트 secp256k1 개인 키 (부분 복호화의 ECIES 복호화용). 생략하면 SDK가 일회용 키 쌍을 생성하고 사용 후 영점화합니다.
* `params.globalPubKey` *(선택)*: `Uint8Array` - DKG global public key (`observer.getGlobalPubKey()`에서 가져옴). 생략하면 SDK가 대신 쿼리합니다.
* `params.timeoutMs` *(선택)*: `number` - 부분 복호화 수집을 위한 타임아웃. `120_000`이 좋은 시작점입니다.
* `params.feeOverride` *(선택)*: `bigint` - 명시적 읽기 수수료. `readFee()` 자동 쿼리를 건너뜁니다 (정확한 일치 의미, 다른 금액을 지불하는 방법이 아님).
* `params.onInvalidPartial` *(선택)*: `(event, error) => void` - 검증자의 부분 복호화가 `attestationConfig` 검사를 통과하지 못해 제외될 때 호출됩니다.
* `params.attestationConfig` *(선택)*: `AttestationConfig` - 부분 복호화를 수락하기 전에 각 검증자의 SGX enclave를 검증합니다.

```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>
  가장 짧은 고수준 경로를 원한다면, `requesterPubKey`,
  `recipientPrivKey`, `globalPubKey`를 생략하고 `accessCDR()`이 채우도록 할 수 있습니다.
  임계값은 부분 복호화 버킷의 DKG 라운드에서 자동으로 파생됩니다.
</Note>

<Warning>
  볼트에 한 번도 쓰여진 적이 없는 경우 동기적으로 `EmptyVaultError`를 던집니다.
  이는 수수료가 발생하는 `read()` 트랜잭션이 제출되기 *전에* 사전 검사를 통해 발생하므로,
  수수료가 소비되지 않습니다.
</Warning>

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

***

### downloadFile

CDR을 통해 암호화된 파일 키를 읽고, `StorageProvider`에서 암호화된 blob을 다운로드한 다음,
복호화된 파일 바이트를 반환하는 고수준 메서드.

매개변수:

* `params.uuid`: `number` - 볼트 UUID
* `params.accessAuxData`: `` `0x${string}` `` - 읽기 조건에 전달되는 보조 데이터
* `params.storageProvider`: `StorageProvider` - 암호화된 콘텐츠를 가져오는 데 사용되는 백엔드
* `params.requesterPubKey` *(선택)*: `` `0x${string}` `` - 읽기 흐름을 위한 명시적 요청자 공개 키
* `params.recipientPrivKey` *(선택)*: `Uint8Array` - 읽기 흐름을 위한 명시적 수신자 개인 키
* `params.globalPubKey` *(선택)*: `Uint8Array` - DKG global public key. 생략하면 자동 쿼리됩니다.
* `params.timeoutMs` *(선택)*: `number` - 검증자 부분 복호화 수집을 위한 타임아웃
* `params.feeOverride` *(선택)*: `bigint` - 명시적 읽기 수수료. `readFee()` 자동 쿼리를 건너뜁니다.
* `params.onInvalidPartial` *(선택)*: `(event, error) => void` - 검증자의 부분 복호화가 `attestationConfig` 검사를 통과하지 못해 제외될 때 호출됩니다.
* `params.attestationConfig` *(선택)*: `AttestationConfig` - 부분 복호화를 수락하기 전에 각 검증자의 SGX enclave를 검증합니다.
* `params.skipCidVerification` *(선택)*: `boolean` - 다운로드한 암호화된 파일의 CID 무결성 검증 건너뛰기 (기본값: `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>
  DATA Foundation 라이선스 게이팅 읽기의 경우, `accessAuxData`는 호출자의
  라이선스 토큰 ID를 `abi.encode(uint256[] licenseTokenIds)`로 인코딩해야 합니다.
</Note>

<Note>
  `downloadFile()`은 `accessCDR()`과 동일한 선택적 키 자동 관리 동작을 상속하며,
  `content`와 함께 `cid` 및 `txHash`를 반환합니다.
</Note>

<Note>
  `content`는 원시 복호화된 파일 바이트입니다. 원본 파일이 텍스트 기반인 경우에만
  텍스트로 디코딩하세요.
</Note>

***

### read

온체인에서 읽기 요청을 제출합니다. 호출자는 볼트의 읽기 조건을 충족해야 합니다. 이는 검증자들이 암호화된 부분 복호화를 제출하도록 유도합니다.

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

매개변수:

* `params.uuid`: `number` - 볼트 UUID
* `params.accessAuxData`: `` `0x${string}` `` - 읽기 조건에 전달되는 보조 데이터
* `params.requesterPubKey`: `` `0x${string}` `` - 일회용의 압축되지 않은 secp256k1 공개 키. 부분 복호화는 이 값에 의해 인덱싱됩니다.
* `params.feeOverride` *(선택)*: `bigint` - 명시적 읽기 수수료. `readFee()` 자동 쿼리를 건너뜁니다.

```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

지정된 `(uuid, requesterPubKey)`에 대해 적어도 임계값만큼의 부분 복호화 제출이 표면화될 때까지
DATA Foundation API REST 엔드포인트 (`/dkg/cdr_partials`)를 폴링합니다.

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

매개변수:

* `params.uuid`: `number` - 볼트 UUID
* `params.requesterPubKey`: `` `0x${string}` `` - 일치하는 `read()` 요청에 사용된 압축되지 않은 secp256k1 공개 키
* `params.timeoutMs` *(선택)*: `number` - 밀리초 단위 타임아웃. `120_000`이 좋은 시작점입니다.
* `params.pollIntervalMs` *(선택)*: `number` - 밀리초 단위의 폴링 간격
* `params.onInvalidPartial` *(선택)*: `(event, error) => void` - 검증자의 부분 복호화가 `attestationConfig` 검사를 통과하지 못해 제외될 때 호출됩니다.
* `params.attestationConfig` *(선택)*: `AttestationConfig` - 각 검증자의 SGX enclave를 검증하고 신뢰할 수 없는 검증자의 부분 복호화를 제외합니다

필요한 임계값은 부분 복호화 버킷 자체의 DKG 라운드(`observer.getThresholdAt(round)`)에서 파생되므로,
폴링 중간에 DKG 롤오버가 발생해도 버킷이 잘못된 라운드와 비교되지 않습니다. 볼트 ciphertext는
호출 시작 시 한 번 읽혀지고 나머지 폴링 루프 동안 고정됩니다.

<Warning>
  충분한 부분 복호화가 수집되기 전에 타임아웃에 도달하면 `PartialCollectionTimeoutError`를 던집니다.
  볼트에 한 번도 쓰여진 적이 없는 경우 `EmptyVaultError`를 던집니다.
</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>
  성공적인 `collectPartials` 호출에서 반환되는 모든 이벤트는 동일한
  `round`와 `ciphertext`를 공유합니다: 결과는 볼트의 현재 ciphertext와 일치하는
  버킷으로 필터링됩니다.
</Note>

***

### decryptDataKey

수집된 부분 복호화를 ECIES를 사용하여 복호화한 다음, TDH2를 통해 결합하여 원본 평문을 복구합니다.

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

매개변수:

* `params.ciphertext`: `TDH2Ciphertext` - 암호화된 데이터 (`{ raw, label }`)
* `params.partials`: `PartialDecryptionEvent[]` - 수집된 부분 복호화
* `params.recipientPrivKey`: `Uint8Array` - 일회용 secp256k1 개인 키 (32 bytes)
* `params.globalPubKey`: `Uint8Array` - DKG global public key
* `params.label`: `Uint8Array` - 32바이트 label (`uuidToLabel(uuid)`에서)

TDH2 combine 임계값은 암묵적으로 `partials.length`로 사용되며,
`collectPartials`는 이미 재구성에 필요한 정확한 임계값 수를 반환합니다.
결합하려는 부분 복호화를 정확히 전달하세요.

<Warning>
  ciphertext가 요구하는 것보다 적은 부분 복호화가 전달되면 `InsufficientPartialsError`를 던집니다.
  ciphertext가 비어 있거나 형식이 잘못된 경우 `InvalidCiphertextError`를 던집니다.
</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

활성 DKG 라운드에 대한 검증자 `commPubKey` + attestation 캐시를 미리 준비합니다.
구성 후 첫 번째 `accessCDR()` / `downloadFile()` 호출은 그렇지 않으면 이 가져오기에서 지연될 수 있습니다.
읽기가 임박한 것을 알고 있는 프론트엔드(예: 지갑 연결 직후)는 이를 백그라운드에서 호출할 수 있습니다.

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

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