@ckbfs/api 1.5.1 → 2.0.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.
Files changed (45) hide show
  1. package/README.md +31 -6
  2. package/RFC.v3.md +210 -0
  3. package/dist/index.d.ts +72 -7
  4. package/dist/index.js +437 -75
  5. package/dist/utils/checksum.d.ts +16 -0
  6. package/dist/utils/checksum.js +74 -8
  7. package/dist/utils/constants.d.ts +2 -1
  8. package/dist/utils/constants.js +12 -2
  9. package/dist/utils/file.d.ts +44 -0
  10. package/dist/utils/file.js +303 -30
  11. package/dist/utils/molecule.d.ts +13 -1
  12. package/dist/utils/molecule.js +32 -5
  13. package/dist/utils/transaction-backup.d.ts +117 -0
  14. package/dist/utils/transaction-backup.js +624 -0
  15. package/dist/utils/transaction.d.ts +7 -115
  16. package/dist/utils/transaction.js +45 -622
  17. package/dist/utils/transactions/index.d.ts +8 -0
  18. package/dist/utils/transactions/index.js +31 -0
  19. package/dist/utils/transactions/shared.d.ts +57 -0
  20. package/dist/utils/transactions/shared.js +17 -0
  21. package/dist/utils/transactions/v1v2.d.ts +80 -0
  22. package/dist/utils/transactions/v1v2.js +592 -0
  23. package/dist/utils/transactions/v3.d.ts +124 -0
  24. package/dist/utils/transactions/v3.js +369 -0
  25. package/dist/utils/witness.d.ts +45 -0
  26. package/dist/utils/witness.js +145 -3
  27. package/examples/append-v3.ts +310 -0
  28. package/examples/chunked-publish.ts +307 -0
  29. package/examples/publish-v3.ts +152 -0
  30. package/examples/publish.ts +4 -4
  31. package/examples/retrieve-v3.ts +222 -0
  32. package/package.json +6 -2
  33. package/small-example.txt +1 -0
  34. package/src/index.ts +568 -87
  35. package/src/utils/checksum.ts +90 -9
  36. package/src/utils/constants.ts +19 -2
  37. package/src/utils/file.ts +386 -35
  38. package/src/utils/molecule.ts +43 -6
  39. package/src/utils/transaction-backup.ts +849 -0
  40. package/src/utils/transaction.ts +39 -848
  41. package/src/utils/transactions/index.ts +16 -0
  42. package/src/utils/transactions/shared.ts +64 -0
  43. package/src/utils/transactions/v1v2.ts +791 -0
  44. package/src/utils/transactions/v3.ts +564 -0
  45. package/src/utils/witness.ts +193 -0
@@ -1,9 +1,21 @@
1
1
  import { CKBFS_HEADER } from './molecule';
2
+ import { ProtocolVersion, ProtocolVersionType } from './constants';
2
3
 
3
4
  /**
4
5
  * Utility functions for creating and handling CKBFS witnesses
5
6
  */
6
7
 
