@ckbfs/api 1.5.0 → 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.
- package/README.md +31 -6
- package/RFC.v3.md +210 -0
- package/dist/index.d.ts +72 -7
- package/dist/index.js +440 -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 -105
- package/dist/utils/transaction.js +45 -565
- 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
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { CKBFS, NetworkType, ProtocolVersion, PublishContentOptions } from '../src/index';
|
|
2
|
+
import { ClientPublicTestnet, Transaction, ccc } from "@ckb-ccc/core";
|
|
3
|
+
import { readFileSync, statSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
// Replace with your actual private key
|
|
7
|
+
const privateKey = process.env.CKB_PRIVATE_KEY || 'your-private-key-here';
|
|
8
|
+
|
|
9
|
+
// Configuration for chunked publishing
|
|
10
|
+
const CHUNK_SIZE_LIMIT = 490 * 1024; // 490KB configurable limit
|
|
11
|
+
const TRANSACTION_CHUNK_SIZE = 31 * 1024; // 31KB per transaction (SDK internal chunking)
|
|
12
|
+
|
|
13
|
+
// Initialize the SDK with network and version options
|
|
14
|
+
const ckbfs = new CKBFS(
|
|
15
|
+
privateKey,
|
|
16
|
+
NetworkType.Testnet, // Use testnet
|
|
17
|
+
{
|
|
18
|
+
version: ProtocolVersion.V2, // Use the latest version (V2)
|
|
19
|
+
chunkSize: TRANSACTION_CHUNK_SIZE,
|
|
20
|
+
useTypeID: false, // Use code hash instead of type ID
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// Initialize CKB client for transaction confirmation
|
|
25
|
+
const client = new ClientPublicTestnet();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Wait for transaction confirmation by polling
|
|
29
|
+
* @param txHash The transaction hash to wait for
|
|
30
|
+
* @param maxRetries Maximum number of retries (default: 60)
|
|
31
|
+
* @param retryInterval Interval between retries in milliseconds (default: 5000)
|
|
32
|
+
*/
|
|
33
|
+
async function waitForTransactionConfirmation(
|
|
34
|
+
txHash: string,
|
|
35
|
+
maxRetries: number = 60,
|
|
36
|
+
retryInterval: number = 5000
|
|
37
|
+
): Promise<boolean> {
|
|
38
|
+
console.log(`Waiting for transaction confirmation: ${txHash}`);
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
41
|
+
try {
|
|
42
|
+
const txWithStatus = await client.getTransaction(txHash);
|
|
43
|
+
if (txWithStatus && txWithStatus.status === 'committed') {
|
|
44
|
+
console.log(`✅ Transaction confirmed after ${i + 1} attempts`);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(`⏳ Attempt ${i + 1}/${maxRetries} - Transaction status: ${txWithStatus?.status || 'unknown'}`);
|
|
49
|
+
|
|
50
|
+
if (i < maxRetries - 1) {
|
|
51
|
+
await new Promise(resolve => setTimeout(resolve, retryInterval));
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.log(`❌ Error checking transaction status (attempt ${i + 1}/${maxRetries}):`, error);
|
|
55
|
+
if (i < maxRetries - 1) {
|
|
56
|
+
await new Promise(resolve => setTimeout(resolve, retryInterval));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(`❌ Transaction not confirmed after ${maxRetries} attempts`);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get file information from a transaction (needed for append operations)
|
|
67
|
+
* @param txHash The transaction hash
|
|
68
|
+
*/
|
|
69
|
+
async function getCellInfoFromTransaction(txHash: string) {
|
|
70
|
+
try {
|
|
71
|
+
const txWithStatus = await client.getTransaction(txHash);
|
|
72
|
+
if (!txWithStatus || !txWithStatus.transaction) {
|
|
73
|
+
throw new Error(`Transaction ${txHash} not found`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const tx = Transaction.from(txWithStatus.transaction);
|
|
77
|
+
const output = tx.outputs[0]; // First output should be CKBFS cell
|
|
78
|
+
|
|
79
|
+
if (!output || !output.type) {
|
|
80
|
+
throw new Error('No CKBFS cell found in the transaction');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Parse output data using CKBFS utilities
|
|
84
|
+
const outputData = tx.outputsData[0];
|
|
85
|
+
const rawData = outputData.startsWith('0x')
|
|
86
|
+
? ccc.bytesFrom(outputData.slice(2), 'hex')
|
|
87
|
+
: Buffer.from(outputData, 'hex');
|
|
88
|
+
|
|
89
|
+
// Import CKBFSData utility for proper parsing
|
|
90
|
+
const { CKBFSData } = await import('../src/utils/molecule');
|
|
91
|
+
|
|
92
|
+
// Try to unpack CKBFS data with both protocol versions
|
|
93
|
+
let ckbfsData: any;
|
|
94
|
+
try {
|
|
95
|
+
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V2);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
try {
|
|
98
|
+
ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V1);
|
|
99
|
+
} catch (v1Error) {
|
|
100
|
+
throw new Error(`Failed to unpack CKBFS data: V2(${error}), V1(${v1Error})`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
outPoint: {
|
|
106
|
+
txHash,
|
|
107
|
+
index: 0
|
|
108
|
+
},
|
|
109
|
+
type: output.type,
|
|
110
|
+
lock: output.lock,
|
|
111
|
+
capacity: output.capacity,
|
|
112
|
+
data: ckbfsData // Now properly parsed CKBFSDataType
|
|
113
|
+
};
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('Error retrieving cell info:', error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Publish a large file using chunked approach
|
|
122
|
+
* @param filePath Path to the file to publish
|
|
123
|
+
* @param options Publishing options
|
|
124
|
+
*/
|
|
125
|
+
async function publishLargeFile(
|
|
126
|
+
filePath: string,
|
|
127
|
+
options: {
|
|
128
|
+
contentType?: string;
|
|
129
|
+
filename?: string;
|
|
130
|
+
maxChunkSize?: number;
|
|
131
|
+
} = {}
|
|
132
|
+
): Promise<string> {
|
|
133
|
+
try {
|
|
134
|
+
// Check if file exists and get its size
|
|
135
|
+
const fileStats = statSync(filePath);
|
|
136
|
+
const fileSize = fileStats.size;
|
|
137
|
+
const maxChunkSize = options.maxChunkSize || CHUNK_SIZE_LIMIT;
|
|
138
|
+
|
|
139
|
+
console.log(`📁 File: ${filePath}`);
|
|
140
|
+
console.log(`📏 Size: ${fileSize} bytes (${(fileSize / 1024 / 1024).toFixed(2)} MB)`);
|
|
141
|
+
console.log(`🔧 Chunk size limit: ${maxChunkSize} bytes (${(maxChunkSize / 1024 / 1024).toFixed(2)} MB)`);
|
|
142
|
+
|
|
143
|
+
// Get address info
|
|
144
|
+
const address = await ckbfs.getAddress();
|
|
145
|
+
console.log(`💼 Using address: ${address.toString()}`);
|
|
146
|
+
|
|
147
|
+
// Determine content type and filename
|
|
148
|
+
const filename = options.filename || filePath.split(/[\\\/]/).pop() || 'unknown';
|
|
149
|
+
const contentType = options.contentType || 'application/octet-stream';
|
|
150
|
+
|
|
151
|
+
if (fileSize <= maxChunkSize) {
|
|
152
|
+
console.log('📤 File size is within limit - publishing directly...');
|
|
153
|
+
|
|
154
|
+
const txHash = await ckbfs.publishFile(filePath, {
|
|
155
|
+
contentType,
|
|
156
|
+
filename
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
console.log(`✅ File published successfully in single transaction!`);
|
|
160
|
+
console.log(`📋 Transaction Hash: ${txHash}`);
|
|
161
|
+
console.log(`🔗 View at: https://pudge.explorer.nervos.org/transaction/${txHash}`);
|
|
162
|
+
|
|
163
|
+
return txHash;
|
|
164
|
+
} else {
|
|
165
|
+
console.log('📦 File is too large - using chunked publishing...');
|
|
166
|
+
console.log(`🔢 Will split into ${Math.ceil(fileSize / maxChunkSize)} chunks`);
|
|
167
|
+
|
|
168
|
+
// Read file content
|
|
169
|
+
const fileContent = readFileSync(filePath);
|
|
170
|
+
|
|
171
|
+
// Calculate number of chunks needed
|
|
172
|
+
const numChunks = Math.ceil(fileSize / maxChunkSize);
|
|
173
|
+
let lastTxHash = '';
|
|
174
|
+
let ckbfsCell: any = null;
|
|
175
|
+
|
|
176
|
+
for (let chunkIndex = 0; chunkIndex < numChunks; chunkIndex++) {
|
|
177
|
+
const start = chunkIndex * maxChunkSize;
|
|
178
|
+
const end = Math.min(start + maxChunkSize, fileSize);
|
|
179
|
+
const chunkContent = fileContent.slice(start, end);
|
|
180
|
+
|
|
181
|
+
console.log(`\n🔄 Processing chunk ${chunkIndex + 1}/${numChunks}`);
|
|
182
|
+
console.log(`📏 Chunk size: ${chunkContent.length} bytes`);
|
|
183
|
+
|
|
184
|
+
if (chunkIndex === 0) {
|
|
185
|
+
// First chunk - publish as new file
|
|
186
|
+
console.log('📤 Publishing first chunk...');
|
|
187
|
+
|
|
188
|
+
lastTxHash = await ckbfs.publishContent(chunkContent, {
|
|
189
|
+
contentType,
|
|
190
|
+
filename
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
console.log(`✅ First chunk published: ${lastTxHash}`);
|
|
194
|
+
|
|
195
|
+
} else {
|
|
196
|
+
// Subsequent chunks - append to existing file
|
|
197
|
+
console.log('➕ Appending chunk...');
|
|
198
|
+
|
|
199
|
+
// Wait for previous transaction to be confirmed before appending
|
|
200
|
+
const isConfirmed = await waitForTransactionConfirmation(lastTxHash);
|
|
201
|
+
if (!isConfirmed) {
|
|
202
|
+
throw new Error(`Previous transaction ${lastTxHash} was not confirmed`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Get cell info from the last transaction
|
|
206
|
+
ckbfsCell = await getCellInfoFromTransaction(lastTxHash);
|
|
207
|
+
|
|
208
|
+
lastTxHash = await ckbfs.appendContent(chunkContent, ckbfsCell);
|
|
209
|
+
|
|
210
|
+
console.log(`✅ Chunk ${chunkIndex + 1} appended: ${lastTxHash}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log(`🔗 View chunk ${chunkIndex + 1} at: https://pudge.explorer.nervos.org/transaction/${lastTxHash}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Wait for final transaction confirmation
|
|
217
|
+
console.log('\n⏳ Waiting for final transaction confirmation...');
|
|
218
|
+
const finalConfirmed = await waitForTransactionConfirmation(lastTxHash);
|
|
219
|
+
if (!finalConfirmed) {
|
|
220
|
+
console.log('⚠️ Warning: Final transaction was not confirmed within timeout');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`\n🎉 Large file published successfully using ${numChunks} chunks!`);
|
|
224
|
+
console.log(`📋 Final Transaction Hash: ${lastTxHash}`);
|
|
225
|
+
console.log(`🔗 View final result at: https://pudge.explorer.nervos.org/transaction/${lastTxHash}`);
|
|
226
|
+
|
|
227
|
+
return lastTxHash;
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.error('❌ Error publishing large file:', error);
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Example demonstrating chunked file publishing
|
|
237
|
+
*/
|
|
238
|
+
async function chunkedPublishExample() {
|
|
239
|
+
try {
|
|
240
|
+
// Example 1: Small file (should publish directly)
|
|
241
|
+
console.log('\n=== Example 1: Small File (Direct Publishing) ===');
|
|
242
|
+
const smallFilePath = './small-example.txt';
|
|
243
|
+
|
|
244
|
+
// Create a small test file
|
|
245
|
+
const fs = require('fs');
|
|
246
|
+
if (!fs.existsSync(smallFilePath)) {
|
|
247
|
+
fs.writeFileSync(smallFilePath, 'This is a small test file that should be published directly.');
|
|
248
|
+
console.log(`Created test file: ${smallFilePath}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
console.log('\n=== Example 2: Large File (Chunked Publishing) ===');
|
|
252
|
+
|
|
253
|
+
// For demonstration purposes, create a larger file or use an existing one
|
|
254
|
+
const largeFilePath = '/mnt/old/nvme_data/ヨルシカ - 春泥棒(OFFICIAL VIDEO) [Sw1Flgub9s8].mp4';
|
|
255
|
+
|
|
256
|
+
// Create a large test file (6MB) if it doesn't exist
|
|
257
|
+
if (!fs.existsSync(largeFilePath)) {
|
|
258
|
+
console.log('Creating large test file (6MB) for demonstration...');
|
|
259
|
+
const largeContent = Buffer.alloc(6 * 1024 * 1024, 0x41); // 6MB of 'A' characters
|
|
260
|
+
fs.writeFileSync(largeFilePath, largeContent);
|
|
261
|
+
console.log(`Created large test file: ${largeFilePath}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
await publishLargeFile(largeFilePath, {
|
|
265
|
+
contentType: 'video/mp4',
|
|
266
|
+
filename: 'ヨルシカ - 春泥棒(OFFICIAL VIDEO).mp4',
|
|
267
|
+
maxChunkSize: CHUNK_SIZE_LIMIT // 4MB chunks
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error('Example failed:', error);
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Main function to run the chunked publishing example
|
|
278
|
+
*/
|
|
279
|
+
async function main() {
|
|
280
|
+
console.log('🚀 Running CKBFS Chunked Publishing Example...');
|
|
281
|
+
console.log('===============================================');
|
|
282
|
+
console.log(`📋 Protocol Version: ${ProtocolVersion.V2}`);
|
|
283
|
+
console.log(`📏 Chunk Size Limit: ${(CHUNK_SIZE_LIMIT / 1024 / 1024).toFixed(2)} MB`);
|
|
284
|
+
console.log(`🔧 Transaction Chunk Size: ${(TRANSACTION_CHUNK_SIZE / 1024).toFixed(2)} KB`);
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
await chunkedPublishExample();
|
|
288
|
+
console.log('\n🎉 All examples completed successfully!');
|
|
289
|
+
process.exit(0);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error('\n❌ Examples failed:', error);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Run the example if this file is executed directly
|
|
297
|
+
if (require.main === module) {
|
|
298
|
+
main().catch(console.error);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export {
|
|
302
|
+
publishLargeFile,
|
|
303
|
+
waitForTransactionConfirmation,
|
|
304
|
+
getCellInfoFromTransaction,
|
|
305
|
+
CHUNK_SIZE_LIMIT,
|
|
306
|
+
TRANSACTION_CHUNK_SIZE
|
|
307
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { CKBFS, NetworkType, ProtocolVersion, PublishContentOptions } from '../src/index';
|
|
2
|
+
|
|
3
|
+
// Replace with your actual private key
|
|
4
|
+
const privateKey = process.env.CKB_PRIVATE_KEY || 'your-private-key-here';
|
|
5
|
+
|
|
6
|
+
// Initialize the SDK for CKBFS v3
|
|
7
|
+
const ckbfs = new CKBFS(
|
|
8
|
+
privateKey,
|
|
9
|
+
NetworkType.Testnet, // Use testnet
|
|
10
|
+
{
|
|
11
|
+
version: ProtocolVersion.V3, // Use v3
|
|
12
|
+
chunkSize: 30 * 1024, // 30KB chunks
|
|
13
|
+
useTypeID: false, // Use code hash instead of type ID
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Example of publishing a file to CKBFS v3
|
|
19
|
+
*/
|
|
20
|
+
async function publishFileV3Example() {
|
|
21
|
+
try {
|
|
22
|
+
// Get address info
|
|
23
|
+
const address = await ckbfs.getAddress();
|
|
24
|
+
console.log(`Using address: ${address.toString()}`);
|
|
25
|
+
|
|
26
|
+
// Get CKBFS script config
|
|
27
|
+
const config = ckbfs.getCKBFSConfig();
|
|
28
|
+
console.log('Using CKBFS v3 config:', config);
|
|
29
|
+
|
|
30
|
+
// Publish a text file to CKBFS v3
|
|
31
|
+
const filePath = './examples/example.txt';
|
|
32
|
+
|
|
33
|
+
// You can provide additional options
|
|
34
|
+
const options = {
|
|
35
|
+
contentType: 'text/plain',
|
|
36
|
+
filename: 'v3-example.txt',
|
|
37
|
+
// Specify capacity if needed (default is 200 CKB)
|
|
38
|
+
// capacity: 250n * 100000000n
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
console.log(`Publishing file to CKBFS v3: ${filePath}`);
|
|
42
|
+
const txHash = await ckbfs.publishFileV3(filePath, options);
|
|
43
|
+
|
|
44
|
+
console.log(`File published successfully to CKBFS v3!`);
|
|
45
|
+
console.log(`Transaction Hash: ${txHash}`);
|
|
46
|
+
console.log(`View at: https://pudge.explorer.nervos.org/transaction/${txHash}`);
|
|
47
|
+
|
|
48
|
+
return txHash;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Error publishing file to v3:', error);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Example of publishing content directly (string) to CKBFS v3
|
|
57
|
+
*/
|
|
58
|
+
async function publishContentV3Example() {
|
|
59
|
+
try {
|
|
60
|
+
// Get address info
|
|
61
|
+
const address = await ckbfs.getAddress();
|
|
62
|
+
console.log(`Using address for v3 content publish: ${address.toString()}`);
|
|
63
|
+
|
|
64
|
+
// Define content and options (contentType and filename are required)
|
|
65
|
+
const content = "Hello CKBFS v3 from direct content! This uses witness-based storage with no backlinks in cell data.";
|
|
66
|
+
const options: PublishContentOptions = {
|
|
67
|
+
contentType: 'text/plain',
|
|
68
|
+
filename: 'v3_direct_content_example.txt',
|
|
69
|
+
// You can optionally specify feeRate, network, useTypeID
|
|
70
|
+
// feeRate: 3000
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
console.log(`Publishing direct content to CKBFS v3: "${content}"`);
|
|
74
|
+
const txHash = await ckbfs.publishContentV3(content, options);
|
|
75
|
+
|
|
76
|
+
console.log(`Direct content published successfully to CKBFS v3!`);
|
|
77
|
+
console.log(`Transaction Hash: ${txHash}`);
|
|
78
|
+
console.log(`View at: https://pudge.explorer.nervos.org/transaction/${txHash}`);
|
|
79
|
+
|
|
80
|
+
return txHash;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Error publishing direct content to v3:', error);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Example of publishing content using the simplified V3 API
|
|
89
|
+
*/
|
|
90
|
+
async function publishContentV3WithSimplifiedAPI() {
|
|
91
|
+
try {
|
|
92
|
+
// Get address info
|
|
93
|
+
const address = await ckbfs.getAddress();
|
|
94
|
+
console.log(`Using address for v3 content publish: ${address.toString()}`);
|
|
95
|
+
|
|
96
|
+
// Define content for publishing
|
|
97
|
+
const content = "Hello CKBFS v3! This example shows the simplified publishing API.";
|
|
98
|
+
|
|
99
|
+
// Send the transaction using the public interface
|
|
100
|
+
console.log('\n=== SENDING TRANSACTION ===');
|
|
101
|
+
const txHash = await ckbfs.publishContent(content, {
|
|
102
|
+
contentType: 'text/plain',
|
|
103
|
+
filename: 'v3_tx_example.txt',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log(`Transaction sent successfully!`);
|
|
107
|
+
console.log(`Transaction Hash: ${txHash}`);
|
|
108
|
+
console.log(`View at: https://pudge.explorer.nervos.org/transaction/${txHash}`);
|
|
109
|
+
|
|
110
|
+
return txHash;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('Error in detailed v3 publish example:', error);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Main function to run the v3 publish example
|
|
119
|
+
*/
|
|
120
|
+
async function main() {
|
|
121
|
+
console.log('Running CKBFS v3 publishing example...');
|
|
122
|
+
console.log('=====================================');
|
|
123
|
+
console.log(`Using CKBFS protocol version: ${ProtocolVersion.V3}`);
|
|
124
|
+
console.log('Key v3 features:');
|
|
125
|
+
console.log('- Witnesses-based storage (lower costs)');
|
|
126
|
+
console.log('- No backlinks in cell data');
|
|
127
|
+
console.log('- Structured witnesses with chaining');
|
|
128
|
+
console.log('=====================================');
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
// Run the simplified V3 API example
|
|
132
|
+
await publishContentV3WithSimplifiedAPI();
|
|
133
|
+
console.log('=====================================');
|
|
134
|
+
|
|
135
|
+
// Uncomment to also test the regular publish method:
|
|
136
|
+
// await publishContentV3Example();
|
|
137
|
+
// console.log('=====================================');
|
|
138
|
+
|
|
139
|
+
// Uncomment to also test file publishing:
|
|
140
|
+
// await publishFileV3Example();
|
|
141
|
+
console.log('CKBFS v3 example completed successfully!');
|
|
142
|
+
process.exit(0);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('CKBFS v3 example failed:', error);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Run the example if this file is executed directly
|
|
150
|
+
if (require.main === module) {
|
|
151
|
+
main().catch(console.error);
|
|
152
|
+
}
|
package/examples/publish.ts
CHANGED
|
@@ -28,12 +28,12 @@ async function publishExample() {
|
|
|
28
28
|
console.log('Using CKBFS config:', config);
|
|
29
29
|
|
|
30
30
|
// Publish a text file to CKBFS
|
|
31
|
-
const filePath = './
|
|
31
|
+
const filePath = './examples/example.txt';
|
|
32
32
|
|
|
33
33
|
// You can provide additional options
|
|
34
34
|
const options = {
|
|
35
|
-
contentType: '
|
|
36
|
-
filename: '
|
|
35
|
+
contentType: 'plain/text',
|
|
36
|
+
filename: 'example.txt',
|
|
37
37
|
// Specify capacity if needed (default is 200 CKB)
|
|
38
38
|
// capacity: 250n * 100000000n
|
|
39
39
|
};
|
|
@@ -95,7 +95,7 @@ async function main() {
|
|
|
95
95
|
try {
|
|
96
96
|
await publishExample();
|
|
97
97
|
console.log('----------------------------------');
|
|
98
|
-
await publishContentExample();
|
|
98
|
+
//await publishContentExample();
|
|
99
99
|
console.log('Example completed successfully!');
|
|
100
100
|
process.exit(0);
|
|
101
101
|
} catch (error) {
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getFileContentFromChainByIdentifierV3,
|
|
3
|
+
saveFileFromChainByIdentifierV3,
|
|
4
|
+
ProtocolVersion,
|
|
5
|
+
} from '../src/index';
|
|
6
|
+
import { ClientPublicTestnet } from '@ckb-ccc/core';
|
|
7
|
+
|
|
8
|
+
// Initialize the CKB client
|
|
9
|
+
const client = new ClientPublicTestnet(); // Use testnet
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Example of retrieving a file from CKBFS v3 using TypeID
|
|
13
|
+
*/
|
|
14
|
+
async function retrieveFileByTypeIdV3Example() {
|
|
15
|
+
try {
|
|
16
|
+
// Example TypeID (replace with actual TypeID from a v3 published file)
|
|
17
|
+
const typeId = "0xc387111856be837b1f358d49a9d7e4461dc18244c2b7b59f9012617b1c09d7be";
|
|
18
|
+
|
|
19
|
+
console.log(`Retrieving CKBFS v3 file by TypeID: ${typeId}`);
|
|
20
|
+
|
|
21
|
+
const fileData = await getFileContentFromChainByIdentifierV3(
|
|
22
|
+
client,
|
|
23
|
+
typeId,
|
|
24
|
+
{
|
|
25
|
+
network: "testnet",
|
|
26
|
+
version: ProtocolVersion.V3,
|
|
27
|
+
useTypeID: false
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (!fileData) {
|
|
32
|
+
console.log('File not found or failed to retrieve');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('CKBFS v3 file retrieved successfully!');
|
|
37
|
+
console.log(`Filename: ${fileData.filename}`);
|
|
38
|
+
console.log(`Content type: ${fileData.contentType}`);
|
|
39
|
+
console.log(`Size: ${fileData.size} bytes`);
|
|
40
|
+
console.log(`Checksum: ${fileData.checksum}`);
|
|
41
|
+
|
|
42
|
+
// Enhanced content preview to better understand the file content
|
|
43
|
+
const contentText = fileData.content.toString();
|
|
44
|
+
console.log(`Full content length: ${contentText.length} characters`);
|
|
45
|
+
|
|
46
|
+
if (contentText.length <= 200) {
|
|
47
|
+
console.log(`Full content: "${contentText}"`);
|
|
48
|
+
} else {
|
|
49
|
+
console.log(`Content preview (first 100 chars): "${contentText.slice(0, 100)}"`);
|
|
50
|
+
console.log(`Content preview (last 100 chars): "${contentText.slice(-100)}"`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check if content looks like it might be truncated or only showing appends
|
|
54
|
+
const lines = contentText.split('\n');
|
|
55
|
+
console.log(`Content has ${lines.length} lines`);
|
|
56
|
+
if (lines.length > 3) {
|
|
57
|
+
console.log(`First line: "${lines[0]}"`);
|
|
58
|
+
console.log(`Last line: "${lines[lines.length - 1]}"`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return fileData;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('Error retrieving file by TypeID from v3:', error);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Example of retrieving and saving a file from CKBFS v3
|
|
70
|
+
*/
|
|
71
|
+
async function retrieveAndSaveFileV3Example() {
|
|
72
|
+
try {
|
|
73
|
+
// Example TypeID (replace with actual TypeID from a v3 published file)
|
|
74
|
+
const typeId = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
|
|
75
|
+
|
|
76
|
+
console.log(`Retrieving and saving CKBFS v3 file by TypeID: ${typeId}`);
|
|
77
|
+
|
|
78
|
+
const savedPath = await saveFileFromChainByIdentifierV3(
|
|
79
|
+
client,
|
|
80
|
+
typeId,
|
|
81
|
+
'./retrieved_v3_file.txt', // Optional output path
|
|
82
|
+
{
|
|
83
|
+
network: "testnet",
|
|
84
|
+
version: ProtocolVersion.V3,
|
|
85
|
+
useTypeID: false
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (!savedPath) {
|
|
90
|
+
console.log('File not found or failed to save');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(`CKBFS v3 file saved successfully to: ${savedPath}`);
|
|
95
|
+
|
|
96
|
+
return savedPath;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('Error retrieving and saving file from v3:', error);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Example of retrieving a file using CKBFS URI format
|
|
105
|
+
*/
|
|
106
|
+
async function retrieveFileByURIV3Example() {
|
|
107
|
+
try {
|
|
108
|
+
// Example CKBFS URI (replace with actual URI)
|
|
109
|
+
const ckbfsUri = "ckbfs://c387111856be837b1f358d49a9d7e4461dc18244c2b7b59f9012617b1c09d7be";
|
|
110
|
+
|
|
111
|
+
console.log(`Retrieving CKBFS v3 file by URI: ${ckbfsUri}`);
|
|
112
|
+
|
|
113
|
+
const fileData = await getFileContentFromChainByIdentifierV3(
|
|
114
|
+
client,
|
|
115
|
+
ckbfsUri,
|
|
116
|
+
{
|
|
117
|
+
network: "testnet",
|
|
118
|
+
version: ProtocolVersion.V3,
|
|
119
|
+
useTypeID: false
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!fileData) {
|
|
124
|
+
console.log('File not found or failed to retrieve');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log('CKBFS v3 file retrieved successfully using URI!');
|
|
129
|
+
console.log(`Filename: ${fileData.filename}`);
|
|
130
|
+
console.log(`Content type: ${fileData.contentType}`);
|
|
131
|
+
console.log(`Size: ${fileData.size} bytes`);
|
|
132
|
+
console.log(`Checksum: ${fileData.checksum}`);
|
|
133
|
+
console.log(`Content: ${fileData.content}`);
|
|
134
|
+
|
|
135
|
+
return fileData;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('Error retrieving file by URI from v3:', error);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Example of retrieving a file using OutPoint format
|
|
144
|
+
*/
|
|
145
|
+
async function retrieveFileByOutPointV3Example() {
|
|
146
|
+
try {
|
|
147
|
+
// Example OutPoint URI (replace with actual transaction hash and index)
|
|
148
|
+
const outPointUri = "ckbfs://1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefi0";
|
|
149
|
+
|
|
150
|
+
console.log(`Retrieving CKBFS v3 file by OutPoint: ${outPointUri}`);
|
|
151
|
+
|
|
152
|
+
const fileData = await getFileContentFromChainByIdentifierV3(
|
|
153
|
+
client,
|
|
154
|
+
outPointUri,
|
|
155
|
+
{
|
|
156
|
+
network: "testnet",
|
|
157
|
+
version: ProtocolVersion.V3,
|
|
158
|
+
useTypeID: false
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
if (!fileData) {
|
|
163
|
+
console.log('File not found or failed to retrieve');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log('CKBFS v3 file retrieved successfully using OutPoint!');
|
|
168
|
+
console.log(`Filename: ${fileData.filename}`);
|
|
169
|
+
console.log(`Content type: ${fileData.contentType}`);
|
|
170
|
+
console.log(`Size: ${fileData.size} bytes`);
|
|
171
|
+
console.log(`Checksum: ${fileData.checksum}`);
|
|
172
|
+
|
|
173
|
+
return fileData;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error('Error retrieving file by OutPoint from v3:', error);
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Main function to run the v3 retrieval examples
|
|
182
|
+
*/
|
|
183
|
+
async function main() {
|
|
184
|
+
console.log('Running CKBFS v3 file retrieval examples...');
|
|
185
|
+
console.log('============================================');
|
|
186
|
+
console.log(`Using CKBFS protocol version: ${ProtocolVersion.V3}`);
|
|
187
|
+
console.log('Key v3 retrieval features:');
|
|
188
|
+
console.log('- Follows witness-based backlink chain');
|
|
189
|
+
console.log('- Reconstructs file from witness content');
|
|
190
|
+
console.log('- Supports TypeID, URI, and OutPoint formats');
|
|
191
|
+
console.log('============================================');
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
console.log('Note: These examples require existing CKBFS v3 files.');
|
|
195
|
+
console.log('Please run publish-v3.ts first to create v3 files,');
|
|
196
|
+
console.log('then update the identifiers in this example.');
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log('Supported identifier formats:');
|
|
199
|
+
console.log('- TypeID: 0x1234...abcdef');
|
|
200
|
+
console.log('- CKBFS URI: ckbfs://1234...abcdef');
|
|
201
|
+
console.log('- OutPoint URI: ckbfs://1234...abcdefi0');
|
|
202
|
+
console.log('');
|
|
203
|
+
|
|
204
|
+
// Uncomment to test different retrieval methods:
|
|
205
|
+
await retrieveFileByTypeIdV3Example();
|
|
206
|
+
// await retrieveAndSaveFileV3Example();
|
|
207
|
+
await retrieveFileByURIV3Example();
|
|
208
|
+
// await retrieveFileByOutPointV3Example();
|
|
209
|
+
|
|
210
|
+
console.log('Retrieval example structure completed successfully!');
|
|
211
|
+
console.log('Update the identifiers and uncomment methods to test.');
|
|
212
|
+
process.exit(0);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error('CKBFS v3 retrieval examples failed:', error);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Run the example if this file is executed directly
|
|
220
|
+
if (require.main === module) {
|
|
221
|
+
main().catch(console.error);
|
|
222
|
+
}
|