@ckbfs/api 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +569 -0
- package/code.png +0 -0
- package/demo-output.txt +1 -0
- package/direct_direct_content_example.txt +1 -0
- package/dist/index.d.ts +17 -15
- package/dist/index.js +47 -22
- package/dist/utils/constants.d.ts +7 -3
- package/dist/utils/constants.js +41 -34
- package/dist/utils/file.d.ts +179 -0
- package/dist/utils/file.js +599 -31
- package/dist/utils/molecule.d.ts +3 -2
- package/dist/utils/molecule.js +22 -24
- package/dist/utils/transaction.d.ts +23 -4
- package/dist/utils/transaction.js +318 -66
- package/examples/append.ts +2 -39
- package/examples/example.txt +1 -0
- package/examples/identifier-test.ts +178 -0
- package/examples/index.ts +36 -24
- package/examples/publish.ts +5 -5
- package/examples/retrieve.ts +580 -0
- package/examples/witness-decode-demo.ts +190 -0
- package/identifier_direct_content_example.txt +1 -0
- package/package-lock.json +4978 -0
- package/package.json +3 -2
- package/src/index.ts +181 -99
- package/src/utils/constants.ts +77 -43
- package/src/utils/file.ts +864 -59
- package/src/utils/molecule.ts +41 -36
- package/src/utils/transaction.ts +585 -190
- package/traditional_direct_content_example.txt +1 -0
- package/typeid_direct_content_example.txt +1 -0
- package/example.txt +0 -1
- package/src/utils/createPublishTransaction +0 -24
package/README.md
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# CKBFS API
|
|
2
|
+
|
|
3
|
+
A TypeScript SDK for the CKBFS (CKB File System) protocol on the Nervos CKB blockchain. This library provides a high-level interface for publishing, appending, and retrieving files on the decentralized CKB network.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
CKBFS is a file system protocol built on top of the CKB blockchain that enables decentralized file storage with content integrity verification. Files are stored in transaction witnesses with Adler32 checksums for data integrity and support for file versioning through backlinks.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **File Publishing**: Store files of any type on the CKB blockchain
|
|
12
|
+
- **File Appending**: Add content to existing files with automatic checksum updates
|
|
13
|
+
- **Content Integrity**: Adler32 checksum verification for all file operations
|
|
14
|
+
- **Protocol Versions**: Support for both V1 and V2 CKBFS protocol versions
|
|
15
|
+
- **Network Support**: Compatible with CKB mainnet and testnet
|
|
16
|
+
- **Chunked Storage**: Automatic file chunking for large files
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @ckbfs/api
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { CKBFS, NetworkType, ProtocolVersion } from '@ckbfs/api';
|
|
28
|
+
|
|
29
|
+
// Initialize with private key
|
|
30
|
+
const ckbfs = new CKBFS(
|
|
31
|
+
'your-private-key-here',
|
|
32
|
+
NetworkType.Testnet,
|
|
33
|
+
{
|
|
34
|
+
version: ProtocolVersion.V2,
|
|
35
|
+
chunkSize: 30 * 1024, // 30KB chunks
|
|
36
|
+
useTypeID: false
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Publish a file
|
|
41
|
+
const txHash = await ckbfs.publishFile('./example.txt', {
|
|
42
|
+
contentType: 'text/plain',
|
|
43
|
+
filename: 'example.txt'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
console.log(`File published: ${txHash}`);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### CKBFS Class
|
|
52
|
+
|
|
53
|
+
#### Constructor
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
new CKBFS(signerOrPrivateKey, networkOrOptions?, options?)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
- `signerOrPrivateKey`: `Signer | string` - CKB signer instance or private key
|
|
61
|
+
- `networkOrOptions`: `NetworkType | CKBFSOptions` - Network type or configuration options
|
|
62
|
+
- `options`: `CKBFSOptions` - Additional configuration when using private key
|
|
63
|
+
|
|
64
|
+
**CKBFSOptions:**
|
|
65
|
+
```typescript
|
|
66
|
+
interface CKBFSOptions {
|
|
67
|
+
chunkSize?: number; // Default: 30KB
|
|
68
|
+
version?: string; // Default: V2
|
|
69
|
+
useTypeID?: boolean; // Default: false
|
|
70
|
+
network?: NetworkType; // Default: Testnet
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Methods
|
|
75
|
+
|
|
76
|
+
##### publishFile(filePath, options?)
|
|
77
|
+
|
|
78
|
+
Publishes a file to CKBFS from the file system.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
async publishFile(filePath: string, options?: FileOptions): Promise<string>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
##### publishContent(content, options)
|
|
85
|
+
|
|
86
|
+
Publishes content directly without reading from file system.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
async publishContent(
|
|
90
|
+
content: string | Uint8Array,
|
|
91
|
+
options: PublishContentOptions
|
|
92
|
+
): Promise<string>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
##### appendFile(filePath, ckbfsCell, options?)
|
|
96
|
+
|
|
97
|
+
Appends content from a file to an existing CKBFS file.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
async appendFile(
|
|
101
|
+
filePath: string,
|
|
102
|
+
ckbfsCell: AppendOptions['ckbfsCell'],
|
|
103
|
+
options?: Omit<FileOptions, 'contentType' | 'filename'>
|
|
104
|
+
): Promise<string>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
##### appendContent(content, ckbfsCell, options?)
|
|
108
|
+
|
|
109
|
+
Appends content directly to an existing CKBFS file.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
async appendContent(
|
|
113
|
+
content: string | Uint8Array,
|
|
114
|
+
ckbfsCell: AppendOptions['ckbfsCell'],
|
|
115
|
+
options?: AppendContentOptions
|
|
116
|
+
): Promise<string>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
##### Transaction Creation Methods
|
|
120
|
+
|
|
121
|
+
Create unsigned transactions for custom signing workflows:
|
|
122
|
+
|
|
123
|
+
- `createPublishTransaction(filePath, options?)`
|
|
124
|
+
- `createPublishContentTransaction(content, options)`
|
|
125
|
+
- `createAppendTransaction(filePath, ckbfsCell, options?)`
|
|
126
|
+
- `createAppendContentTransaction(content, ckbfsCell, options?)`
|
|
127
|
+
|
|
128
|
+
### Types
|
|
129
|
+
|
|
130
|
+
#### FileOptions
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
interface FileOptions {
|
|
134
|
+
contentType?: string;
|
|
135
|
+
filename?: string;
|
|
136
|
+
capacity?: bigint;
|
|
137
|
+
feeRate?: number;
|
|
138
|
+
network?: NetworkType;
|
|
139
|
+
version?: string;
|
|
140
|
+
useTypeID?: boolean;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### PublishContentOptions
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
type PublishContentOptions = Omit<FileOptions, 'capacity' | 'contentType' | 'filename'> &
|
|
148
|
+
Required<Pick<FileOptions, 'contentType' | 'filename'>> &
|
|
149
|
+
{ capacity?: bigint };
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### NetworkType
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
enum NetworkType {
|
|
156
|
+
Mainnet = 'mainnet',
|
|
157
|
+
Testnet = 'testnet'
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Protocol Versions
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const ProtocolVersion = {
|
|
165
|
+
V1: '20240906.ce6724722cf6', // Original version
|
|
166
|
+
V2: '20241025.db973a8e8032' // Enhanced version with multi-witness support
|
|
167
|
+
} as const;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
### Publishing a File
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { CKBFS, NetworkType, ProtocolVersion } from '@ckbfs/api';
|
|
176
|
+
|
|
177
|
+
const ckbfs = new CKBFS('your-private-key', NetworkType.Testnet);
|
|
178
|
+
|
|
179
|
+
// Publish with automatic content type detection
|
|
180
|
+
const txHash = await ckbfs.publishFile('./document.pdf');
|
|
181
|
+
|
|
182
|
+
// Publish with custom options
|
|
183
|
+
const txHash2 = await ckbfs.publishFile('./data.json', {
|
|
184
|
+
contentType: 'application/json',
|
|
185
|
+
filename: 'my-data.json',
|
|
186
|
+
capacity: 300n * 100000000n // 300 CKB
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Publishing Content Directly
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const content = "Hello, CKBFS!";
|
|
194
|
+
const txHash = await ckbfs.publishContent(content, {
|
|
195
|
+
contentType: 'text/plain',
|
|
196
|
+
filename: 'greeting.txt'
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Appending to a File
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// First, get the CKBFS cell information from a previous transaction
|
|
204
|
+
const ckbfsCell = await getCellInfoFromTransaction(previousTxHash);
|
|
205
|
+
|
|
206
|
+
// Append content from a file
|
|
207
|
+
const appendTxHash = await ckbfs.appendFile('./additional-content.txt', ckbfsCell);
|
|
208
|
+
|
|
209
|
+
// Or append content directly
|
|
210
|
+
const appendTxHash2 = await ckbfs.appendContent(
|
|
211
|
+
"Additional content to append",
|
|
212
|
+
ckbfsCell
|
|
213
|
+
);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Retrieving Files
|
|
217
|
+
|
|
218
|
+
#### Traditional Method (with blockchain queries)
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { getFileContentFromChain, saveFileFromChain } from '@ckbfs/api';
|
|
222
|
+
import { ClientPublicTestnet } from '@ckb-ccc/core';
|
|
223
|
+
|
|
224
|
+
const client = new ClientPublicTestnet();
|
|
225
|
+
|
|
226
|
+
// Get file content from blockchain (follows backlinks automatically)
|
|
227
|
+
const content = await getFileContentFromChain(
|
|
228
|
+
client,
|
|
229
|
+
{ txHash: 'transaction-hash', index: 0 },
|
|
230
|
+
ckbfsData
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// Save to disk
|
|
234
|
+
const savedPath = saveFileFromChain(content, ckbfsData, './downloaded-file.txt');
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Generic Identifier Retrieval (flexible interface)
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import {
|
|
241
|
+
getFileContentFromChainByIdentifier,
|
|
242
|
+
saveFileFromChainByIdentifier,
|
|
243
|
+
decodeFileFromChainByIdentifier,
|
|
244
|
+
parseIdentifier,
|
|
245
|
+
IdentifierType
|
|
246
|
+
} from '@ckbfs/api';
|
|
247
|
+
import { ClientPublicTestnet } from '@ckb-ccc/core';
|
|
248
|
+
|
|
249
|
+
const client = new ClientPublicTestnet();
|
|
250
|
+
|
|
251
|
+
// Supported identifier formats:
|
|
252
|
+
const typeIdHex = '0xbce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a';
|
|
253
|
+
const ckbfsTypeIdUri = 'ckbfs://bce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a';
|
|
254
|
+
const ckbfsOutPointUri = 'ckbfs://431c9d668c1815d26eb4f7ac6256eb350ab351474daea8d588400146ab228780i0';
|
|
255
|
+
|
|
256
|
+
// Parse identifier to see what type it is
|
|
257
|
+
const parsed = parseIdentifier(ckbfsTypeIdUri);
|
|
258
|
+
console.log(`Type: ${parsed.type}`); // "typeId" or "outPoint"
|
|
259
|
+
|
|
260
|
+
// Get file content using any identifier format (follows backlinks automatically)
|
|
261
|
+
const fileData = await getFileContentFromChainByIdentifier(client, ckbfsTypeIdUri, {
|
|
262
|
+
network: 'testnet',
|
|
263
|
+
version: ProtocolVersion.V2,
|
|
264
|
+
useTypeID: false
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
if (fileData) {
|
|
268
|
+
console.log(`File: ${fileData.filename}`);
|
|
269
|
+
console.log(`Size: ${fileData.size} bytes`);
|
|
270
|
+
console.log(`Content type: ${fileData.contentType}`);
|
|
271
|
+
console.log(`Parsed as: ${fileData.parsedId.type}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Save file using outPoint format
|
|
275
|
+
const savedPath = await saveFileFromChainByIdentifier(
|
|
276
|
+
client,
|
|
277
|
+
ckbfsOutPointUri,
|
|
278
|
+
'./downloaded-file.txt'
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Decode using direct witness method with TypeID hex
|
|
282
|
+
const decodedData = await decodeFileFromChainByIdentifier(client, typeIdHex);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
#### TypeID-based Retrieval (legacy interface)
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import {
|
|
289
|
+
getFileContentFromChainByTypeId,
|
|
290
|
+
saveFileFromChainByTypeId,
|
|
291
|
+
decodeFileFromChainByTypeId
|
|
292
|
+
} from '@ckbfs/api';
|
|
293
|
+
|
|
294
|
+
// Legacy functions still work with TypeID strings
|
|
295
|
+
const fileData = await getFileContentFromChainByTypeId(client, typeId);
|
|
296
|
+
const savedPath = await saveFileFromChainByTypeId(client, typeId, './file.txt');
|
|
297
|
+
const decodedData = await decodeFileFromChainByTypeId(client, typeId);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Direct Witness Decoding (new method)
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import {
|
|
304
|
+
decodeWitnessContent,
|
|
305
|
+
decodeFileFromWitnessData,
|
|
306
|
+
saveFileFromWitnessData
|
|
307
|
+
} from '@ckbfs/api';
|
|
308
|
+
|
|
309
|
+
// Decode individual witness
|
|
310
|
+
const decoded = decodeWitnessContent(witnessHex);
|
|
311
|
+
if (decoded && decoded.isValid) {
|
|
312
|
+
console.log(`Content: ${decoded.content.length} bytes`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Decode complete file from witness data
|
|
316
|
+
const file = decodeFileFromWitnessData({
|
|
317
|
+
witnesses: tx.witnesses,
|
|
318
|
+
indexes: [1, 2, 3], // witness indexes containing content
|
|
319
|
+
filename: 'example.txt',
|
|
320
|
+
contentType: 'text/plain'
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Save directly from witness data
|
|
324
|
+
const savedPath = saveFileFromWitnessData({
|
|
325
|
+
witnesses: tx.witnesses,
|
|
326
|
+
indexes: [1, 2, 3],
|
|
327
|
+
filename: 'example.txt',
|
|
328
|
+
contentType: 'text/plain'
|
|
329
|
+
}, './decoded-file.txt');
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Utility Functions
|
|
333
|
+
|
|
334
|
+
The SDK exports various utility functions for advanced use cases:
|
|
335
|
+
|
|
336
|
+
### Checksum Operations
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { calculateChecksum, verifyChecksum, updateChecksum } from '@ckbfs/api';
|
|
340
|
+
|
|
341
|
+
const checksum = await calculateChecksum(fileData);
|
|
342
|
+
const isValid = await verifyChecksum(fileData, expectedChecksum);
|
|
343
|
+
const newChecksum = await updateChecksum(oldChecksum, appendedData);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### File Operations
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import {
|
|
350
|
+
readFileAsUint8Array,
|
|
351
|
+
getContentType,
|
|
352
|
+
splitFileIntoChunks,
|
|
353
|
+
getFileContentFromChainByIdentifier,
|
|
354
|
+
saveFileFromChainByIdentifier,
|
|
355
|
+
decodeFileFromChainByIdentifier,
|
|
356
|
+
parseIdentifier,
|
|
357
|
+
IdentifierType
|
|
358
|
+
} from '@ckbfs/api';
|
|
359
|
+
|
|
360
|
+
const fileData = readFileAsUint8Array('./file.txt');
|
|
361
|
+
const mimeType = getContentType('./file.txt');
|
|
362
|
+
const chunks = splitFileIntoChunks('./large-file.bin', 30 * 1024);
|
|
363
|
+
|
|
364
|
+
// Generic identifier-based file operations
|
|
365
|
+
const client = new ClientPublicTestnet();
|
|
366
|
+
const identifier = 'ckbfs://bce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a';
|
|
367
|
+
const parsed = parseIdentifier(identifier);
|
|
368
|
+
const fileContent = await getFileContentFromChainByIdentifier(client, identifier);
|
|
369
|
+
const savedPath = await saveFileFromChainByIdentifier(client, identifier, './output.txt');
|
|
370
|
+
const decodedFile = await decodeFileFromChainByIdentifier(client, identifier);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Witness Operations
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import {
|
|
377
|
+
createCKBFSWitness,
|
|
378
|
+
extractCKBFSWitnessContent,
|
|
379
|
+
isCKBFSWitness
|
|
380
|
+
} from '@ckbfs/api';
|
|
381
|
+
|
|
382
|
+
const witness = createCKBFSWitness(contentBytes);
|
|
383
|
+
const { version, content } = extractCKBFSWitnessContent(witness);
|
|
384
|
+
const isValid = isCKBFSWitness(witness);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Identifier Formats
|
|
388
|
+
|
|
389
|
+
CKBFS supports multiple identifier formats for flexible file access:
|
|
390
|
+
|
|
391
|
+
### 1. TypeID Hex String
|
|
392
|
+
```
|
|
393
|
+
0xbce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a
|
|
394
|
+
```
|
|
395
|
+
Direct TypeID from the CKBFS cell's type script args.
|
|
396
|
+
|
|
397
|
+
### 2. CKBFS TypeID URI
|
|
398
|
+
```
|
|
399
|
+
ckbfs://bce89252cece632ef819943bed9cd0e2576f8ce26f9f02075b621b1c9a28056a
|
|
400
|
+
```
|
|
401
|
+
CKBFS URI format using TypeID (without 0x prefix).
|
|
402
|
+
|
|
403
|
+
### 3. CKBFS OutPoint URI
|
|
404
|
+
```
|
|
405
|
+
ckbfs://431c9d668c1815d26eb4f7ac6256eb350ab351474daea8d588400146ab228780i0
|
|
406
|
+
```
|
|
407
|
+
CKBFS URI format using transaction hash and output index: `ckbfs://{txHash}i{index}`
|
|
408
|
+
|
|
409
|
+
### Identifier Detection
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
import { parseIdentifier, IdentifierType } from '@ckbfs/api';
|
|
413
|
+
|
|
414
|
+
const parsed = parseIdentifier('ckbfs://abc123...i0');
|
|
415
|
+
console.log(parsed.type); // IdentifierType.OutPoint
|
|
416
|
+
console.log(parsed.txHash); // '0xabc123...'
|
|
417
|
+
console.log(parsed.index); // 0
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Protocol Details
|
|
421
|
+
|
|
422
|
+
### CKBFS Data Structure
|
|
423
|
+
|
|
424
|
+
CKBFS uses a molecule-encoded data structure stored in cell output data:
|
|
425
|
+
|
|
426
|
+
**V1 Format:**
|
|
427
|
+
```
|
|
428
|
+
{
|
|
429
|
+
index: number, // Single witness index
|
|
430
|
+
checksum: number, // Adler32 checksum
|
|
431
|
+
contentType: string, // MIME type
|
|
432
|
+
filename: string, // Original filename
|
|
433
|
+
backLinks: BackLink[] // Previous versions
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**V2 Format:**
|
|
438
|
+
```
|
|
439
|
+
{
|
|
440
|
+
indexes: number[], // Multiple witness indexes
|
|
441
|
+
checksum: number, // Adler32 checksum
|
|
442
|
+
contentType: string, // MIME type
|
|
443
|
+
filename: string, // Original filename
|
|
444
|
+
backLinks: BackLink[] // Previous versions
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Witness Format
|
|
449
|
+
|
|
450
|
+
CKBFS witnesses contain:
|
|
451
|
+
- 5-byte header: "CKBFS" (0x43, 0x4B, 0x42, 0x46, 0x53)
|
|
452
|
+
- 1-byte version: 0x00
|
|
453
|
+
- Variable-length content data
|
|
454
|
+
|
|
455
|
+
### Checksum Algorithm
|
|
456
|
+
|
|
457
|
+
CKBFS uses Adler32 for content integrity verification. When appending content, the checksum is updated using the rolling Adler32 algorithm to maintain cumulative integrity across all file versions.
|
|
458
|
+
|
|
459
|
+
## Development
|
|
460
|
+
|
|
461
|
+
### Building
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
npm run build
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Testing
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
npm test
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Running Examples
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
# Publish example
|
|
477
|
+
npm run example:publish
|
|
478
|
+
|
|
479
|
+
# Append example (requires existing transaction hash)
|
|
480
|
+
npm run example:append -- --txhash=0x123456...
|
|
481
|
+
|
|
482
|
+
# Retrieve example (demonstrates witness decoding and generic identifier APIs)
|
|
483
|
+
npm run example:retrieve -- --txhash=0x123456...
|
|
484
|
+
|
|
485
|
+
# All examples
|
|
486
|
+
npm run example
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## Environment Variables
|
|
490
|
+
|
|
491
|
+
- `CKB_PRIVATE_KEY`: Your CKB private key for examples
|
|
492
|
+
- `PUBLISH_TX_HASH`: Transaction hash for append examples
|
|
493
|
+
- `TARGET_TX_HASH`: Transaction hash for retrieve examples
|
|
494
|
+
|
|
495
|
+
## Advanced Usage
|
|
496
|
+
|
|
497
|
+
### Working with Different Identifier Formats
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
import {
|
|
501
|
+
getFileContentFromChainByIdentifier,
|
|
502
|
+
parseIdentifier,
|
|
503
|
+
IdentifierType
|
|
504
|
+
} from '@ckbfs/api';
|
|
505
|
+
|
|
506
|
+
// Example identifiers
|
|
507
|
+
const identifiers = [
|
|
508
|
+
'0xabc123...', // TypeID hex
|
|
509
|
+
'ckbfs://abc123...', // CKBFS TypeID URI
|
|
510
|
+
'ckbfs://def456...i0' // CKBFS OutPoint URI
|
|
511
|
+
];
|
|
512
|
+
|
|
513
|
+
// Process any identifier format
|
|
514
|
+
for (const id of identifiers) {
|
|
515
|
+
const parsed = parseIdentifier(id);
|
|
516
|
+
console.log(`Processing ${parsed.type} identifier`);
|
|
517
|
+
|
|
518
|
+
const fileData = await getFileContentFromChainByIdentifier(client, id);
|
|
519
|
+
if (fileData) {
|
|
520
|
+
console.log(`Retrieved: ${fileData.filename}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Batch File Operations
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
// Retrieve multiple files using different identifier formats
|
|
529
|
+
const fileIdentifiers = [
|
|
530
|
+
'ckbfs://file1-typeid...',
|
|
531
|
+
'ckbfs://tx-hash1...i0',
|
|
532
|
+
'0xfile2-typeid...'
|
|
533
|
+
];
|
|
534
|
+
|
|
535
|
+
const files = await Promise.all(
|
|
536
|
+
fileIdentifiers.map(id =>
|
|
537
|
+
getFileContentFromChainByIdentifier(client, id)
|
|
538
|
+
)
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
files.forEach((file, index) => {
|
|
542
|
+
if (file) {
|
|
543
|
+
console.log(`File ${index + 1}: ${file.filename} (${file.size} bytes)`);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
## Network Configuration
|
|
549
|
+
|
|
550
|
+
The SDK supports both CKB mainnet and testnet with different deployed contract addresses:
|
|
551
|
+
|
|
552
|
+
### Testnet (Default)
|
|
553
|
+
- CKBFS V1: `0xe8905ad29a02cf8befa9c258f4f941773839a618d75a64afc22059de9413f712`
|
|
554
|
+
- CKBFS V2: `0x31e6376287d223b8c0410d562fb422f04d1d617b2947596a14c3d2efb7218d3a`
|
|
555
|
+
|
|
556
|
+
### Mainnet
|
|
557
|
+
- CKBFS V2: `0x31e6376287d223b8c0410d562fb422f04d1d617b2947596a14c3d2efb7218d3a`
|
|
558
|
+
|
|
559
|
+
## License
|
|
560
|
+
|
|
561
|
+
MIT
|
|
562
|
+
|
|
563
|
+
## Contributing
|
|
564
|
+
|
|
565
|
+
Contributions are welcome. Please ensure all tests pass and follow the existing code style.
|
|
566
|
+
|
|
567
|
+
## Support
|
|
568
|
+
|
|
569
|
+
For issues and questions, please use the GitHub issue tracker.
|
package/code.png
ADDED
|
Binary file
|
package/demo-output.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Hollo, WKBFS! This is a test file. More content in second chunk. Last churk of the test file.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Hello CKBFS from direct content!
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Script, Signer, Transaction } from "@ckb-ccc/core";
|
|
2
|
-
import { calculateChecksum, verifyChecksum, updateChecksum, verifyWitnessChecksum } from
|
|
3
|
-
import { createCKBFSCell, createPublishTransaction as utilCreatePublishTransaction, createAppendTransaction as utilCreateAppendTransaction, publishCKBFS as utilPublishCKBFS, appendCKBFS as utilAppendCKBFS, CKBFSCellOptions, PublishOptions, AppendOptions } from
|
|
4
|
-
import { readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile, getFileContentFromChain, saveFileFromChain } from
|
|
5
|
-
import { createCKBFSWitness, createTextCKBFSWitness, extractCKBFSWitnessContent, isCKBFSWitness, createChunkedCKBFSWitnesses } from
|
|
6
|
-
import { CKBFSData, BackLinkV1, BackLinkV2, CKBFSDataType, BackLinkType, CKBFS_HEADER, CKBFS_HEADER_STRING } from
|
|
7
|
-
import { NetworkType, ProtocolVersion, DEFAULT_NETWORK, DEFAULT_VERSION, CKBFS_CODE_HASH, CKBFS_TYPE_ID, ADLER32_CODE_HASH, ADLER32_TYPE_ID, DEP_GROUP_TX_HASH, DEPLOY_TX_HASH, getCKBFSScriptConfig, CKBFSScriptConfig } from
|
|
2
|
+
import { calculateChecksum, verifyChecksum, updateChecksum, verifyWitnessChecksum } from "./utils/checksum";
|
|
3
|
+
import { createCKBFSCell, createPublishTransaction as utilCreatePublishTransaction, createAppendTransaction as utilCreateAppendTransaction, publishCKBFS as utilPublishCKBFS, appendCKBFS as utilAppendCKBFS, CKBFSCellOptions, PublishOptions, AppendOptions } from "./utils/transaction";
|
|
4
|
+
import { readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile, getFileContentFromChain, saveFileFromChain, getFileContentFromChainByTypeId, saveFileFromChainByTypeId, decodeFileFromChainByTypeId, getFileContentFromChainByIdentifier, saveFileFromChainByIdentifier, decodeFileFromChainByIdentifier, parseIdentifier, IdentifierType, decodeWitnessContent, decodeMultipleWitnessContents, extractFileFromWitnesses, decodeFileFromWitnessData, saveFileFromWitnessData } from "./utils/file";
|
|
5
|
+
import { createCKBFSWitness, createTextCKBFSWitness, extractCKBFSWitnessContent, isCKBFSWitness, createChunkedCKBFSWitnesses } from "./utils/witness";
|
|
6
|
+
import { CKBFSData, BackLinkV1, BackLinkV2, CKBFSDataType, BackLinkType, CKBFS_HEADER, CKBFS_HEADER_STRING } from "./utils/molecule";
|
|
7
|
+
import { NetworkType, ProtocolVersion, ProtocolVersionType, DEFAULT_NETWORK, DEFAULT_VERSION, CKBFS_CODE_HASH, CKBFS_TYPE_ID, ADLER32_CODE_HASH, ADLER32_TYPE_ID, DEP_GROUP_TX_HASH, DEPLOY_TX_HASH, getCKBFSScriptConfig, CKBFSScriptConfig } from "./utils/constants";
|
|
8
8
|
/**
|
|
9
9
|
* Custom options for file publishing and appending
|
|
10
10
|
*/
|
|
@@ -14,19 +14,19 @@ export interface FileOptions {
|
|
|
14
14
|
capacity?: bigint;
|
|
15
15
|
feeRate?: number;
|
|
16
16
|
network?: NetworkType;
|
|
17
|
-
version?:
|
|
17
|
+
version?: ProtocolVersionType;
|
|
18
18
|
useTypeID?: boolean;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Options required when publishing content directly (string or Uint8Array)
|
|
22
22
|
*/
|
|
23
|
-
export type PublishContentOptions = Omit<FileOptions,
|
|
23
|
+
export type PublishContentOptions = Omit<FileOptions, "capacity" | "contentType" | "filename"> & Required<Pick<FileOptions, "contentType" | "filename">> & {
|
|
24
24
|
capacity?: bigint;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
27
|
* Options required when appending content directly (string or Uint8Array)
|
|
28
28
|
*/
|
|
29
|
-
export type AppendContentOptions = Omit<FileOptions,
|
|
29
|
+
export type AppendContentOptions = Omit<FileOptions, "contentType" | "filename" | "capacity"> & {
|
|
30
30
|
capacity?: bigint;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
@@ -34,9 +34,10 @@ export type AppendContentOptions = Omit<FileOptions, 'contentType' | 'filename'
|
|
|
34
34
|
*/
|
|
35
35
|
export interface CKBFSOptions {
|
|
36
36
|
chunkSize?: number;
|
|
37
|
-
version?:
|
|
37
|
+
version?: ProtocolVersionType;
|
|
38
38
|
useTypeID?: boolean;
|
|
39
39
|
network?: NetworkType;
|
|
40
|
+
rpcUrl?: string;
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
42
43
|
* Main CKBFS SDK class
|
|
@@ -47,6 +48,7 @@ export declare class CKBFS {
|
|
|
47
48
|
private network;
|
|
48
49
|
private version;
|
|
49
50
|
private useTypeID;
|
|
51
|
+
private rpcUrl;
|
|
50
52
|
/**
|
|
51
53
|
* Creates a new CKBFS SDK instance
|
|
52
54
|
* @param signerOrPrivateKey The signer instance or CKB private key to use for signing transactions
|
|
@@ -90,7 +92,7 @@ export declare class CKBFS {
|
|
|
90
92
|
* @param options Additional options for the append operation
|
|
91
93
|
* @returns Promise resolving to the transaction hash
|
|
92
94
|
*/
|
|
93
|
-
appendFile(filePath: string, ckbfsCell: AppendOptions[
|
|
95
|
+
appendFile(filePath: string, ckbfsCell: AppendOptions["ckbfsCell"], options?: Omit<FileOptions, "contentType" | "filename">): Promise<string>;
|
|
94
96
|
/**
|
|
95
97
|
* Appends content (string or Uint8Array) directly to an existing CKBFS file
|
|
96
98
|
* @param content The content string or byte array to append
|
|
@@ -98,7 +100,7 @@ export declare class CKBFS {
|
|
|
98
100
|
* @param options Additional options for the append operation
|
|
99
101
|
* @returns Promise resolving to the transaction hash
|
|
100
102
|
*/
|
|
101
|
-
appendContent(content: string | Uint8Array, ckbfsCell: AppendOptions[
|
|
103
|
+
appendContent(content: string | Uint8Array, ckbfsCell: AppendOptions["ckbfsCell"], options?: AppendContentOptions): Promise<string>;
|
|
102
104
|
/**
|
|
103
105
|
* Creates a new transaction for publishing a file but doesn't sign or send it
|
|
104
106
|
* @param filePath The path to the file to publish
|
|
@@ -120,7 +122,7 @@ export declare class CKBFS {
|
|
|
120
122
|
* @param options Additional options for the append operation
|
|
121
123
|
* @returns Promise resolving to the unsigned transaction
|
|
122
124
|
*/
|
|
123
|
-
createAppendTransaction(filePath: string, ckbfsCell: AppendOptions[
|
|
125
|
+
createAppendTransaction(filePath: string, ckbfsCell: AppendOptions["ckbfsCell"], options?: Omit<FileOptions, "contentType" | "filename">): Promise<Transaction>;
|
|
124
126
|
/**
|
|
125
127
|
* Creates a new transaction for appending content (string or Uint8Array) directly, but doesn't sign or send it
|
|
126
128
|
* @param content The content string or byte array to append
|
|
@@ -128,6 +130,6 @@ export declare class CKBFS {
|
|
|
128
130
|
* @param options Additional options for the append operation
|
|
129
131
|
* @returns Promise resolving to the unsigned transaction
|
|
130
132
|
*/
|
|
131
|
-
createAppendContentTransaction(content: string | Uint8Array, ckbfsCell: AppendOptions[
|
|
133
|
+
createAppendContentTransaction(content: string | Uint8Array, ckbfsCell: AppendOptions["ckbfsCell"], options?: AppendContentOptions): Promise<Transaction>;
|
|
132
134
|
}
|
|
133
|
-
export { calculateChecksum, verifyChecksum, updateChecksum, verifyWitnessChecksum, createCKBFSCell, utilCreatePublishTransaction as createPublishTransaction, utilCreateAppendTransaction as createAppendTransaction, utilPublishCKBFS as publishCKBFS, utilAppendCKBFS as appendCKBFS, readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile, getFileContentFromChain, saveFileFromChain, createCKBFSWitness, createTextCKBFSWitness, extractCKBFSWitnessContent, isCKBFSWitness, createChunkedCKBFSWitnesses, CKBFSData, BackLinkV1, BackLinkV2, CKBFSDataType, BackLinkType, CKBFSCellOptions, PublishOptions, AppendOptions, CKBFS_HEADER, CKBFS_HEADER_STRING, NetworkType, ProtocolVersion, DEFAULT_NETWORK, DEFAULT_VERSION, CKBFS_CODE_HASH, CKBFS_TYPE_ID, ADLER32_CODE_HASH, ADLER32_TYPE_ID, DEP_GROUP_TX_HASH, DEPLOY_TX_HASH, getCKBFSScriptConfig, CKBFSScriptConfig };
|
|
135
|
+
export { calculateChecksum, verifyChecksum, updateChecksum, verifyWitnessChecksum, createCKBFSCell, utilCreatePublishTransaction as createPublishTransaction, utilCreateAppendTransaction as createAppendTransaction, utilPublishCKBFS as publishCKBFS, utilAppendCKBFS as appendCKBFS, readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile, getFileContentFromChain, saveFileFromChain, getFileContentFromChainByTypeId, saveFileFromChainByTypeId, decodeFileFromChainByTypeId, getFileContentFromChainByIdentifier, saveFileFromChainByIdentifier, decodeFileFromChainByIdentifier, parseIdentifier, IdentifierType, decodeWitnessContent, decodeMultipleWitnessContents, extractFileFromWitnesses, decodeFileFromWitnessData, saveFileFromWitnessData, createCKBFSWitness, createTextCKBFSWitness, extractCKBFSWitnessContent, isCKBFSWitness, createChunkedCKBFSWitnesses, CKBFSData, BackLinkV1, BackLinkV2, CKBFSDataType, BackLinkType, CKBFSCellOptions, PublishOptions, AppendOptions, CKBFS_HEADER, CKBFS_HEADER_STRING, NetworkType, ProtocolVersion, ProtocolVersionType, DEFAULT_NETWORK, DEFAULT_VERSION, CKBFS_CODE_HASH, CKBFS_TYPE_ID, ADLER32_CODE_HASH, ADLER32_TYPE_ID, DEP_GROUP_TX_HASH, DEPLOY_TX_HASH, getCKBFSScriptConfig, CKBFSScriptConfig, };
|