@ckbfs/api 1.5.1 → 2.0.1
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 +31 -6
- package/RFC.v3.md +210 -0
- package/dist/index.d.ts +72 -7
- package/dist/index.js +437 -75
- package/dist/utils/checksum.d.ts +16 -0
- package/dist/utils/checksum.js +74 -8
- package/dist/utils/constants.d.ts +2 -1
- package/dist/utils/constants.js +12 -2
- package/dist/utils/file.d.ts +44 -0
- package/dist/utils/file.js +303 -30
- package/dist/utils/molecule.d.ts +13 -1
- package/dist/utils/molecule.js +32 -5
- package/dist/utils/transaction-backup.d.ts +117 -0
- package/dist/utils/transaction-backup.js +624 -0
- package/dist/utils/transaction.d.ts +7 -115
- package/dist/utils/transaction.js +45 -622
- package/dist/utils/transactions/index.d.ts +8 -0
- package/dist/utils/transactions/index.js +31 -0
- package/dist/utils/transactions/shared.d.ts +57 -0
- package/dist/utils/transactions/shared.js +17 -0
- package/dist/utils/transactions/v1v2.d.ts +80 -0
- package/dist/utils/transactions/v1v2.js +592 -0
- package/dist/utils/transactions/v3.d.ts +124 -0
- package/dist/utils/transactions/v3.js +369 -0
- package/dist/utils/witness.d.ts +45 -0
- package/dist/utils/witness.js +145 -3
- package/examples/append-v3.ts +310 -0
- package/examples/chunked-publish.ts +307 -0
- package/examples/publish-v3.ts +152 -0
- package/examples/publish.ts +4 -4
- package/examples/retrieve-v3.ts +222 -0
- package/package.json +6 -2
- package/small-example.txt +1 -0
- package/src/index.ts +568 -87
- package/src/utils/checksum.ts +90 -9
- package/src/utils/constants.ts +19 -2
- package/src/utils/file.ts +386 -35
- package/src/utils/molecule.ts +43 -6
- package/src/utils/transaction-backup.ts +849 -0
- package/src/utils/transaction.ts +39 -848
- package/src/utils/transactions/index.ts +16 -0
- package/src/utils/transactions/shared.ts +64 -0
- package/src/utils/transactions/v1v2.ts +791 -0
- package/src/utils/transactions/v3.ts +564 -0
- package/src/utils/witness.ts +193 -0
package/src/utils/witness.ts
CHANGED
|
@@ -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
|