@ckbfs/api 1.2.4 → 1.2.6
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 +506 -74
- package/code.png +0 -0
- package/demo-output.txt +1 -0
- package/direct_direct_content_example.txt +1 -0
- package/dist/index.d.ts +55 -13
- package/dist/index.js +143 -18
- 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 +10 -4
- package/dist/utils/transaction.js +29 -27
- package/examples/example.txt +1 -0
- package/examples/identifier-test.ts +178 -0
- package/examples/index.ts +36 -24
- package/examples/publish.ts +40 -6
- package/examples/retrieve.ts +542 -77
- 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 +317 -97
- 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 +172 -146
- package/traditional_direct_content_example.txt +1 -0
- package/typeid_direct_content_example.txt +1 -0
- package/.cursor/rules/typescript.mdc +0 -49
- package/ABC.LOGS +0 -1
- package/RFC.v2.md +0 -341
- package/append.txt +0 -1
- package/example.txt +0 -1
- package/publish-tx-hash-v1.txt +0 -1
- package/src/utils/createPublishTransaction +0 -24
- package/test-download.txt +0 -2
package/examples/retrieve.ts
CHANGED
@@ -1,110 +1,575 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import {
|
2
|
+
CKBFS,
|
3
|
+
NetworkType,
|
4
|
+
ProtocolVersion,
|
5
|
+
ProtocolVersionType,
|
6
|
+
CKBFSDataType,
|
7
|
+
CKBFSData,
|
8
|
+
decodeWitnessContent,
|
9
|
+
decodeMultipleWitnessContents,
|
10
|
+
extractFileFromWitnesses,
|
11
|
+
decodeFileFromWitnessData,
|
12
|
+
saveFileFromWitnessData,
|
13
|
+
getFileContentFromChain,
|
14
|
+
saveFileFromChain,
|
15
|
+
getFileContentFromChainByTypeId,
|
16
|
+
saveFileFromChainByTypeId,
|
17
|
+
decodeFileFromChainByTypeId,
|
18
|
+
getFileContentFromChainByIdentifier,
|
19
|
+
saveFileFromChainByIdentifier,
|
20
|
+
decodeFileFromChainByIdentifier,
|
21
|
+
parseIdentifier,
|
22
|
+
IdentifierType,
|
23
|
+
} from "../src/index";
|
24
|
+
import { Script, ClientPublicTestnet, Transaction, ccc } from "@ckb-ccc/core";
|
3
25
|
|
4
|
-
// Replace with your actual private key (
|
5
|
-
const privateKey = process.env.CKB_PRIVATE_KEY ||
|
26
|
+
// Replace with your actual private key (optional for this example)
|
27
|
+
const privateKey = process.env.CKB_PRIVATE_KEY || "your-private-key-here";
|
6
28
|
|
7
29
|
// Parse command line arguments for transaction hash
|
8
|
-
const txHashArg = process.argv.find(arg => arg.startsWith(
|
9
|
-
const
|
30
|
+
const txHashArg = process.argv.find((arg) => arg.startsWith("--txhash="));
|
31
|
+
const targetTxHash = txHashArg
|
32
|
+
? txHashArg.split("=")[1]
|
33
|
+
: process.env.TARGET_TX_HASH ||
|
34
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000";
|
10
35
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
// Initialize the SDK (read-only is fine for retrieval)
|
20
|
-
const ckbfs = new CKBFS(
|
21
|
-
privateKey,
|
22
|
-
NetworkType.Testnet,
|
23
|
-
{
|
24
|
-
version: ProtocolVersion.V2,
|
25
|
-
useTypeID: false
|
36
|
+
/**
|
37
|
+
* Ensures a string is prefixed with '0x'
|
38
|
+
* @param value The string to ensure is hex prefixed
|
39
|
+
* @returns A hex prefixed string
|
40
|
+
*/
|
41
|
+
function ensureHexPrefix(value: string): `0x${string}` {
|
42
|
+
if (value.startsWith("0x")) {
|
43
|
+
return value as `0x${string}`;
|
26
44
|
}
|
27
|
-
|
45
|
+
return `0x${value}` as `0x${string}`;
|
46
|
+
}
|
28
47
|
|
29
48
|
// Initialize CKB client for testnet
|
30
49
|
const client = new ClientPublicTestnet();
|
31
50
|
|
32
51
|
/**
|
33
|
-
* Example of retrieving
|
52
|
+
* Example of retrieving and decoding file content using traditional blockchain method
|
34
53
|
*/
|
35
|
-
async function
|
54
|
+
async function traditionalRetrieveExample() {
|
55
|
+
console.log("=== Traditional Blockchain Retrieval Method ===");
|
56
|
+
|
36
57
|
try {
|
37
|
-
|
38
|
-
|
39
|
-
// Get transaction details
|
40
|
-
const txWithStatus = await client.getTransaction(txHash);
|
58
|
+
// Get transaction from blockchain
|
59
|
+
const txWithStatus = await client.getTransaction(targetTxHash);
|
41
60
|
if (!txWithStatus || !txWithStatus.transaction) {
|
42
|
-
throw new Error(`Transaction ${
|
43
|
-
}
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
61
|
+
throw new Error(`Transaction ${targetTxHash} not found`);
|
62
|
+
}
|
63
|
+
|
64
|
+
const tx = Transaction.from(txWithStatus.transaction);
|
65
|
+
console.log(`Transaction found with ${tx.outputs.length} outputs`);
|
66
|
+
|
67
|
+
// Find CKBFS cell (first output with type script)
|
68
|
+
const ckbfsCellIndex = 0;
|
69
|
+
const output = tx.outputs[ckbfsCellIndex];
|
70
|
+
if (!output || !output.type) {
|
71
|
+
throw new Error("No CKBFS cell found in the transaction");
|
72
|
+
}
|
73
|
+
|
74
|
+
// Get and parse output data
|
50
75
|
const outputData = tx.outputsData[ckbfsCellIndex];
|
51
76
|
if (!outputData) {
|
52
|
-
throw new Error(
|
53
|
-
}
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
77
|
+
throw new Error("Output data not found");
|
78
|
+
}
|
79
|
+
|
80
|
+
const rawData = outputData.startsWith("0x")
|
81
|
+
? ccc.bytesFrom(outputData.slice(2), "hex")
|
82
|
+
: Buffer.from(outputData, "hex");
|
83
|
+
|
84
|
+
// Try both protocol versions for unpacking
|
85
|
+
let ckbfsData: CKBFSDataType;
|
86
|
+
let version: ProtocolVersionType;
|
87
|
+
|
88
|
+
try {
|
89
|
+
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V2);
|
90
|
+
version = ProtocolVersion.V2;
|
91
|
+
} catch (error) {
|
92
|
+
console.log("Failed to unpack as V2, trying V1...");
|
93
|
+
try {
|
94
|
+
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V1);
|
95
|
+
version = ProtocolVersion.V1;
|
96
|
+
} catch (v1Error) {
|
97
|
+
throw new Error(
|
98
|
+
`Failed to unpack CKBFS data with both versions: V2(${error}), V1(${v1Error})`,
|
99
|
+
);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
console.log(`Successfully unpacked CKBFS data using ${version}:`);
|
104
|
+
console.log(`- Filename: ${ckbfsData.filename}`);
|
105
|
+
console.log(`- Content Type: ${ckbfsData.contentType}`);
|
106
|
+
console.log(`- Checksum: ${ckbfsData.checksum}`);
|
107
|
+
console.log(`- Backlinks: ${ckbfsData.backLinks?.length || 0}`);
|
108
|
+
|
109
|
+
// Retrieve complete file content using traditional method
|
110
|
+
const outPoint = { txHash: targetTxHash, index: ckbfsCellIndex };
|
111
|
+
const content = await getFileContentFromChain(client, outPoint, ckbfsData);
|
112
|
+
|
113
|
+
// Save file using traditional method
|
114
|
+
const savedPath = saveFileFromChain(
|
115
|
+
content,
|
116
|
+
ckbfsData,
|
117
|
+
`./traditional_${ckbfsData.filename}`,
|
118
|
+
);
|
119
|
+
|
120
|
+
console.log(`Traditional method completed. File saved to: ${savedPath}`);
|
121
|
+
console.log(`File size: ${content.length} bytes`);
|
122
|
+
|
123
|
+
return { ckbfsData, tx, content };
|
124
|
+
} catch (error) {
|
125
|
+
console.error("Traditional retrieval failed:", error);
|
126
|
+
throw error;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Example of decoding file content directly from witnesses (new method)
|
132
|
+
*/
|
133
|
+
async function directWitnessDecodingExample() {
|
134
|
+
console.log("\n=== Direct Witness Decoding Method (New) ===");
|
135
|
+
|
136
|
+
try {
|
137
|
+
// Get transaction from blockchain
|
138
|
+
const txWithStatus = await client.getTransaction(targetTxHash);
|
139
|
+
if (!txWithStatus || !txWithStatus.transaction) {
|
140
|
+
throw new Error(`Transaction ${targetTxHash} not found`);
|
141
|
+
}
|
142
|
+
|
143
|
+
const tx = Transaction.from(txWithStatus.transaction);
|
144
|
+
|
145
|
+
// Get CKBFS data to know which witnesses contain content
|
146
|
+
const outputData = tx.outputsData[0];
|
147
|
+
if (!outputData) {
|
148
|
+
throw new Error("Output data not found");
|
149
|
+
}
|
150
|
+
|
151
|
+
const rawData = outputData.startsWith("0x")
|
152
|
+
? ccc.bytesFrom(outputData.slice(2), "hex")
|
153
|
+
: Buffer.from(outputData, "hex");
|
154
|
+
|
155
|
+
let ckbfsData: CKBFSDataType;
|
156
|
+
let version: ProtocolVersionType;
|
157
|
+
|
71
158
|
try {
|
72
|
-
console.log('Trying to unpack with V2...');
|
73
159
|
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V2);
|
160
|
+
version = ProtocolVersion.V2;
|
74
161
|
} catch (error) {
|
75
|
-
console.log('Failed with V2, trying V1...');
|
76
162
|
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V1);
|
163
|
+
version = ProtocolVersion.V1;
|
77
164
|
}
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
165
|
+
|
166
|
+
console.log(`Using protocol ${version} for witness decoding`);
|
167
|
+
|
168
|
+
// Get witness indexes from CKBFS data
|
169
|
+
const indexes =
|
170
|
+
ckbfsData.indexes ||
|
171
|
+
(ckbfsData.index !== undefined ? [ckbfsData.index] : []);
|
172
|
+
console.log(`Content witness indexes: ${indexes.join(", ")}`);
|
173
|
+
|
174
|
+
// Example 1: Decode individual witnesses
|
175
|
+
console.log("\n--- Example 1: Decode Individual Witnesses ---");
|
176
|
+
for (const idx of indexes) {
|
177
|
+
if (idx < tx.witnesses.length) {
|
178
|
+
const decoded = decodeWitnessContent(tx.witnesses[idx]);
|
179
|
+
if (decoded) {
|
180
|
+
console.log(
|
181
|
+
`Witness ${idx}: ${decoded.content.length} bytes, valid: ${decoded.isValid}`,
|
182
|
+
);
|
183
|
+
} else {
|
184
|
+
console.log(`Witness ${idx}: Not a valid CKBFS witness`);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
// Example 2: Decode multiple witnesses at once
|
190
|
+
console.log("\n--- Example 2: Decode Multiple Witnesses ---");
|
191
|
+
const relevantWitnesses = indexes
|
192
|
+
.map((idx) => tx.witnesses[idx])
|
193
|
+
.filter(Boolean);
|
194
|
+
const combinedContent = decodeMultipleWitnessContents(
|
195
|
+
relevantWitnesses,
|
196
|
+
true,
|
197
|
+
);
|
198
|
+
console.log(
|
199
|
+
`Combined content from ${relevantWitnesses.length} witnesses: ${combinedContent.length} bytes`,
|
200
|
+
);
|
201
|
+
|
202
|
+
// Example 3: Extract file using witness indexes
|
203
|
+
console.log("\n--- Example 3: Extract File Using Indexes ---");
|
204
|
+
const extractedContent = extractFileFromWitnesses(tx.witnesses, indexes);
|
205
|
+
console.log(`Extracted content: ${extractedContent.length} bytes`);
|
206
|
+
|
207
|
+
// Example 4: High-level decode with metadata
|
208
|
+
console.log("\n--- Example 4: High-level Decode with Metadata ---");
|
209
|
+
const decodedFile = decodeFileFromWitnessData({
|
210
|
+
witnesses: tx.witnesses,
|
211
|
+
indexes: indexes,
|
212
|
+
filename: ckbfsData.filename,
|
213
|
+
contentType: ckbfsData.contentType,
|
214
|
+
});
|
215
|
+
|
216
|
+
console.log(`Decoded file:`);
|
217
|
+
console.log(`- Filename: ${decodedFile.filename}`);
|
218
|
+
console.log(`- Content Type: ${decodedFile.contentType}`);
|
219
|
+
console.log(`- Size: ${decodedFile.size} bytes`);
|
220
|
+
|
221
|
+
// Example 5: Save file directly from witness data
|
222
|
+
console.log("\n--- Example 5: Save File from Witness Data ---");
|
223
|
+
const savedPath = saveFileFromWitnessData(
|
224
|
+
{
|
225
|
+
witnesses: tx.witnesses,
|
226
|
+
indexes: indexes,
|
227
|
+
filename: ckbfsData.filename,
|
228
|
+
contentType: ckbfsData.contentType,
|
229
|
+
},
|
230
|
+
`./direct_${ckbfsData.filename}`,
|
231
|
+
);
|
232
|
+
|
233
|
+
console.log(
|
234
|
+
`Direct witness decoding completed. File saved to: ${savedPath}`,
|
235
|
+
);
|
236
|
+
|
237
|
+
return { decodedFile, extractedContent };
|
89
238
|
} catch (error) {
|
90
|
-
console.error(
|
239
|
+
console.error("Direct witness decoding failed:", error);
|
91
240
|
throw error;
|
92
241
|
}
|
93
242
|
}
|
94
243
|
|
95
244
|
/**
|
96
|
-
*
|
245
|
+
* Example of handling backlinks with direct witness decoding
|
246
|
+
*/
|
247
|
+
async function backlinksDecodingExample() {
|
248
|
+
console.log("\n=== Backlinks Decoding Example ===");
|
249
|
+
|
250
|
+
try {
|
251
|
+
// Get main transaction
|
252
|
+
const txWithStatus = await client.getTransaction(targetTxHash);
|
253
|
+
if (!txWithStatus || !txWithStatus.transaction) {
|
254
|
+
throw new Error(`Transaction ${targetTxHash} not found`);
|
255
|
+
}
|
256
|
+
|
257
|
+
const tx = Transaction.from(txWithStatus.transaction);
|
258
|
+
const outputData = tx.outputsData[0];
|
259
|
+
const rawData = outputData.startsWith("0x")
|
260
|
+
? ccc.bytesFrom(outputData.slice(2), "hex")
|
261
|
+
: Buffer.from(outputData, "hex");
|
262
|
+
|
263
|
+
let ckbfsData: CKBFSDataType;
|
264
|
+
try {
|
265
|
+
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V2);
|
266
|
+
} catch (error) {
|
267
|
+
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V1);
|
268
|
+
}
|
269
|
+
|
270
|
+
console.log(`Found ${ckbfsData.backLinks?.length || 0} backlinks`);
|
271
|
+
|
272
|
+
if (ckbfsData.backLinks && ckbfsData.backLinks.length > 0) {
|
273
|
+
// Process each backlink
|
274
|
+
for (let i = 0; i < ckbfsData.backLinks.length; i++) {
|
275
|
+
const backlink = ckbfsData.backLinks[i];
|
276
|
+
console.log(`\nProcessing backlink ${i + 1}:`);
|
277
|
+
console.log(`- Transaction: ${backlink.txHash}`);
|
278
|
+
console.log(`- Checksum: ${backlink.checksum}`);
|
279
|
+
|
280
|
+
try {
|
281
|
+
// Get backlink transaction
|
282
|
+
const backTxWithStatus = await client.getTransaction(backlink.txHash);
|
283
|
+
if (backTxWithStatus && backTxWithStatus.transaction) {
|
284
|
+
const backTx = Transaction.from(backTxWithStatus.transaction);
|
285
|
+
|
286
|
+
// Get backlink indexes
|
287
|
+
const backIndexes =
|
288
|
+
backlink.indexes ||
|
289
|
+
(backlink.index !== undefined ? [backlink.index] : []);
|
290
|
+
console.log(`- Witness indexes: ${backIndexes.join(", ")}`);
|
291
|
+
|
292
|
+
// Decode content from backlink witnesses
|
293
|
+
const backlinkContent = extractFileFromWitnesses(
|
294
|
+
backTx.witnesses,
|
295
|
+
backIndexes,
|
296
|
+
);
|
297
|
+
console.log(`- Content size: ${backlinkContent.length} bytes`);
|
298
|
+
|
299
|
+
// Save backlink content
|
300
|
+
const backlinkPath = `./backlink_${i + 1}_content.bin`;
|
301
|
+
require("fs").writeFileSync(backlinkPath, backlinkContent);
|
302
|
+
console.log(`- Saved to: ${backlinkPath}`);
|
303
|
+
}
|
304
|
+
} catch (error) {
|
305
|
+
console.warn(`Failed to process backlink ${i + 1}: ${error}`);
|
306
|
+
}
|
307
|
+
}
|
308
|
+
} else {
|
309
|
+
console.log("No backlinks found in this file");
|
310
|
+
}
|
311
|
+
} catch (error) {
|
312
|
+
console.error("Backlinks decoding failed:", error);
|
313
|
+
throw error;
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
/**
|
318
|
+
* Example of retrieving files using generic identifiers (new method)
|
319
|
+
*/
|
320
|
+
async function genericIdentifierRetrievalExample() {
|
321
|
+
console.log("\n=== Generic Identifier Retrieval Example ===");
|
322
|
+
|
323
|
+
try {
|
324
|
+
// Get transaction to extract TypeID from it
|
325
|
+
const txWithStatus = await client.getTransaction(targetTxHash);
|
326
|
+
if (!txWithStatus || !txWithStatus.transaction) {
|
327
|
+
throw new Error(`Transaction ${targetTxHash} not found`);
|
328
|
+
}
|
329
|
+
|
330
|
+
const tx = Transaction.from(txWithStatus.transaction);
|
331
|
+
|
332
|
+
// Find CKBFS cell (first output with type script)
|
333
|
+
const ckbfsCellIndex = 0;
|
334
|
+
const output = tx.outputs[ckbfsCellIndex];
|
335
|
+
if (!output || !output.type) {
|
336
|
+
throw new Error("No CKBFS cell found in the transaction");
|
337
|
+
}
|
338
|
+
|
339
|
+
// Extract TypeID from the type script args
|
340
|
+
const typeId = output.type.args;
|
341
|
+
console.log(`Extracted TypeID: ${typeId}`);
|
342
|
+
|
343
|
+
if (!typeId || typeId === "0x") {
|
344
|
+
console.log(
|
345
|
+
"No TypeID found in this transaction, skipping identifier examples",
|
346
|
+
);
|
347
|
+
return;
|
348
|
+
}
|
349
|
+
|
350
|
+
// Create different identifier formats for demonstration
|
351
|
+
const identifiers = [
|
352
|
+
typeId, // Pure TypeID hex string
|
353
|
+
`ckbfs://${typeId.slice(2)}`, // CKBFS URI with TypeID
|
354
|
+
`ckbfs://${targetTxHash.slice(2)}i${ckbfsCellIndex}`, // CKBFS URI with outPoint
|
355
|
+
];
|
356
|
+
|
357
|
+
console.log("\nTesting different identifier formats:");
|
358
|
+
identifiers.forEach((id, index) => {
|
359
|
+
const parsed = parseIdentifier(id);
|
360
|
+
console.log(`${index + 1}. ${id}`);
|
361
|
+
console.log(` Type: ${parsed.type}`);
|
362
|
+
if (parsed.typeId) console.log(` TypeID: ${parsed.typeId}`);
|
363
|
+
if (parsed.txHash)
|
364
|
+
console.log(` TxHash: ${parsed.txHash}, Index: ${parsed.index}`);
|
365
|
+
});
|
366
|
+
|
367
|
+
// Example 1: Get file content using generic identifier (traditional method with backlinks)
|
368
|
+
console.log(
|
369
|
+
"\n--- Example 1: Get File Content by Generic Identifier (Traditional) ---",
|
370
|
+
);
|
371
|
+
|
372
|
+
// Test with TypeID format
|
373
|
+
const fileData = await getFileContentFromChainByIdentifier(
|
374
|
+
client,
|
375
|
+
identifiers[0],
|
376
|
+
{
|
377
|
+
network: "testnet",
|
378
|
+
version: ProtocolVersion.V2,
|
379
|
+
useTypeID: false,
|
380
|
+
},
|
381
|
+
);
|
382
|
+
|
383
|
+
if (fileData) {
|
384
|
+
console.log(`Retrieved file: ${fileData.filename}`);
|
385
|
+
console.log(`Content type: ${fileData.contentType}`);
|
386
|
+
console.log(`Size: ${fileData.size} bytes`);
|
387
|
+
console.log(`Checksum: ${fileData.checksum}`);
|
388
|
+
console.log(`Backlinks: ${fileData.backLinks.length}`);
|
389
|
+
console.log(`Parsed identifier type: ${fileData.parsedId.type}`);
|
390
|
+
} else {
|
391
|
+
console.log("File not found by identifier");
|
392
|
+
return;
|
393
|
+
}
|
394
|
+
|
395
|
+
// Example 2: Test all identifier formats
|
396
|
+
console.log("\n--- Example 2: Test All Identifier Formats ---");
|
397
|
+
for (let i = 0; i < identifiers.length; i++) {
|
398
|
+
const identifier = identifiers[i];
|
399
|
+
console.log(`\nTesting identifier ${i + 1}: ${identifier}`);
|
400
|
+
|
401
|
+
try {
|
402
|
+
const testData = await getFileContentFromChainByIdentifier(
|
403
|
+
client,
|
404
|
+
identifier,
|
405
|
+
{
|
406
|
+
network: "testnet",
|
407
|
+
version: ProtocolVersion.V2,
|
408
|
+
useTypeID: false,
|
409
|
+
},
|
410
|
+
);
|
411
|
+
|
412
|
+
if (testData) {
|
413
|
+
console.log(
|
414
|
+
`✓ Success: ${testData.filename} (${testData.size} bytes)`,
|
415
|
+
);
|
416
|
+
console.log(` Parsed as: ${testData.parsedId.type}`);
|
417
|
+
} else {
|
418
|
+
console.log(`✗ Failed: File not found`);
|
419
|
+
}
|
420
|
+
} catch (error) {
|
421
|
+
console.log(`✗ Error: ${error}`);
|
422
|
+
}
|
423
|
+
}
|
424
|
+
|
425
|
+
// Example 3: Save file using CKBFS URI format
|
426
|
+
console.log("\n--- Example 3: Save File using CKBFS URI ---");
|
427
|
+
const savedPath = await saveFileFromChainByIdentifier(
|
428
|
+
client,
|
429
|
+
identifiers[1], // Use CKBFS URI format
|
430
|
+
`./identifier_${fileData.filename}`,
|
431
|
+
{
|
432
|
+
network: "testnet",
|
433
|
+
version: ProtocolVersion.V2,
|
434
|
+
useTypeID: false,
|
435
|
+
},
|
436
|
+
);
|
437
|
+
|
438
|
+
if (savedPath) {
|
439
|
+
console.log(`File saved via identifier to: ${savedPath}`);
|
440
|
+
}
|
441
|
+
|
442
|
+
// Example 4: Decode file using outPoint format with direct witness method
|
443
|
+
console.log(
|
444
|
+
"\n--- Example 4: Decode File using OutPoint Format (Direct Witness) ---",
|
445
|
+
);
|
446
|
+
const decodedData = await decodeFileFromChainByIdentifier(
|
447
|
+
client,
|
448
|
+
identifiers[2],
|
449
|
+
{
|
450
|
+
network: "testnet",
|
451
|
+
version: ProtocolVersion.V2,
|
452
|
+
useTypeID: false,
|
453
|
+
},
|
454
|
+
);
|
455
|
+
|
456
|
+
if (decodedData) {
|
457
|
+
console.log(`Decoded file: ${decodedData.filename}`);
|
458
|
+
console.log(`Content type: ${decodedData.contentType}`);
|
459
|
+
console.log(`Size: ${decodedData.size} bytes`);
|
460
|
+
console.log(`Checksum: ${decodedData.checksum}`);
|
461
|
+
console.log(`Parsed identifier type: ${decodedData.parsedId.type}`);
|
462
|
+
|
463
|
+
// Verify content matches
|
464
|
+
const contentMatches =
|
465
|
+
Buffer.compare(fileData.content, decodedData.content) === 0;
|
466
|
+
console.log(
|
467
|
+
`Content verification: ${contentMatches ? "PASSED" : "FAILED"}`,
|
468
|
+
);
|
469
|
+
}
|
470
|
+
|
471
|
+
console.log("\nGeneric identifier retrieval completed successfully!");
|
472
|
+
return { fileData, decodedData, identifiers };
|
473
|
+
} catch (error) {
|
474
|
+
console.error("Generic identifier retrieval failed:", error);
|
475
|
+
throw error;
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
/**
|
480
|
+
* Performance comparison between traditional and direct methods
|
481
|
+
*/
|
482
|
+
async function performanceComparisonExample() {
|
483
|
+
console.log("\n=== Performance Comparison ===");
|
484
|
+
|
485
|
+
try {
|
486
|
+
// Traditional method timing
|
487
|
+
const traditionalStart = Date.now();
|
488
|
+
const traditionalResult = await traditionalRetrieveExample();
|
489
|
+
const traditionalTime = Date.now() - traditionalStart;
|
490
|
+
|
491
|
+
// Direct method timing
|
492
|
+
const directStart = Date.now();
|
493
|
+
const directResult = await directWitnessDecodingExample();
|
494
|
+
const directTime = Date.now() - directStart;
|
495
|
+
|
496
|
+
console.log("\n--- Performance Results ---");
|
497
|
+
console.log(`Traditional method: ${traditionalTime}ms`);
|
498
|
+
console.log(`Direct witness method: ${directTime}ms`);
|
499
|
+
console.log(
|
500
|
+
`Performance improvement: ${(((traditionalTime - directTime) / traditionalTime) * 100).toFixed(1)}%`,
|
501
|
+
);
|
502
|
+
|
503
|
+
// Verify content matches
|
504
|
+
const contentMatches =
|
505
|
+
Buffer.compare(
|
506
|
+
traditionalResult.content,
|
507
|
+
directResult.extractedContent,
|
508
|
+
) === 0;
|
509
|
+
console.log(
|
510
|
+
`Content verification: ${contentMatches ? "PASSED" : "FAILED"}`,
|
511
|
+
);
|
512
|
+
} catch (error) {
|
513
|
+
console.error("Performance comparison failed:", error);
|
514
|
+
}
|
515
|
+
}
|
516
|
+
|
517
|
+
/**
|
518
|
+
* Main function to run all examples
|
97
519
|
*/
|
98
520
|
async function main() {
|
99
|
-
console.log(
|
100
|
-
console.log(
|
101
|
-
|
521
|
+
console.log("Running CKBFS retrieve and witness decoding examples...");
|
522
|
+
console.log("=========================================================");
|
523
|
+
console.log(`Target transaction: ${targetTxHash}`);
|
524
|
+
|
525
|
+
// Validate transaction hash
|
526
|
+
if (
|
527
|
+
targetTxHash ===
|
528
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
529
|
+
) {
|
530
|
+
console.error(
|
531
|
+
"Please provide a valid transaction hash by setting the TARGET_TX_HASH environment variable or using --txhash=0x... argument",
|
532
|
+
);
|
533
|
+
console.log("\nExample usage:");
|
534
|
+
console.log(" npm run example:retrieve -- --txhash=0x123456...");
|
535
|
+
console.log(" TARGET_TX_HASH=0x123456... npm run example:retrieve");
|
536
|
+
process.exit(1);
|
537
|
+
}
|
538
|
+
|
102
539
|
try {
|
103
|
-
|
104
|
-
|
540
|
+
// Run traditional method
|
541
|
+
await traditionalRetrieveExample();
|
542
|
+
|
543
|
+
// Run new direct witness decoding methods
|
544
|
+
await directWitnessDecodingExample();
|
545
|
+
|
546
|
+
// Handle backlinks if present
|
547
|
+
await backlinksDecodingExample();
|
548
|
+
|
549
|
+
// Run generic identifier-based retrieval examples
|
550
|
+
await genericIdentifierRetrievalExample();
|
551
|
+
|
552
|
+
console.log("\n=== Summary ===");
|
553
|
+
console.log("All examples completed successfully!");
|
554
|
+
console.log("\nKey advantages of direct witness decoding:");
|
555
|
+
console.log("- No need for recursive blockchain queries");
|
556
|
+
console.log("- Faster performance for simple cases");
|
557
|
+
console.log("- More control over witness processing");
|
558
|
+
console.log("- Useful for offline processing with cached data");
|
559
|
+
console.log("\nTraditional method advantages:");
|
560
|
+
console.log("- Automatic backlink following");
|
561
|
+
console.log("- Complete file reconstruction");
|
562
|
+
console.log("- Built-in error handling for complex cases");
|
563
|
+
console.log("\nGeneric identifier method advantages:");
|
564
|
+
console.log("- Flexible input formats (TypeID, CKBFS URIs, outPoint)");
|
565
|
+
console.log("- Simple interface - works with any identifier format");
|
566
|
+
console.log("- No need to know specific format beforehand");
|
567
|
+
console.log("- Automatic format detection and parsing");
|
568
|
+
console.log("- Works with both traditional and direct witness decoding");
|
569
|
+
|
105
570
|
process.exit(0);
|
106
571
|
} catch (error) {
|
107
|
-
console.error(
|
572
|
+
console.error("Examples failed:", error);
|
108
573
|
process.exit(1);
|
109
574
|
}
|
110
575
|
}
|
@@ -112,4 +577,4 @@ async function main() {
|
|
112
577
|
// Run the example if this file is executed directly
|
113
578
|
if (require.main === module) {
|
114
579
|
main().catch(console.error);
|
115
|
-
}
|
580
|
+
}
|