@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
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ensureHexPrefix = ensureHexPrefix;
|
|
4
4
|
exports.createCKBFSCell = createCKBFSCell;
|
|
5
|
+
exports.preparePublishTransaction = preparePublishTransaction;
|
|
5
6
|
exports.createPublishTransaction = createPublishTransaction;
|
|
6
7
|
exports.createAppendTransaction = createAppendTransaction;
|
|
8
|
+
exports.createAppendTransactionDry = createAppendTransactionDry;
|
|
7
9
|
exports.publishCKBFS = publishCKBFS;
|
|
8
10
|
exports.appendCKBFS = appendCKBFS;
|
|
9
11
|
const core_1 = require("@ckb-ccc/core");
|
|
@@ -17,7 +19,7 @@ const constants_1 = require("./constants");
|
|
|
17
19
|
* @returns A hex prefixed string
|
|
18
20
|
*/
|
|
19
21
|
function ensureHexPrefix(value) {
|
|
20
|
-
if (value.startsWith(
|
|
22
|
+
if (value.startsWith("0x")) {
|
|
21
23
|
return value;
|
|
22
24
|
}
|
|
23
25
|
return `0x${value}`;
|
|
@@ -28,7 +30,7 @@ function ensureHexPrefix(value) {
|
|
|
28
30
|
* @returns The created cell output
|
|
29
31
|
*/
|
|
30
32
|
function createCKBFSCell(options) {
|
|
31
|
-
const { contentType, filename, capacity, lock, network = constants_1.DEFAULT_NETWORK, version = constants_1.DEFAULT_VERSION, useTypeID = false } = options;
|
|
33
|
+
const { contentType, filename, capacity, lock, network = constants_1.DEFAULT_NETWORK, version = constants_1.DEFAULT_VERSION, useTypeID = false, } = options;
|
|
32
34
|
// Get CKBFS script config
|
|
33
35
|
const config = (0, constants_1.getCKBFSScriptConfig)(network, version, useTypeID);
|
|
34
36
|
// Create pre CKBFS type script
|
|
@@ -41,15 +43,14 @@ function createCKBFSCell(options) {
|
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
+
* Prepares a transaction for publishing a file to CKBFS without fee and change handling
|
|
47
|
+
* You will need to manually set the typeID if you did not provide inputs, or just check is return value emptyTypeID is true
|
|
46
48
|
* @param options Options for publishing the file
|
|
47
|
-
* @returns Promise resolving to the
|
|
49
|
+
* @returns Promise resolving to the prepared transaction and the output index of CKBFS Cell
|
|
48
50
|
*/
|
|
49
|
-
async function
|
|
50
|
-
const { contentChunks, contentType, filename, lock, capacity,
|
|
51
|
+
async function preparePublishTransaction(options) {
|
|
52
|
+
const { from, contentChunks, contentType, filename, lock, capacity, network = constants_1.DEFAULT_NETWORK, version = constants_1.DEFAULT_VERSION, useTypeID = false, } = options;
|
|
51
53
|
// Calculate checksum for the combined content
|
|
52
|
-
const textEncoder = new TextEncoder();
|
|
53
54
|
const combinedContent = Buffer.concat(contentChunks);
|
|
54
55
|
const checksum = await (0, checksum_1.calculateChecksum)(combinedContent);
|
|
55
56
|
// Create CKBFS witnesses - each chunk already includes the CKBFS header
|
|
@@ -57,9 +58,7 @@ async function createPublishTransaction(signer, options) {
|
|
|
57
58
|
// not to be confused with the Protocol Version (V1 vs V2)
|
|
58
59
|
const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSWitnesses)(contentChunks);
|
|
59
60
|
// Calculate the actual witness indices where our content is placed
|
|
60
|
-
|
|
61
|
-
// So our CKBFS data starts at index 1
|
|
62
|
-
const contentStartIndex = 1;
|
|
61
|
+
const contentStartIndex = from?.witnesses.length || 1;
|
|
63
62
|
const witnessIndices = Array.from({ length: contentChunks.length }, (_, i) => contentStartIndex + i);
|
|
64
63
|
// Create CKBFS cell output data based on version
|
|
65
64
|
let outputData;
|
|
@@ -88,65 +87,171 @@ async function createPublishTransaction(signer, options) {
|
|
|
88
87
|
// Get CKBFS script config
|
|
89
88
|
const config = (0, constants_1.getCKBFSScriptConfig)(network, version, useTypeID);
|
|
90
89
|
const preCkbfsTypeScript = new core_1.Script(ensureHexPrefix(config.codeHash), config.hashType, "0x0000000000000000000000000000000000000000000000000000000000000000");
|
|
91
|
-
const ckbfsCellSize = BigInt(outputData.length +
|
|
90
|
+
const ckbfsCellSize = BigInt(outputData.length +
|
|
91
|
+
preCkbfsTypeScript.occupiedSize +
|
|
92
|
+
lock.occupiedSize +
|
|
93
|
+
8) * 100000000n;
|
|
92
94
|
// Create pre transaction without cell deps initially
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
95
|
+
let preTx;
|
|
96
|
+
if (from) {
|
|
97
|
+
// If from is not empty, inject/merge the fields
|
|
98
|
+
preTx = core_1.Transaction.from({
|
|
99
|
+
...from,
|
|
100
|
+
outputs: from.outputs.length === 0
|
|
101
|
+
? [
|
|
102
|
+
createCKBFSCell({
|
|
103
|
+
contentType,
|
|
104
|
+
filename,
|
|
105
|
+
lock,
|
|
106
|
+
network,
|
|
107
|
+
version,
|
|
108
|
+
useTypeID,
|
|
109
|
+
capacity: ckbfsCellSize || capacity,
|
|
110
|
+
}),
|
|
111
|
+
]
|
|
112
|
+
: [
|
|
113
|
+
...from.outputs,
|
|
114
|
+
createCKBFSCell({
|
|
115
|
+
contentType,
|
|
116
|
+
filename,
|
|
117
|
+
lock,
|
|
118
|
+
network,
|
|
119
|
+
version,
|
|
120
|
+
useTypeID,
|
|
121
|
+
capacity: ckbfsCellSize || capacity,
|
|
122
|
+
}),
|
|
123
|
+
],
|
|
124
|
+
witnesses: from.witnesses.length === 0
|
|
125
|
+
? [
|
|
126
|
+
[], // Empty secp witness for signing if not provided
|
|
127
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
128
|
+
]
|
|
129
|
+
: [
|
|
130
|
+
...from.witnesses,
|
|
131
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
132
|
+
],
|
|
133
|
+
outputsData: from.outputsData.length === 0
|
|
134
|
+
? [outputData]
|
|
135
|
+
: [
|
|
136
|
+
...from.outputsData,
|
|
137
|
+
outputData,
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
preTx = core_1.Transaction.from({
|
|
143
|
+
outputs: [
|
|
144
|
+
createCKBFSCell({
|
|
145
|
+
contentType,
|
|
146
|
+
filename,
|
|
147
|
+
lock,
|
|
148
|
+
network,
|
|
149
|
+
version,
|
|
150
|
+
useTypeID,
|
|
151
|
+
capacity: ckbfsCellSize || capacity,
|
|
152
|
+
}),
|
|
153
|
+
],
|
|
154
|
+
witnesses: [
|
|
155
|
+
[], // Empty secp witness for signing
|
|
156
|
+
...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`),
|
|
157
|
+
],
|
|
158
|
+
outputsData: [outputData],
|
|
159
|
+
});
|
|
160
|
+
}
|
|
113
161
|
// Add the CKBFS dep group cell dependency
|
|
114
162
|
preTx.addCellDeps({
|
|
115
163
|
outPoint: {
|
|
116
164
|
txHash: ensureHexPrefix(config.depTxHash),
|
|
117
165
|
index: config.depIndex || 0,
|
|
118
166
|
},
|
|
119
|
-
depType: "depGroup"
|
|
167
|
+
depType: "depGroup",
|
|
120
168
|
});
|
|
121
|
-
// Get the recommended address to ensure lock script cell deps are included
|
|
122
|
-
const address = await signer.getRecommendedAddressObj();
|
|
123
|
-
// Complete inputs by capacity
|
|
124
|
-
await preTx.completeInputsByCapacity(signer);
|
|
125
|
-
// Complete fee change to lock
|
|
126
|
-
await preTx.completeFeeChangeToLock(signer, lock, feeRate || 2000);
|
|
127
169
|
// Create type ID args
|
|
128
|
-
const
|
|
170
|
+
const outputIndex = from ? from.outputs.length : 0;
|
|
171
|
+
const args = preTx.inputs.length > 0 ? core_1.ccc.hashTypeId(preTx.inputs[0], outputIndex) : "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
129
172
|
// Create CKBFS type script with type ID
|
|
130
173
|
const ckbfsTypeScript = new core_1.Script(ensureHexPrefix(config.codeHash), config.hashType, args);
|
|
131
174
|
// Create final transaction with same cell deps as preTx
|
|
132
175
|
const tx = core_1.Transaction.from({
|
|
133
176
|
cellDeps: preTx.cellDeps,
|
|
134
|
-
witnesses:
|
|
135
|
-
[], // Reset first witness for signing
|
|
136
|
-
...preTx.witnesses.slice(1)
|
|
137
|
-
],
|
|
177
|
+
witnesses: preTx.witnesses,
|
|
138
178
|
outputsData: preTx.outputsData,
|
|
139
179
|
inputs: preTx.inputs,
|
|
140
|
-
outputs:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
180
|
+
outputs: outputIndex === 0
|
|
181
|
+
? [
|
|
182
|
+
{
|
|
183
|
+
lock,
|
|
184
|
+
type: ckbfsTypeScript,
|
|
185
|
+
capacity: preTx.outputs[outputIndex].capacity,
|
|
186
|
+
},
|
|
187
|
+
]
|
|
188
|
+
: [
|
|
189
|
+
...preTx.outputs.slice(0, outputIndex), // Include rest of outputs (e.g., change)
|
|
190
|
+
{
|
|
191
|
+
lock,
|
|
192
|
+
type: ckbfsTypeScript,
|
|
193
|
+
capacity: preTx.outputs[outputIndex].capacity,
|
|
194
|
+
},
|
|
195
|
+
],
|
|
148
196
|
});
|
|
149
|
-
return tx;
|
|
197
|
+
return { tx, outputIndex, emptyTypeID: args === "0x0000000000000000000000000000000000000000000000000000000000000000" };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Creates a transaction for publishing a file to CKBFS
|
|
201
|
+
* @param signer The signer to use for the transaction
|
|
202
|
+
* @param options Options for publishing the file
|
|
203
|
+
* @returns Promise resolving to the created transaction
|
|
204
|
+
*/
|
|
205
|
+
async function createPublishTransaction(signer, options) {
|
|
206
|
+
const { feeRate, lock, } = options;
|
|
207
|
+
// Use preparePublishTransaction to create the base transaction
|
|
208
|
+
const { tx: preTx, outputIndex, emptyTypeID } = await preparePublishTransaction(options);
|
|
209
|
+
// Complete inputs by capacity
|
|
210
|
+
await preTx.completeInputsByCapacity(signer);
|
|
211
|
+
// Complete fee change to lock
|
|
212
|
+
await preTx.completeFeeChangeToLock(signer, lock, feeRate || 2000);
|
|
213
|
+
// If emptyTypeID is true, we need to create the proper type ID args
|
|
214
|
+
if (emptyTypeID) {
|
|
215
|
+
// Get CKBFS script config
|
|
216
|
+
const config = (0, constants_1.getCKBFSScriptConfig)(options.network || constants_1.DEFAULT_NETWORK, options.version || constants_1.DEFAULT_VERSION, options.useTypeID || false);
|
|
217
|
+
// Create type ID args
|
|
218
|
+
const args = core_1.ccc.hashTypeId(preTx.inputs[0], outputIndex);
|
|
219
|
+
// Create CKBFS type script with type ID
|
|
220
|
+
const ckbfsTypeScript = new core_1.Script(ensureHexPrefix(config.codeHash), config.hashType, args);
|
|
221
|
+
// Create final transaction with updated type script
|
|
222
|
+
const tx = core_1.Transaction.from({
|
|
223
|
+
cellDeps: preTx.cellDeps,
|
|
224
|
+
witnesses: preTx.witnesses,
|
|
225
|
+
outputsData: preTx.outputsData,
|
|
226
|
+
inputs: preTx.inputs,
|
|
227
|
+
outputs: outputIndex === 0
|
|
228
|
+
? [{
|
|
229
|
+
lock,
|
|
230
|
+
type: ckbfsTypeScript,
|
|
231
|
+
capacity: preTx.outputs[outputIndex].capacity,
|
|
232
|
+
}]
|
|
233
|
+
: [
|
|
234
|
+
...preTx.outputs.slice(0, outputIndex), // Include outputs before CKBFS cell
|
|
235
|
+
{
|
|
236
|
+
lock,
|
|
237
|
+
type: ckbfsTypeScript,
|
|
238
|
+
capacity: preTx.outputs[outputIndex].capacity,
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
});
|
|
242
|
+
return tx;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// If typeID was already set properly, just reset the first witness for signing
|
|
246
|
+
const tx = core_1.Transaction.from({
|
|
247
|
+
cellDeps: preTx.cellDeps,
|
|
248
|
+
witnesses: preTx.witnesses,
|
|
249
|
+
outputsData: preTx.outputsData,
|
|
250
|
+
inputs: preTx.inputs,
|
|
251
|
+
outputs: preTx.outputs,
|
|
252
|
+
});
|
|
253
|
+
return tx;
|
|
254
|
+
}
|
|
150
255
|
}
|
|
151
256
|
/**
|
|
152
257
|
* Creates a transaction for appending content to a CKBFS file
|
|
@@ -155,7 +260,7 @@ async function createPublishTransaction(signer, options) {
|
|
|
155
260
|
* @returns Promise resolving to the created transaction
|
|
156
261
|
*/
|
|
157
262
|
async function createAppendTransaction(signer, options) {
|
|
158
|
-
const { ckbfsCell, contentChunks, feeRate, network = constants_1.DEFAULT_NETWORK, version = constants_1.DEFAULT_VERSION } = options;
|
|
263
|
+
const { ckbfsCell, contentChunks, feeRate, network = constants_1.DEFAULT_NETWORK, version = constants_1.DEFAULT_VERSION, } = options;
|
|
159
264
|
const { outPoint, data, type, lock, capacity } = ckbfsCell;
|
|
160
265
|
// Get CKBFS script config early to use version info
|
|
161
266
|
const config = (0, constants_1.getCKBFSScriptConfig)(network, version);
|
|
@@ -183,7 +288,8 @@ async function createAppendTransaction(signer, options) {
|
|
|
183
288
|
newBackLink = {
|
|
184
289
|
// In V1, field order is index, checksum, txHash
|
|
185
290
|
// and index is a single number value, not an array
|
|
186
|
-
index: data.index ||
|
|
291
|
+
index: data.index ||
|
|
292
|
+
(data.indexes && data.indexes.length > 0 ? data.indexes[0] : 0),
|
|
187
293
|
checksum: data.checksum,
|
|
188
294
|
txHash: outPoint.txHash,
|
|
189
295
|
};
|
|
@@ -229,12 +335,13 @@ async function createAppendTransaction(signer, options) {
|
|
|
229
335
|
// Get sizes and calculate capacity requirements
|
|
230
336
|
const newDataSize = outputData.length;
|
|
231
337
|
// Calculate the required capacity for the output cell
|
|
232
|
-
// This accounts for:
|
|
338
|
+
// This accounts for:
|
|
233
339
|
// 1. The output data size
|
|
234
340
|
// 2. The type script's occupied size
|
|
235
341
|
// 3. The lock script's occupied size
|
|
236
342
|
// 4. A constant of 8 bytes (for header overhead)
|
|
237
|
-
const ckbfsCellSize = BigInt(outputData.length + type.occupiedSize + lock.occupiedSize + 8) *
|
|
343
|
+
const ckbfsCellSize = BigInt(outputData.length + type.occupiedSize + lock.occupiedSize + 8) *
|
|
344
|
+
100000000n;
|
|
238
345
|
console.log(`Original capacity: ${capacity}, Calculated size: ${ckbfsCellSize}, Data size: ${outputData.length}`);
|
|
239
346
|
// Use the maximum value between calculated size and original capacity
|
|
240
347
|
// to ensure we have enough capacity but don't decrease capacity unnecessarily
|
|
@@ -248,18 +355,16 @@ async function createAppendTransaction(signer, options) {
|
|
|
248
355
|
index: outPoint.index,
|
|
249
356
|
},
|
|
250
357
|
since: "0x0",
|
|
251
|
-
}
|
|
358
|
+
},
|
|
252
359
|
],
|
|
253
360
|
outputs: [
|
|
254
361
|
{
|
|
255
362
|
lock,
|
|
256
363
|
type,
|
|
257
364
|
capacity: outputCapacity,
|
|
258
|
-
}
|
|
365
|
+
},
|
|
259
366
|
],
|
|
260
|
-
outputsData: [
|
|
261
|
-
outputData,
|
|
262
|
-
]
|
|
367
|
+
outputsData: [outputData],
|
|
263
368
|
});
|
|
264
369
|
// Add the CKBFS dep group cell dependency
|
|
265
370
|
tx.addCellDeps({
|
|
@@ -267,7 +372,7 @@ async function createAppendTransaction(signer, options) {
|
|
|
267
372
|
txHash: ensureHexPrefix(config.depTxHash),
|
|
268
373
|
index: config.depIndex || 0,
|
|
269
374
|
},
|
|
270
|
-
depType: "depGroup"
|
|
375
|
+
depType: "depGroup",
|
|
271
376
|
});
|
|
272
377
|
const inputsBefore = tx.inputs.length;
|
|
273
378
|
// If we need more capacity than the original cell had, add additional inputs
|
|
@@ -279,20 +384,167 @@ async function createAppendTransaction(signer, options) {
|
|
|
279
384
|
const witnesses = [];
|
|
280
385
|
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
281
386
|
if (address.script.hash() === lock.hash()) {
|
|
282
|
-
witnesses.push(
|
|
387
|
+
witnesses.push("0x");
|
|
283
388
|
}
|
|
284
389
|
// add ckbfs witnesses
|
|
285
|
-
witnesses.push(...ckbfsWitnesses.map(w => `0x${Buffer.from(w).toString(
|
|
390
|
+
witnesses.push(...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`));
|
|
286
391
|
// Add empty witnesses for signer's input
|
|
287
392
|
// This is to ensure that the transaction is valid and can be signed
|
|
288
393
|
for (let i = inputsBefore; i < tx.inputs.length; i++) {
|
|
289
|
-
witnesses.push(
|
|
394
|
+
witnesses.push("0x");
|
|
290
395
|
}
|
|
291
396
|
tx.witnesses = witnesses;
|
|
292
397
|
// Complete fee
|
|
293
398
|
await tx.completeFeeChangeToLock(signer, address.script, feeRate || 2000);
|
|
294
399
|
return tx;
|
|
295
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* Creates a transaction for appending content to a CKBFS file
|
|
403
|
+
* @param signer The signer to use for the transaction
|
|
404
|
+
* @param options Options for appending content
|
|
405
|
+
* @returns Promise resolving to the created transaction
|
|
406
|
+
*/
|
|
407
|
+
async function createAppendTransactionDry(signer, options) {
|
|
408
|
+
const { ckbfsCell, contentChunks, network = constants_1.DEFAULT_NETWORK, version = constants_1.DEFAULT_VERSION, } = options;
|
|
409
|
+
const { outPoint, data, type, lock, capacity } = ckbfsCell;
|
|
410
|
+
// Get CKBFS script config early to use version info
|
|
411
|
+
const config = (0, constants_1.getCKBFSScriptConfig)(network, version);
|
|
412
|
+
// Create CKBFS witnesses - each chunk already includes the CKBFS header
|
|
413
|
+
// Pass 0 as version byte - this is the protocol version byte in the witness header
|
|
414
|
+
// not to be confused with the Protocol Version (V1 vs V2)
|
|
415
|
+
const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSWitnesses)(contentChunks);
|
|
416
|
+
// Combine the new content chunks for checksum calculation
|
|
417
|
+
const combinedContent = Buffer.concat(contentChunks);
|
|
418
|
+
// Update the existing checksum with the new content - this matches Adler32's
|
|
419
|
+
// cumulative nature as required by Rule 11 in the RFC
|
|
420
|
+
const contentChecksum = await (0, checksum_1.updateChecksum)(data.checksum, combinedContent);
|
|
421
|
+
console.log(`Updated checksum from ${data.checksum} to ${contentChecksum} for appended content`);
|
|
422
|
+
// Get the recommended address to ensure lock script cell deps are included
|
|
423
|
+
const address = await signer.getRecommendedAddressObj();
|
|
424
|
+
// Calculate the actual witness indices where our content is placed
|
|
425
|
+
// CKBFS data starts at index 1 if signer's lock script is the same as ckbfs's lock script
|
|
426
|
+
// else CKBFS data starts at index 0
|
|
427
|
+
const contentStartIndex = address.script.hash() === lock.hash() ? 1 : 0;
|
|
428
|
+
const witnessIndices = Array.from({ length: contentChunks.length }, (_, i) => contentStartIndex + i);
|
|
429
|
+
// Create backlink for the current state based on version
|
|
430
|
+
let newBackLink;
|
|
431
|
+
if (version === constants_1.ProtocolVersion.V1) {
|
|
432
|
+
// V1 format: Use index field (single number)
|
|
433
|
+
newBackLink = {
|
|
434
|
+
// In V1, field order is index, checksum, txHash
|
|
435
|
+
// and index is a single number value, not an array
|
|
436
|
+
index: data.index ||
|
|
437
|
+
(data.indexes && data.indexes.length > 0 ? data.indexes[0] : 0),
|
|
438
|
+
checksum: data.checksum,
|
|
439
|
+
txHash: outPoint.txHash,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
// V2 format: Use indexes field (array of numbers)
|
|
444
|
+
newBackLink = {
|
|
445
|
+
// In V2, field order is indexes, checksum, txHash
|
|
446
|
+
// and indexes is an array of numbers
|
|
447
|
+
indexes: data.indexes || (data.index ? [data.index] : []),
|
|
448
|
+
checksum: data.checksum,
|
|
449
|
+
txHash: outPoint.txHash,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
// Update backlinks - add the new one to the existing backlinks array
|
|
453
|
+
const backLinks = [...(data.backLinks || []), newBackLink];
|
|
454
|
+
// Define output data based on version
|
|
455
|
+
let outputData;
|
|
456
|
+
if (version === constants_1.ProtocolVersion.V1) {
|
|
457
|
+
// In V1, index is a single number, not an array
|
|
458
|
+
// The first witness index is used (V1 can only reference one witness)
|
|
459
|
+
outputData = molecule_1.CKBFSData.pack({
|
|
460
|
+
index: witnessIndices[0], // Use only the first index as a number
|
|
461
|
+
checksum: contentChecksum,
|
|
462
|
+
contentType: data.contentType,
|
|
463
|
+
filename: data.filename,
|
|
464
|
+
backLinks,
|
|
465
|
+
}, constants_1.ProtocolVersion.V1); // Explicitly use V1 for packing
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
// In V2, indexes is an array of witness indices
|
|
469
|
+
outputData = molecule_1.CKBFSData.pack({
|
|
470
|
+
indexes: witnessIndices,
|
|
471
|
+
checksum: contentChecksum,
|
|
472
|
+
contentType: data.contentType,
|
|
473
|
+
filename: data.filename,
|
|
474
|
+
backLinks,
|
|
475
|
+
}, constants_1.ProtocolVersion.V2); // Explicitly use V2 for packing
|
|
476
|
+
}
|
|
477
|
+
// Pack the original data to get its size - use the appropriate version
|
|
478
|
+
const originalData = molecule_1.CKBFSData.pack(data, version);
|
|
479
|
+
const originalDataSize = originalData.length;
|
|
480
|
+
// Get sizes and calculate capacity requirements
|
|
481
|
+
const newDataSize = outputData.length;
|
|
482
|
+
// Calculate the required capacity for the output cell
|
|
483
|
+
// This accounts for:
|
|
484
|
+
// 1. The output data size
|
|
485
|
+
// 2. The type script's occupied size
|
|
486
|
+
// 3. The lock script's occupied size
|
|
487
|
+
// 4. A constant of 8 bytes (for header overhead)
|
|
488
|
+
const ckbfsCellSize = BigInt(outputData.length + type.occupiedSize + lock.occupiedSize + 8) *
|
|
489
|
+
100000000n;
|
|
490
|
+
console.log(`Original capacity: ${capacity}, Calculated size: ${ckbfsCellSize}, Data size: ${outputData.length}`);
|
|
491
|
+
// Use the maximum value between calculated size and original capacity
|
|
492
|
+
// to ensure we have enough capacity but don't decrease capacity unnecessarily
|
|
493
|
+
const outputCapacity = ckbfsCellSize > capacity ? ckbfsCellSize : capacity;
|
|
494
|
+
// Create initial transaction with the CKBFS cell input
|
|
495
|
+
const tx = core_1.Transaction.from({
|
|
496
|
+
inputs: [
|
|
497
|
+
{
|
|
498
|
+
previousOutput: {
|
|
499
|
+
txHash: outPoint.txHash,
|
|
500
|
+
index: outPoint.index,
|
|
501
|
+
},
|
|
502
|
+
since: "0x0",
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
outputs: [
|
|
506
|
+
{
|
|
507
|
+
lock,
|
|
508
|
+
type,
|
|
509
|
+
capacity: outputCapacity,
|
|
510
|
+
},
|
|
511
|
+
],
|
|
512
|
+
outputsData: [outputData],
|
|
513
|
+
});
|
|
514
|
+
// Add the CKBFS dep group cell dependency
|
|
515
|
+
tx.addCellDeps({
|
|
516
|
+
outPoint: {
|
|
517
|
+
txHash: ensureHexPrefix(config.depTxHash),
|
|
518
|
+
index: config.depIndex || 0,
|
|
519
|
+
},
|
|
520
|
+
depType: "depGroup",
|
|
521
|
+
});
|
|
522
|
+
const inputsBefore = tx.inputs.length;
|
|
523
|
+
// // If we need more capacity than the original cell had, add additional inputs
|
|
524
|
+
// if (outputCapacity > capacity) {
|
|
525
|
+
// console.log(
|
|
526
|
+
// `Need additional capacity: ${outputCapacity - capacity} shannons`,
|
|
527
|
+
// );
|
|
528
|
+
// // Add more inputs to cover the increased capacity
|
|
529
|
+
// await tx.completeInputsByCapacity(signer);
|
|
530
|
+
// }
|
|
531
|
+
const witnesses = [];
|
|
532
|
+
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
533
|
+
if (address.script.hash() === lock.hash()) {
|
|
534
|
+
witnesses.push("0x");
|
|
535
|
+
}
|
|
536
|
+
// add ckbfs witnesses
|
|
537
|
+
witnesses.push(...ckbfsWitnesses.map((w) => `0x${Buffer.from(w).toString("hex")}`));
|
|
538
|
+
// Add empty witnesses for signer's input
|
|
539
|
+
// This is to ensure that the transaction is valid and can be signed
|
|
540
|
+
for (let i = inputsBefore; i < tx.inputs.length; i++) {
|
|
541
|
+
witnesses.push("0x");
|
|
542
|
+
}
|
|
543
|
+
tx.witnesses = witnesses;
|
|
544
|
+
// Complete fee
|
|
545
|
+
//await tx.completeFeeChangeToLock(signer, address.script, feeRate || 2000);
|
|
546
|
+
return tx;
|
|
547
|
+
}
|
|
296
548
|
/**
|
|
297
549
|
* Creates a complete transaction for publishing a file to CKBFS
|
|
298
550
|
* @param signer The signer to use for the transaction
|
package/examples/append.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CKBFS, NetworkType, ProtocolVersion, CKBFSDataType, extractCKBFSWitnessContent, isCKBFSWitness, CKBFSData
|
|
1
|
+
import { CKBFS, NetworkType, ProtocolVersion, CKBFSDataType, extractCKBFSWitnessContent, isCKBFSWitness, CKBFSData } from '../src/index';
|
|
2
2
|
import { Script, ClientPublicTestnet, Transaction, ccc } from "@ckb-ccc/core";
|
|
3
3
|
|
|
4
4
|
// Replace with your actual private key
|
|
@@ -169,37 +169,6 @@ async function appendExample() {
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
/**
|
|
173
|
-
* Example of appending content directly (string) to an existing CKBFS file
|
|
174
|
-
* @param previousAppendTxHash The transaction hash from the previous append operation
|
|
175
|
-
*/
|
|
176
|
-
async function appendContentExample(previousAppendTxHash: string) {
|
|
177
|
-
try {
|
|
178
|
-
console.log(`Getting cell info from previous append transaction: ${previousAppendTxHash}`);
|
|
179
|
-
const ckbfsCell = await getCellInfoFromTransaction(previousAppendTxHash);
|
|
180
|
-
|
|
181
|
-
const contentToAppend = "\nAnd this is more content appended directly as a string!";
|
|
182
|
-
const options: AppendContentOptions = {
|
|
183
|
-
// You can optionally specify feeRate, network, version
|
|
184
|
-
// feeRate: 3000
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
console.log(`Appending direct content: "${contentToAppend}"`);
|
|
188
|
-
|
|
189
|
-
// Append the string content
|
|
190
|
-
const txHash = await ckbfs.appendContent(contentToAppend, ckbfsCell, options);
|
|
191
|
-
|
|
192
|
-
console.log(`Direct content appended successfully!`);
|
|
193
|
-
console.log(`Transaction Hash: ${txHash}`);
|
|
194
|
-
console.log(`View at: https://pudge.explorer.nervos.org/transaction/${txHash}`);
|
|
195
|
-
|
|
196
|
-
return txHash;
|
|
197
|
-
} catch (error) {
|
|
198
|
-
console.error('Error appending direct content:', error);
|
|
199
|
-
throw error;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
172
|
/**
|
|
204
173
|
* Main function to run the example
|
|
205
174
|
*/
|
|
@@ -209,13 +178,7 @@ async function main() {
|
|
|
209
178
|
console.log(`Using CKBFS protocol version: ${ProtocolVersion.V2}`);
|
|
210
179
|
|
|
211
180
|
try {
|
|
212
|
-
|
|
213
|
-
const firstAppendTxHash = await appendExample();
|
|
214
|
-
console.log('-------------------------------');
|
|
215
|
-
|
|
216
|
-
// Now run the content append, using the output from the first append
|
|
217
|
-
await appendContentExample(firstAppendTxHash);
|
|
218
|
-
|
|
181
|
+
await appendExample();
|
|
219
182
|
console.log('Example completed successfully!');
|
|
220
183
|
process.exit(0);
|
|
221
184
|
} catch (error) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Hello CKBFS!
|