8
+ /**
9
+ * V3 witness structure for backlinks and content chaining
10
+ */
11
+ export interface CKBFSV3WitnessOptions {
12
+ previousTxHash?: string; // 32 bytes, 0x00...00 for publish
13
+ previousWitnessIndex?: number; // 4 bytes, 0x00000000 for publish
14
+ previousChecksum?: number; // 4 bytes, 0x00000000 for publish
15
+ nextIndex?: number; // 4 bytes, 0x00000000 for tail witness
16
+ content: Uint8Array;
17
+ }
18
+
7
19
  /**
8
20
  * Creates a CKBFS witness with content
9
21
  * @param content The content to include in the witness
@@ -66,6 +78,187 @@ export function isCKBFSWitness(witness: Uint8Array): boolean {
66
78
  return headerString === 'CKBFS';
67
79
  }
68
80
 
81
+ /**
82
+ * Creates a CKBFS v3 witness with structured format
83
+ * @param options Witness options including backlink data and content
84
+ * @returns Uint8Array containing the v3 witness data
85
+ */
86
+ export function createCKBFSV3Witness(options: CKBFSV3WitnessOptions): Uint8Array {
87
+ const {
88
+ previousTxHash = '0x' + '00'.repeat(32),
89
+ previousWitnessIndex = 0,
90
+ previousChecksum = 0,
91
+ nextIndex = 0,
92
+ content
93
+ } = options;
94
+
95
+ // Create witness components
96
+ const header = CKBFS_HEADER; // "CKBFS" (5 bytes)
97
+ const version = new Uint8Array([0x03]); // Version 3 (1 byte)
98
+
99
+ // Previous position: txHash (32 bytes) + witnessIndex (4 bytes)
100
+ const prevTxHashBytes = new Uint8Array(32);
101
+ if (previousTxHash !== '0x' + '00'.repeat(32)) {
102
+ const hexStr = previousTxHash.startsWith('0x') ? previousTxHash.slice(2) : previousTxHash;
103
+ for (let i = 0; i < 32; i++) {
104
+ prevTxHashBytes[i] = parseInt(hexStr.substr(i * 2, 2), 16);
105
+ }
106
+ }
107
+
108
+ const prevWitnessIndexBytes = new Uint8Array(4);
109
+ new DataView(prevWitnessIndexBytes.buffer).setUint32(0, previousWitnessIndex, true); // little-endian
110
+
111
+ // Previous checksum (4 bytes)
112
+ const prevChecksumBytes = new Uint8Array(4);
113
+ new DataView(prevChecksumBytes.buffer).setUint32(0, previousChecksum, true); // little-endian
114
+
115
+ // Next index (4 bytes)
116
+ const nextIndexBytes = new Uint8Array(4);
117
+ new DataView(nextIndexBytes.buffer).setUint32(0, nextIndex, true); // little-endian
118
+
119
+ // Combine all parts
120
+ return Buffer.concat([
121
+ header, // 5 bytes: "CKBFS"
122
+ version, // 1 byte: 0x03
123
+ prevTxHashBytes, // 32 bytes: previous tx hash
124
+ prevWitnessIndexBytes, // 4 bytes: previous witness index
125
+ prevChecksumBytes, // 4 bytes: previous checksum
126
+ nextIndexBytes, // 4 bytes: next index
127
+ content // variable: content bytes
128
+ ]);
129
+ }
130
+
131
+ /**
132
+ * Creates an array of v3 witnesses for chunked content
133
+ * @param contentChunks Array of content chunks
134
+ * @param options Backlink options for head witness and start index
135
+ * @returns Array of Uint8Array witnesses
136
+ */
137
+ export function createChunkedCKBFSV3Witnesses(
138
+ contentChunks: Uint8Array[],
139
+ options: Omit<CKBFSV3WitnessOptions, 'content' | 'nextIndex'> & { startIndex?: number } = {}
140
+ ): Uint8Array[] {
141
+ if (contentChunks.length === 0) {
142
+ return [];
143
+ }
144
+
145
+ // Default start index to 1 if not provided (witness 0 is typically for signing)
146
+ const startIndex = options.startIndex || 1;
147
+ const witnesses: Uint8Array[] = [];
148
+
149
+ for (let i = 0; i < contentChunks.length; i++) {
150
+ const isHead = i === 0;
151
+ const isTail = i === contentChunks.length - 1;
152
+
153
+ if (isHead) {
154
+ // Head witness with backlink info
155
+ witnesses.push(createCKBFSV3Witness({
156
+ ...options,
157
+ nextIndex: isTail ? 0 : startIndex + i + 1, // Correct next witness index calculation
158
+ content: contentChunks[i]
159
+ }));
160
+ } else {
161
+ // Middle/tail witness with minimal header
162
+ const nextIndex = isTail ? 0 : startIndex + i + 1;
163
+ const nextIndexBytes = new Uint8Array(4);
164
+ new DataView(nextIndexBytes.buffer).setUint32(0, nextIndex, true);
165
+
166
+ witnesses.push(Buffer.concat([
167
+ nextIndexBytes, // 4 bytes: next index
168
+ contentChunks[i] // variable: content bytes
169
+ ]));
170
+ }
171
+ }
172
+
173
+ return witnesses;
174
+ }
175
+
176
+ /**
177
+ * Extracts content from a CKBFS v3 witness
178
+ * @param witness The CKBFS v3 witness data
179
+ * @param isHeadWitness Whether this is the head witness or a continuation witness
180
+ * @returns Object containing the extracted data
181
+ */
182
+ export function extractCKBFSV3WitnessContent(witness: Uint8Array, isHeadWitness: boolean = true): {
183
+ version?: number;
184
+ previousTxHash?: string;
185
+ previousWitnessIndex?: number;
186
+ previousChecksum?: number;
187
+ nextIndex: number;
188
+ content: Uint8Array;
189
+ } {
190
+ if (isHeadWitness) {
191
+ // Head witness format: CKBFS(5) + version(1) + prevTxHash(32) + prevWitnessIndex(4) + prevChecksum(4) + nextIndex(4) + content
192
+ if (witness.length < 50) {
193
+ throw new Error('Invalid CKBFS v3 head witness: too short');
194
+ }
195
+
196
+ const header = witness.slice(0, 5);
197
+ const headerString = new TextDecoder().decode(header);
198
+
199
+ if (headerString !== 'CKBFS') {
200
+ throw new Error('Invalid CKBFS v3 head witness: missing CKBFS header');
201
+ }
202
+
203
+ const version = witness[5];
204
+ if (version !== 0x03) {
205
+ throw new Error(`Invalid CKBFS v3 head witness: expected version 0x03, got 0x${version.toString(16)}`);
206
+ }
207
+
208
+ // Extract previous position
209
+ const prevTxHashBytes = witness.slice(6, 38);
210
+ const previousTxHash = '0x' + Array.from(prevTxHashBytes).map(b => b.toString(16).padStart(2, '0')).join('');
211
+
212
+ const previousWitnessIndex = new DataView(witness.slice(38, 42).buffer).getUint32(0, true);
213
+ const previousChecksum = new DataView(witness.slice(42, 46).buffer).getUint32(0, true);
214
+ const nextIndex = new DataView(witness.slice(46, 50).buffer).getUint32(0, true);
215
+ const content = witness.slice(50);
216
+
217
+ return {
218
+ version,
219
+ previousTxHash,
220
+ previousWitnessIndex,
221
+ previousChecksum,
222
+ nextIndex,
223
+ content
224
+ };
225
+ } else {
226
+ // Continuation witness format: nextIndex(4) + content
227
+ if (witness.length < 4) {
228
+ throw new Error('Invalid CKBFS v3 continuation witness: too short');
229
+ }
230
+
231
+ const nextIndex = new DataView(witness.slice(0, 4).buffer).getUint32(0, true);
232
+ const content = witness.slice(4);
233
+
234
+ return {
235
+ nextIndex,
236
+ content
237
+ };
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Checks if a witness is a valid CKBFS v3 witness
243
+ * @param witness The witness data to check
244
+ * @returns Boolean indicating whether the witness is a valid CKBFS v3 witness
245
+ */
246
+ export function isCKBFSV3Witness(witness: Uint8Array): boolean {
247
+ if (witness.length < 50) {
248
+ return false;
249
+ }
250
+
251
+ const header = witness.slice(0, 5);
252
+ const headerString = new TextDecoder().decode(header);
253
+
254
+ if (headerString !== 'CKBFS') {
255
+ return false;
256
+ }
257
+
258
+ const version = witness[5];
259
+ return version === 0x03;
260
+ }
261
+
69
262
  /**
70
263
  * Creates an array of witnesses for a CKBFS transaction from content chunks
71
264
  * @param contentChunks Array of content chunks