@ckbfs/api 1.2.0 → 1.2.3

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/ABC.LOGS ADDED
@@ -0,0 +1 @@
1
+ MINT,TO:ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdpallzwnhe64rqnqmev7hf98yrmh4yzucgdw7qwGIVE_NAME,NEW_NAME:NERVAPE_COOKIE
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Script, Signer, Transaction } from "@ckb-ccc/core";
2
2
  import { calculateChecksum, verifyChecksum, updateChecksum, verifyWitnessChecksum } from './utils/checksum';
3
3
  import { createCKBFSCell, createPublishTransaction, createAppendTransaction, publishCKBFS, appendCKBFS, CKBFSCellOptions, PublishOptions, AppendOptions } from './utils/transaction';
4
- import { readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile } from './utils/file';
4
+ import { readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile, getFileContentFromChain, saveFileFromChain } from './utils/file';
5
5
  import { createCKBFSWitness, createTextCKBFSWitness, extractCKBFSWitnessContent, isCKBFSWitness, createChunkedCKBFSWitnesses } from './utils/witness';
6
6
  import { CKBFSData, BackLinkV1, BackLinkV2, CKBFSDataType, BackLinkType, CKBFS_HEADER, CKBFS_HEADER_STRING } from './utils/molecule';
7
7
  import { NetworkType, ProtocolVersion, DEFAULT_NETWORK, DEFAULT_VERSION, CKBFS_CODE_HASH, CKBFS_TYPE_ID, ADLER32_CODE_HASH, ADLER32_TYPE_ID, DEP_GROUP_TX_HASH, DEPLOY_TX_HASH, getCKBFSScriptConfig, CKBFSScriptConfig } from './utils/constants';
@@ -88,4 +88,4 @@ export declare class CKBFS {
88
88
  */
89
89
  createAppendTransaction(filePath: string, ckbfsCell: AppendOptions['ckbfsCell'], options?: Omit<FileOptions, 'contentType' | 'filename'>): Promise<Transaction>;
90
90
  }
91
- export { calculateChecksum, verifyChecksum, updateChecksum, verifyWitnessChecksum, createCKBFSCell, createPublishTransaction, createAppendTransaction, publishCKBFS, appendCKBFS, readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile, createCKBFSWitness, createTextCKBFSWitness, extractCKBFSWitnessContent, isCKBFSWitness, createChunkedCKBFSWitnesses, CKBFSData, BackLinkV1, BackLinkV2, CKBFSDataType, BackLinkType, CKBFSCellOptions, PublishOptions, AppendOptions, CKBFS_HEADER, CKBFS_HEADER_STRING, NetworkType, ProtocolVersion, DEFAULT_NETWORK, DEFAULT_VERSION, CKBFS_CODE_HASH, CKBFS_TYPE_ID, ADLER32_CODE_HASH, ADLER32_TYPE_ID, DEP_GROUP_TX_HASH, DEPLOY_TX_HASH, getCKBFSScriptConfig, CKBFSScriptConfig };
91
+ export { calculateChecksum, verifyChecksum, updateChecksum, verifyWitnessChecksum, createCKBFSCell, createPublishTransaction, createAppendTransaction, publishCKBFS, appendCKBFS, readFile, readFileAsText, readFileAsUint8Array, writeFile, getContentType, splitFileIntoChunks, combineChunksToFile, getFileContentFromChain, saveFileFromChain, createCKBFSWitness, createTextCKBFSWitness, extractCKBFSWitnessContent, isCKBFSWitness, createChunkedCKBFSWitnesses, CKBFSData, BackLinkV1, BackLinkV2, CKBFSDataType, BackLinkType, CKBFSCellOptions, PublishOptions, AppendOptions, CKBFS_HEADER, CKBFS_HEADER_STRING, NetworkType, ProtocolVersion, DEFAULT_NETWORK, DEFAULT_VERSION, CKBFS_CODE_HASH, CKBFS_TYPE_ID, ADLER32_CODE_HASH, ADLER32_TYPE_ID, DEP_GROUP_TX_HASH, DEPLOY_TX_HASH, getCKBFSScriptConfig, CKBFSScriptConfig };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCKBFSScriptConfig = exports.DEPLOY_TX_HASH = exports.DEP_GROUP_TX_HASH = exports.ADLER32_TYPE_ID = exports.ADLER32_CODE_HASH = exports.CKBFS_TYPE_ID = exports.CKBFS_CODE_HASH = exports.DEFAULT_VERSION = exports.DEFAULT_NETWORK = exports.ProtocolVersion = exports.NetworkType = exports.CKBFS_HEADER_STRING = exports.CKBFS_HEADER = exports.BackLinkV2 = exports.BackLinkV1 = exports.CKBFSData = exports.createChunkedCKBFSWitnesses = exports.isCKBFSWitness = exports.extractCKBFSWitnessContent = exports.createTextCKBFSWitness = exports.createCKBFSWitness = exports.combineChunksToFile = exports.splitFileIntoChunks = exports.getContentType = exports.writeFile = exports.readFileAsUint8Array = exports.readFileAsText = exports.readFile = exports.appendCKBFS = exports.publishCKBFS = exports.createAppendTransaction = exports.createPublishTransaction = exports.createCKBFSCell = exports.verifyWitnessChecksum = exports.updateChecksum = exports.verifyChecksum = exports.calculateChecksum = exports.CKBFS = void 0;
3
+ exports.getCKBFSScriptConfig = exports.DEPLOY_TX_HASH = exports.DEP_GROUP_TX_HASH = exports.ADLER32_TYPE_ID = exports.ADLER32_CODE_HASH = exports.CKBFS_TYPE_ID = exports.CKBFS_CODE_HASH = exports.DEFAULT_VERSION = exports.DEFAULT_NETWORK = exports.ProtocolVersion = exports.NetworkType = exports.CKBFS_HEADER_STRING = exports.CKBFS_HEADER = exports.BackLinkV2 = exports.BackLinkV1 = exports.CKBFSData = exports.createChunkedCKBFSWitnesses = exports.isCKBFSWitness = exports.extractCKBFSWitnessContent = exports.createTextCKBFSWitness = exports.createCKBFSWitness = exports.saveFileFromChain = exports.getFileContentFromChain = exports.combineChunksToFile = exports.splitFileIntoChunks = exports.getContentType = exports.writeFile = exports.readFileAsUint8Array = exports.readFileAsText = exports.readFile = exports.appendCKBFS = exports.publishCKBFS = exports.createAppendTransaction = exports.createPublishTransaction = exports.createCKBFSCell = exports.verifyWitnessChecksum = exports.updateChecksum = exports.verifyChecksum = exports.calculateChecksum = exports.CKBFS = void 0;
4
4
  const core_1 = require("@ckb-ccc/core");
5
5
  const checksum_1 = require("./utils/checksum");
6
6
  Object.defineProperty(exports, "calculateChecksum", { enumerable: true, get: function () { return checksum_1.calculateChecksum; } });
@@ -21,6 +21,8 @@ Object.defineProperty(exports, "writeFile", { enumerable: true, get: function ()
21
21
  Object.defineProperty(exports, "getContentType", { enumerable: true, get: function () { return file_1.getContentType; } });
22
22
  Object.defineProperty(exports, "splitFileIntoChunks", { enumerable: true, get: function () { return file_1.splitFileIntoChunks; } });
23
23
  Object.defineProperty(exports, "combineChunksToFile", { enumerable: true, get: function () { return file_1.combineChunksToFile; } });
24
+ Object.defineProperty(exports, "getFileContentFromChain", { enumerable: true, get: function () { return file_1.getFileContentFromChain; } });
25
+ Object.defineProperty(exports, "saveFileFromChain", { enumerable: true, get: function () { return file_1.saveFileFromChain; } });
24
26
  const witness_1 = require("./utils/witness");
25
27
  Object.defineProperty(exports, "createCKBFSWitness", { enumerable: true, get: function () { return witness_1.createCKBFSWitness; } });
26
28
  Object.defineProperty(exports, "createTextCKBFSWitness", { enumerable: true, get: function () { return witness_1.createTextCKBFSWitness; } });
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export declare function calculateChecksum(data: Uint8Array): Promise<number>;
10
10
  /**
11
- * Updates an existing checksum with new data
11
+ * Updates an existing checksum with new data using proper rolling Adler-32 calculation
12
12
  * @param previousChecksum The existing checksum to update
13
13
  * @param newData The new data to add to the checksum
14
14
  * @returns Promise resolving to the updated checksum as a number
@@ -19,21 +19,38 @@ async function calculateChecksum(data) {
19
19
  return checksumBuffer.readUInt32BE();
20
20
  }
21
21
  /**
22
- * Updates an existing checksum with new data
22
+ * Updates an existing checksum with new data using proper rolling Adler-32 calculation
23
23
  * @param previousChecksum The existing checksum to update
24
24
  * @param newData The new data to add to the checksum
25
25
  * @returns Promise resolving to the updated checksum as a number
26
26
  */
27
27
  async function updateChecksum(previousChecksum, newData) {
28
- // In a real implementation, this would require the actual Adler32 state recovery
29
- // For now, we're simply concatenating the previousChecksum as a hex string with the new data
30
- // and calculating a new checksum
31
- const checksumBytes = Buffer.alloc(4);
32
- checksumBytes.writeUInt32BE(previousChecksum);
33
- // Concatenate the previous checksum bytes with the new data
34
- const combinedData = Buffer.concat([checksumBytes, Buffer.from(newData)]);
35
- // Calculate the new checksum
36
- return calculateChecksum(combinedData);
28
+ // Extract a and b values from the previous checksum
29
+ // In Adler-32, the checksum is composed of two 16-bit integers: a and b
30
+ // The final checksum is (b << 16) | a
31
+ const a = previousChecksum & 0xFFFF;
32
+ const b = (previousChecksum >>> 16) & 0xFFFF;
33
+ // Use the adler-32 package to calculate a proper rolling checksum
34
+ // The package doesn't have a "resume" function, so we need to work with the underlying algorithm
35
+ // Initialize with existing a and b values
36
+ let adlerA = a;
37
+ let adlerB = b;
38
+ const MOD_ADLER = 65521; // Adler-32 modulo value
39
+ // Process each byte of the new data
40
+ for (let i = 0; i < newData.length; i++) {
41
+ adlerA = (adlerA + newData[i]) % MOD_ADLER;
42
+ adlerB = (adlerB + adlerA) % MOD_ADLER;
43
+ }
44
+ // Combine a and b to get the final checksum
45
+ // Use a Uint32Array to ensure we get a proper unsigned 32-bit integer
46
+ const buffer = new ArrayBuffer(4);
47
+ const view = new DataView(buffer);
48
+ view.setUint16(0, adlerA, true); // Set lower 16 bits (little endian)
49
+ view.setUint16(2, adlerB, true); // Set upper 16 bits (little endian)
50
+ // Read as an unsigned 32-bit integer
51
+ const updatedChecksum = view.getUint32(0, true);
52
+ console.log(`Updated checksum from ${previousChecksum} to ${updatedChecksum} for appended content`);
53
+ return updatedChecksum;
37
54
  }
38
55
  /**
39
56
  * Verifies if a given checksum matches the expected checksum for the data
@@ -44,3 +44,22 @@ export declare function splitFileIntoChunks(filePath: string, chunkSize: number)
44
44
  * @param outputPath The path to write the combined file to
45
45
  */
46
46
  export declare function combineChunksToFile(chunks: Uint8Array[], outputPath: string): void;
47
+ /**
48
+ * Retrieves complete file content from the blockchain by following backlinks
49
+ * @param client The CKB client to use for blockchain queries
50
+ * @param outPoint The output point of the latest CKBFS cell
51
+ * @param ckbfsData The data from the latest CKBFS cell
52
+ * @returns Promise resolving to the complete file content
53
+ */
54
+ export declare function getFileContentFromChain(client: any, outPoint: {
55
+ txHash: string;
56
+ index: number;
57
+ }, ckbfsData: any): Promise<Uint8Array>;
58
+ /**
59
+ * Saves file content retrieved from blockchain to disk
60
+ * @param content The file content to save
61
+ * @param ckbfsData The CKBFS cell data containing file metadata
62
+ * @param outputPath Optional path to save the file (defaults to filename in current directory)
63
+ * @returns The path where the file was saved
64
+ */
65
+ export declare function saveFileFromChain(content: Uint8Array, ckbfsData: any, outputPath?: string): string;
@@ -10,6 +10,8 @@ exports.writeFile = writeFile;
10
10
  exports.getContentType = getContentType;
11
11
  exports.splitFileIntoChunks = splitFileIntoChunks;
12
12
  exports.combineChunksToFile = combineChunksToFile;
13
+ exports.getFileContentFromChain = getFileContentFromChain;
14
+ exports.saveFileFromChain = saveFileFromChain;
13
15
  const fs_1 = __importDefault(require("fs"));
14
16
  const path_1 = __importDefault(require("path"));
15
17
  /**
@@ -106,3 +108,126 @@ function combineChunksToFile(chunks, outputPath) {
106
108
  const combinedBuffer = Buffer.concat(chunks.map(chunk => Buffer.from(chunk)));
107
109
  writeFile(outputPath, combinedBuffer);
108
110
  }
111
+ /**
112
+ * Utility function to safely decode buffer to string
113
+ * @param buffer The buffer to decode
114
+ * @returns Decoded string or placeholder on error
115
+ */
116
+ function safelyDecode(buffer) {
117
+ if (!buffer)
118
+ return '[Unknown]';
119
+ try {
120
+ if (buffer instanceof Uint8Array) {
121
+ return new TextDecoder().decode(buffer);
122
+ }
123
+ else if (typeof buffer === 'string') {
124
+ return buffer;
125
+ }
126
+ else {
127
+ return `[Buffer: ${buffer.toString()}]`;
128
+ }
129
+ }
130
+ catch (e) {
131
+ return '[Decode Error]';
132
+ }
133
+ }
134
+ /**
135
+ * Retrieves complete file content from the blockchain by following backlinks
136
+ * @param client The CKB client to use for blockchain queries
137
+ * @param outPoint The output point of the latest CKBFS cell
138
+ * @param ckbfsData The data from the latest CKBFS cell
139
+ * @returns Promise resolving to the complete file content
140
+ */
141
+ async function getFileContentFromChain(client, outPoint, ckbfsData) {
142
+ console.log(`Retrieving file: ${safelyDecode(ckbfsData.filename)}`);
143
+ console.log(`Content type: ${safelyDecode(ckbfsData.contentType)}`);
144
+ // Prepare to collect all content pieces
145
+ const contentPieces = [];
146
+ let currentData = ckbfsData;
147
+ let currentOutPoint = outPoint;
148
+ // Process the current transaction first
149
+ const tx = await client.getTransaction(currentOutPoint.txHash);
150
+ if (!tx || !tx.transaction) {
151
+ throw new Error(`Transaction ${currentOutPoint.txHash} not found`);
152
+ }
153
+ // Get content from witnesses
154
+ const indexes = currentData.indexes || (currentData.index !== undefined ? [currentData.index] : []);
155
+ if (indexes.length > 0) {
156
+ // Get content from each witness index
157
+ for (const idx of indexes) {
158
+ if (idx >= tx.transaction.witnesses.length) {
159
+ console.warn(`Witness index ${idx} out of range`);
160
+ continue;
161
+ }
162
+ const witnessHex = tx.transaction.witnesses[idx];
163
+ const witness = Buffer.from(witnessHex.slice(2), 'hex'); // Remove 0x prefix
164
+ // Extract content (skip CKBFS header + version byte)
165
+ if (witness.length >= 6 && witness.slice(0, 5).toString() === 'CKBFS') {
166
+ const content = witness.slice(6);
167
+ contentPieces.unshift(content); // Add to beginning of array (we're going backwards)
168
+ }
169
+ else {
170
+ console.warn(`Witness at index ${idx} is not a valid CKBFS witness`);
171
+ }
172
+ }
173
+ }
174
+ // Follow backlinks recursively
175
+ if (currentData.backLinks && currentData.backLinks.length > 0) {
176
+ // Process each backlink, from most recent to oldest
177
+ for (let i = currentData.backLinks.length - 1; i >= 0; i--) {
178
+ const backlink = currentData.backLinks[i];
179
+ // Get the transaction for this backlink
180
+ const backTx = await client.getTransaction(backlink.txHash);
181
+ if (!backTx || !backTx.transaction) {
182
+ console.warn(`Backlink transaction ${backlink.txHash} not found`);
183
+ continue;
184
+ }
185
+ // Get content from backlink witnesses
186
+ const backIndexes = backlink.indexes || (backlink.index !== undefined ? [backlink.index] : []);
187
+ if (backIndexes.length > 0) {
188
+ // Get content from each witness index
189
+ for (const idx of backIndexes) {
190
+ if (idx >= backTx.transaction.witnesses.length) {
191
+ console.warn(`Backlink witness index ${idx} out of range`);
192
+ continue;
193
+ }
194
+ const witnessHex = backTx.transaction.witnesses[idx];
195
+ const witness = Buffer.from(witnessHex.slice(2), 'hex'); // Remove 0x prefix
196
+ // Extract content (skip CKBFS header + version byte)
197
+ if (witness.length >= 6 && witness.slice(0, 5).toString() === 'CKBFS') {
198
+ const content = witness.slice(6);
199
+ contentPieces.unshift(content); // Add to beginning of array (we're going backwards)
200
+ }
201
+ else {
202
+ console.warn(`Backlink witness at index ${idx} is not a valid CKBFS witness`);
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+ // Combine all content pieces
209
+ return Buffer.concat(contentPieces);
210
+ }
211
+ /**
212
+ * Saves file content retrieved from blockchain to disk
213
+ * @param content The file content to save
214
+ * @param ckbfsData The CKBFS cell data containing file metadata
215
+ * @param outputPath Optional path to save the file (defaults to filename in current directory)
216
+ * @returns The path where the file was saved
217
+ */
218
+ function saveFileFromChain(content, ckbfsData, outputPath) {
219
+ // Get filename from CKBFS data
220
+ const filename = safelyDecode(ckbfsData.filename);
221
+ // Determine output path
222
+ const filePath = outputPath || filename;
223
+ // Ensure directory exists
224
+ const directory = path_1.default.dirname(filePath);
225
+ if (!fs_1.default.existsSync(directory)) {
226
+ fs_1.default.mkdirSync(directory, { recursive: true });
227
+ }
228
+ // Write file
229
+ fs_1.default.writeFileSync(filePath, content);
230
+ console.log(`File saved to: ${filePath}`);
231
+ console.log(`Size: ${content.length} bytes`);
232
+ return filePath;
233
+ }
@@ -4,34 +4,34 @@ import { molecule, number } from "@ckb-lumos/codec";
4
4
  */
5
5
  export declare const Indexes: molecule.ArrayLayoutCodec<import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>>;
6
6
  export declare const BackLinkV1: molecule.ObjectLayoutCodec<{
7
- txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
8
7
  index: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
9
8
  checksum: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
9
+ txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
10
10
  }>;
11
11
  export declare const BackLinkV2: molecule.ObjectLayoutCodec<{
12
- txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
13
12
  indexes: molecule.ArrayLayoutCodec<import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>>;
14
13
  checksum: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
14
+ txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
15
15
  }>;
16
16
  export declare const BackLinksV1: molecule.ArrayLayoutCodec<molecule.ObjectLayoutCodec<{
17
- txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
18
17
  index: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
19
18
  checksum: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
19
+ txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
20
20
  }>>;
21
21
  export declare const BackLinksV2: molecule.ArrayLayoutCodec<molecule.ObjectLayoutCodec<{
22
- txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
23
22
  indexes: molecule.ArrayLayoutCodec<import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>>;
24
23
  checksum: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
24
+ txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
25
25
  }>>;
26
26
  export declare const CKBFSDataV1: molecule.ObjectLayoutCodec<{
27
- index: molecule.ArrayLayoutCodec<import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>>;
27
+ index: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
28
28
  checksum: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
29
29
  contentType: import("@ckb-lumos/codec/lib/base").BytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
30
30
  filename: import("@ckb-lumos/codec/lib/base").BytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
31
31
  backLinks: molecule.ArrayLayoutCodec<molecule.ObjectLayoutCodec<{
32
- txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
33
32
  index: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
34
33
  checksum: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
34
+ txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
35
35
  }>>;
36
36
  }>;
37
37
  export declare const CKBFSDataV2: molecule.ObjectLayoutCodec<{
@@ -40,33 +40,33 @@ export declare const CKBFSDataV2: molecule.ObjectLayoutCodec<{
40
40
  contentType: import("@ckb-lumos/codec/lib/base").BytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
41
41
  filename: import("@ckb-lumos/codec/lib/base").BytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
42
42
  backLinks: molecule.ArrayLayoutCodec<molecule.ObjectLayoutCodec<{
43
- txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
44
43
  indexes: molecule.ArrayLayoutCodec<import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>>;
45
44
  checksum: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<number, number.BIish>;
45
+ txHash: import("@ckb-lumos/codec/lib/base").FixedBytesCodec<string, import("@ckb-lumos/codec").BytesLike>;
46
46
  }>>;
47
47
  }>;
48
48
  export type BackLinkTypeV1 = {
49
- txHash: string;
50
49
  index: number;
51
50
  checksum: number;
51
+ txHash: string;
52
52
  };
53
53
  export type BackLinkTypeV2 = {
54
- txHash: string;
55
54
  indexes: number[];
56
55
  checksum: number;
56
+ txHash: string;
57
57
  };
58
58
  export type BackLinkType = {
59
- txHash: string;
60
59
  index?: number;
61
60
  indexes?: number[];
62
61
  checksum: number;
62
+ txHash: string;
63
63
  };
64
64
  export type CKBFSDataType = {
65
- index?: number[] | number;
65
+ index?: number;
66
66
  indexes?: number[];
67
67
  checksum: number;
68
- contentType: Uint8Array;
69
- filename: Uint8Array;
68
+ contentType: string;
69
+ filename: string;
70
70
  backLinks: BackLinkType[];
71
71
  };
72
72
  export declare const CKBFSData: {
@@ -4,36 +4,36 @@ exports.CKBFS_HEADER_STRING = exports.CKBFS_HEADER = exports.CKBFSData = exports
4
4
  const codec_1 = require("@ckb-lumos/codec");
5
5
  const base_1 = require("@ckb-lumos/base");
6
6
  const constants_1 = require("./constants");
7
+ const core_1 = require("@ckb-ccc/core");
7
8
  /**
8
9
  * Molecule definitions for CKBFS data structures.
9
10
  */
10
- // Define the Indexes vector
11
+ // Define the Indexes vector for V2
11
12
  exports.Indexes = codec_1.molecule.vector(codec_1.number.Uint32);
12
- // Define the BackLink table structure for V1
13
+ // V1: BackLink has index as Uint32, and fields are ordered differently
13
14
  exports.BackLinkV1 = codec_1.molecule.table({
14
- txHash: base_1.blockchain.Byte32,
15
15
  index: codec_1.number.Uint32,
16
16
  checksum: codec_1.number.Uint32,
17
- }, ["txHash", "index", "checksum"]);
18
- // Define the BackLink table structure for V2
19
- exports.BackLinkV2 = codec_1.molecule.table({
20
17
  txHash: base_1.blockchain.Byte32,
18
+ }, ["index", "checksum", "txHash"]);
19
+ // V2: BackLink has indexes as vector of Uint32
20
+ exports.BackLinkV2 = codec_1.molecule.table({
21
21
  indexes: exports.Indexes,
22
22
  checksum: codec_1.number.Uint32,
23
- }, ["txHash", "indexes", "checksum"]);
24
- // Define the BackLinks vector for V1
23
+ txHash: base_1.blockchain.Byte32,
24
+ }, ["indexes", "checksum", "txHash"]);
25
+ // Define the BackLinks vector for V1 and V2
25
26
  exports.BackLinksV1 = codec_1.molecule.vector(exports.BackLinkV1);
26
- // Define the BackLinks vector for V2
27
27
  exports.BackLinksV2 = codec_1.molecule.vector(exports.BackLinkV2);
28
- // Define the CKBFSData table structure for V1
28
+ // V1: CKBFSData has index as optional Uint32
29
29
  exports.CKBFSDataV1 = codec_1.molecule.table({
30
- index: exports.Indexes,
30
+ index: codec_1.number.Uint32,
31
31
  checksum: codec_1.number.Uint32,
32
32
  contentType: base_1.blockchain.Bytes,
33
33
  filename: base_1.blockchain.Bytes,
34
34
  backLinks: exports.BackLinksV1,
35
35
  }, ["index", "checksum", "contentType", "filename", "backLinks"]);
36
- // Define the CKBFSData table structure for V2
36
+ // V2: CKBFSData has indexes as vector of Uint32
37
37
  exports.CKBFSDataV2 = codec_1.molecule.table({
38
38
  indexes: exports.Indexes,
39
39
  checksum: codec_1.number.Uint32,
@@ -41,20 +41,23 @@ exports.CKBFSDataV2 = codec_1.molecule.table({
41
41
  filename: base_1.blockchain.Bytes,
42
42
  backLinks: exports.BackLinksV2,
43
43
  }, ["indexes", "checksum", "contentType", "filename", "backLinks"]);
44
- // Helper function to safely get either index or indexes as array
44
+ // Helper function to get indexes array from data
45
45
  function getIndexes(data) {
46
- if (Array.isArray(data.indexes)) {
46
+ if (data.indexes)
47
47
  return data.indexes;
48
- }
49
- if (Array.isArray(data.index)) {
50
- return data.index;
51
- }
52
- if (typeof data.index === 'number') {
48
+ if (typeof data.index === 'number')
53
49
  return [data.index];
54
- }
55
50
  return [];
56
51
  }
57
- // Helper function to safely get either index or indexes from BackLinkType
52
+ // Helper function to get single index from data
53
+ function getIndex(data) {
54
+ if (typeof data.index === 'number')
55
+ return data.index;
56
+ if (data.indexes && data.indexes.length > 0)
57
+ return data.indexes[0];
58
+ return 0;
59
+ }
60
+ // Helper function to safely get either index or indexes from BackLinkType for V1
58
61
  function getBackLinkIndex(bl) {
59
62
  if (typeof bl.index === 'number') {
60
63
  return bl.index;
@@ -64,7 +67,7 @@ function getBackLinkIndex(bl) {
64
67
  }
65
68
  return 0;
66
69
  }
67
- // Helper function to safely get indexes array from BackLinkType
70
+ // Helper function to safely get indexes array from BackLinkType for V2
68
71
  function getBackLinkIndexes(bl) {
69
72
  if (Array.isArray(bl.indexes)) {
70
73
  return bl.indexes;
@@ -78,78 +81,43 @@ function getBackLinkIndexes(bl) {
78
81
  exports.CKBFSData = {
79
82
  pack: (data, version = constants_1.ProtocolVersion.V2) => {
80
83
  if (version === constants_1.ProtocolVersion.V1) {
81
- // For V1, we need a single number index
82
- let indexValue = [];
83
- // Handle the various ways index might be specified
84
- if (typeof data.index === 'number') {
85
- // Single number
86
- indexValue = [data.index];
87
- }
88
- else if (Array.isArray(data.index) && data.index.length > 0) {
89
- // Array of numbers, use as is
90
- indexValue = data.index;
91
- }
92
- else if (Array.isArray(data.indexes) && data.indexes.length > 0) {
93
- // Try using indexes field if index is not available
94
- indexValue = data.indexes;
95
- }
96
- // Map backlinks to V1 format - ensure each one has a single index
97
- const v1Backlinks = data.backLinks.map(bl => {
98
- let singleIndex = 0;
99
- if (typeof bl.index === 'number') {
100
- singleIndex = bl.index;
101
- }
102
- else if (Array.isArray(bl.indexes) && bl.indexes.length > 0) {
103
- singleIndex = bl.indexes[0];
104
- }
105
- return {
106
- txHash: bl.txHash,
107
- index: singleIndex,
108
- checksum: bl.checksum
109
- };
110
- });
84
+ // V1 formatting - uses single index
111
85
  return exports.CKBFSDataV1.pack({
112
- index: indexValue,
86
+ index: getIndex(data),
113
87
  checksum: data.checksum,
114
- contentType: data.contentType,
115
- filename: data.filename,
116
- backLinks: v1Backlinks
88
+ contentType: core_1.ccc.bytesFrom(data.contentType, 'utf8'),
89
+ filename: core_1.ccc.bytesFrom(data.filename, 'utf8'),
90
+ backLinks: data.backLinks.map(bl => {
91
+ // Ensure txHash is in proper format for molecule encoding
92
+ const txHash = typeof bl.txHash === 'string'
93
+ ? core_1.ccc.bytesFrom(bl.txHash)
94
+ : bl.txHash;
95
+ return {
96
+ index: getBackLinkIndex(bl),
97
+ checksum: bl.checksum,
98
+ txHash,
99
+ };
100
+ }),
117
101
  });
118
102
  }
119
103
  else {
120
- // V2 format - use indexes as an array
121
- let indexesArray = [];
122
- // Handle different index specification formats
123
- if (Array.isArray(data.indexes) && data.indexes.length > 0) {
124
- indexesArray = data.indexes;
125
- }
126
- else if (Array.isArray(data.index) && data.index.length > 0) {
127
- indexesArray = data.index;
128
- }
129
- else if (typeof data.index === 'number') {
130
- indexesArray = [data.index];
131
- }
132
- // Map backlinks to V2 format - ensure each one has indexes array
133
- const v2Backlinks = data.backLinks.map(bl => {
134
- let indexesValue = [];
135
- if (Array.isArray(bl.indexes) && bl.indexes.length > 0) {
136
- indexesValue = bl.indexes;
137
- }
138
- else if (typeof bl.index === 'number') {
139
- indexesValue = [bl.index];
140
- }
141
- return {
142
- txHash: bl.txHash,
143
- indexes: indexesValue,
144
- checksum: bl.checksum
145
- };
146
- });
104
+ // V2 formatting - uses indexes array
147
105
  return exports.CKBFSDataV2.pack({
148
- indexes: indexesArray,
106
+ indexes: getIndexes(data),
149
107
  checksum: data.checksum,
150
- contentType: data.contentType,
151
- filename: data.filename,
152
- backLinks: v2Backlinks
108
+ contentType: core_1.ccc.bytesFrom(data.contentType, 'utf8'),
109
+ filename: core_1.ccc.bytesFrom(data.filename, 'utf8'),
110
+ backLinks: data.backLinks.map(bl => {
111
+ // Ensure txHash is in proper format for molecule encoding
112
+ const txHash = typeof bl.txHash === 'string'
113
+ ? bl.txHash
114
+ : bl.txHash;
115
+ return {
116
+ indexes: getBackLinkIndexes(bl),
117
+ checksum: bl.checksum,
118
+ txHash,
119
+ };
120
+ }),
153
121
  });
154
122
  }
155
123
  },
@@ -160,12 +128,12 @@ exports.CKBFSData = {
160
128
  return {
161
129
  index: unpacked.index,
162
130
  checksum: unpacked.checksum,
163
- contentType: new Uint8Array(Buffer.from(unpacked.contentType)),
164
- filename: new Uint8Array(Buffer.from(unpacked.filename)),
131
+ contentType: core_1.ccc.bytesTo(unpacked.contentType, 'utf8'),
132
+ filename: core_1.ccc.bytesTo(unpacked.filename, 'utf8'),
165
133
  backLinks: unpacked.backLinks.map(bl => ({
166
- txHash: bl.txHash,
167
134
  index: bl.index,
168
135
  checksum: bl.checksum,
136
+ txHash: bl.txHash,
169
137
  })),
170
138
  };
171
139
  }
@@ -175,12 +143,12 @@ exports.CKBFSData = {
175
143
  return {
176
144
  indexes: unpacked.indexes,
177
145
  checksum: unpacked.checksum,
178
- contentType: new Uint8Array(Buffer.from(unpacked.contentType)),
179
- filename: new Uint8Array(Buffer.from(unpacked.filename)),
146
+ contentType: core_1.ccc.bytesTo(unpacked.contentType, 'utf8'),
147
+ filename: core_1.ccc.bytesTo(unpacked.filename, 'utf8'),
180
148
  backLinks: unpacked.backLinks.map(bl => ({
181
- txHash: bl.txHash,
182
149
  indexes: bl.indexes,
183
150
  checksum: bl.checksum,
151
+ txHash: bl.txHash,
184
152
  })),
185
153
  };
186
154
  }
@@ -51,7 +51,9 @@ async function createPublishTransaction(signer, options) {
51
51
  const textEncoder = new TextEncoder();
52
52
  const combinedContent = Buffer.concat(contentChunks);
53
53
  const checksum = await (0, checksum_1.calculateChecksum)(combinedContent);
54
- // Create CKBFS witnesses
54
+ // Create CKBFS witnesses - each chunk already includes the CKBFS header
55
+ // Pass 0 as version byte - this is the protocol version byte in the witness header
56
+ // not to be confused with the Protocol Version (V1 vs V2)
55
57
  const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSWitnesses)(contentChunks);
56
58
  // Calculate the actual witness indices where our content is placed
57
59
  // Index 0 is reserved for the secp256k1 witness for signing
@@ -61,24 +63,24 @@ async function createPublishTransaction(signer, options) {
61
63
  // Create CKBFS cell output data based on version
62
64
  let outputData;
63
65
  if (version === constants_1.ProtocolVersion.V1) {
64
- // V1 format: Single index field
66
+ // V1 format: Single index field (a single number, not an array)
65
67
  // For V1, use the first index where content is placed
66
68
  outputData = molecule_1.CKBFSData.pack({
67
- index: [contentStartIndex],
69
+ index: contentStartIndex,
68
70
  checksum,
69
- contentType: textEncoder.encode(contentType),
70
- filename: textEncoder.encode(filename),
71
+ contentType: contentType,
72
+ filename: filename,
71
73
  backLinks: [],
72
74
  }, version);
73
75
  }
74
76
  else {
75
- // V2 format: Multiple indexes
77
+ // V2 format: Multiple indexes (array of numbers)
76
78
  // For V2, use all the indices where content is placed
77
79
  outputData = molecule_1.CKBFSData.pack({
78
80
  indexes: witnessIndices,
79
81
  checksum,
80
- contentType: textEncoder.encode(contentType),
81
- filename: textEncoder.encode(filename),
82
+ contentType,
83
+ filename,
82
84
  backLinks: [],
83
85
  }, version);
84
86
  }
@@ -156,115 +158,84 @@ async function createAppendTransaction(signer, options) {
156
158
  const { outPoint, data, type, lock, capacity } = ckbfsCell;
157
159
  // Get CKBFS script config early to use version info
158
160
  const config = (0, constants_1.getCKBFSScriptConfig)(network, version);
159
- // Create CKBFS witnesses - this may vary between V1 and V2
160
- // Pass the version to ensure the correct witness format
161
- const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSWitnesses)(contentChunks, 0, version);
162
- // Combine the new content chunks
161
+ // Create CKBFS witnesses - each chunk already includes the CKBFS header
162
+ // Pass 0 as version byte - this is the protocol version byte in the witness header
163
+ // not to be confused with the Protocol Version (V1 vs V2)
164
+ const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSWitnesses)(contentChunks);
165
+ // Combine the new content chunks for checksum calculation
163
166
  const combinedContent = Buffer.concat(contentChunks);
164
- // Instead of calculating a new checksum from scratch, update the existing checksum
165
- // with the new content - this is more efficient and matches the Adler32 algorithm's
166
- // cumulative nature
167
+ // Update the existing checksum with the new content - this matches Adler32's
168
+ // cumulative nature as required by Rule 11 in the RFC
167
169
  const contentChecksum = await (0, checksum_1.updateChecksum)(data.checksum, combinedContent);
168
170
  console.log(`Updated checksum from ${data.checksum} to ${contentChecksum} for appended content`);
171
+ // Calculate the actual witness indices where our content is placed
172
+ // Index 0 is reserved for the secp256k1 witness for signing
173
+ // So our CKBFS data starts at index 1
174
+ const contentStartIndex = 1;
175
+ const witnessIndices = Array.from({ length: contentChunks.length }, (_, i) => contentStartIndex + i);
169
176
  // Create backlink for the current state based on version
170
177
  let newBackLink;
171
178
  if (version === constants_1.ProtocolVersion.V1) {
172
179
  // V1 format: Use index field (single number)
173
- // In V1, BackLink.index should be a single number (not an array)
174
- let singleIndex = 0;
175
- if (data.index) {
176
- if (Array.isArray(data.index) && data.index.length > 0) {
177
- singleIndex = data.index[0];
178
- }
179
- else if (typeof data.index === 'number') {
180
- singleIndex = data.index;
181
- }
182
- }
183
180
  newBackLink = {
184
- txHash: outPoint.txHash,
185
- index: singleIndex,
181
+ // In V1, field order is index, checksum, txHash
182
+ // and index is a single number value, not an array
183
+ index: data.index || (data.indexes && data.indexes.length > 0 ? data.indexes[0] : 0),
186
184
  checksum: data.checksum,
185
+ txHash: outPoint.txHash,
187
186
  };
188
187
  }
189
188
  else {
190
189
  // V2 format: Use indexes field (array of numbers)
191
- const indices = [];
192
- if (data.indexes && Array.isArray(data.indexes)) {
193
- indices.push(...data.indexes);
194
- }
195
- else if (data.index) {
196
- if (Array.isArray(data.index)) {
197
- indices.push(...data.index);
198
- }
199
- else if (typeof data.index === 'number') {
200
- indices.push(data.index);
201
- }
202
- }
203
190
  newBackLink = {
204
- txHash: outPoint.txHash,
205
- indexes: indices,
191
+ // In V2, field order is indexes, checksum, txHash
192
+ // and indexes is an array of numbers
193
+ indexes: data.indexes || (data.index ? [data.index] : []),
206
194
  checksum: data.checksum,
195
+ txHash: outPoint.txHash,
207
196
  };
208
197
  }
209
- // Update backlinks - preserve the existing backlinks
198
+ // Update backlinks - add the new one to the existing backlinks array
210
199
  const backLinks = [...(data.backLinks || []), newBackLink];
211
- // Calculate the actual witness indices where our content is placed
212
- // Index 0 is reserved for the secp256k1 witness for signing
213
- // So our CKBFS data starts at index 1
214
- const contentStartIndex = 1;
215
- const witnessIndices = Array.from({ length: contentChunks.length }, (_, i) => contentStartIndex + i);
216
- // Define indices based on version
200
+ // Define output data based on version
217
201
  let outputData;
218
202
  if (version === constants_1.ProtocolVersion.V1) {
219
- // In V1, index should be a single number (not an array)
220
- // Rule 13 for V1: Output CKBFS Cell's index cannot be null
203
+ // In V1, index is a single number, not an array
204
+ // The first witness index is used (V1 can only reference one witness)
221
205
  outputData = molecule_1.CKBFSData.pack({
222
- index: witnessIndices[0], // Use the first witness index as a single number
206
+ index: witnessIndices[0], // Use only the first index as a number
223
207
  checksum: contentChecksum,
224
208
  contentType: data.contentType,
225
209
  filename: data.filename,
226
- backLinks: backLinks.map(bl => {
227
- // Ensure V1 format backlinks use a single index number
228
- let singleIndex = 0;
229
- // Handle existing index field
230
- if (typeof bl.index === 'number') {
231
- singleIndex = bl.index;
232
- }
233
- // Try to extract from indexes array if available
234
- else if (bl.indexes && Array.isArray(bl.indexes) && bl.indexes.length > 0) {
235
- singleIndex = bl.indexes[0];
236
- }
237
- return {
238
- txHash: bl.txHash,
239
- index: singleIndex,
240
- checksum: bl.checksum
241
- };
242
- }),
243
- }, version);
210
+ backLinks,
211
+ }, constants_1.ProtocolVersion.V1); // Explicitly use V1 for packing
244
212
  }
245
213
  else {
246
- // In V2, use all the indices where content is placed
247
- // Rule 13 for V2: Output CKBFS Cell's indexes cannot be empty
214
+ // In V2, indexes is an array of witness indices
248
215
  outputData = molecule_1.CKBFSData.pack({
249
216
  indexes: witnessIndices,
250
217
  checksum: contentChecksum,
251
218
  contentType: data.contentType,
252
219
  filename: data.filename,
253
220
  backLinks,
254
- }, version);
221
+ }, constants_1.ProtocolVersion.V2); // Explicitly use V2 for packing
255
222
  }
256
223
  // Pack the original data to get its size - use the appropriate version
257
224
  const originalData = molecule_1.CKBFSData.pack(data, version);
258
225
  const originalDataSize = originalData.length;
259
- // Get sizes
226
+ // Get sizes and calculate capacity requirements
260
227
  const newDataSize = outputData.length;
261
- const dataSizeDiff = newDataSize - originalDataSize;
262
- // Calculate the additional capacity needed (in shannons)
263
- // CKB requires 1 shannon per byte of data
264
- const additionalCapacity = BigInt(Math.max(0, dataSizeDiff)) * 100000000n;
265
- // Add the additional capacity to the original cell capacity
266
- console.log(`Original capacity: ${capacity}, Additional needed: ${additionalCapacity}, Data size diff: ${dataSizeDiff}, Version: ${version}`);
267
- const outputCapacity = capacity + additionalCapacity;
228
+ // Calculate the required capacity for the output cell
229
+ // This accounts for:
230
+ // 1. The output data size
231
+ // 2. The type script's occupied size
232
+ // 3. The lock script's occupied size
233
+ // 4. A constant of 8 bytes (for header overhead)
234
+ const ckbfsCellSize = BigInt(outputData.length + type.occupiedSize + lock.occupiedSize + 8) * 100000000n;
235
+ console.log(`Original capacity: ${capacity}, Calculated size: ${ckbfsCellSize}, Data size: ${outputData.length}`);
236
+ // Use the maximum value between calculated size and original capacity
237
+ // to ensure we have enough capacity but don't decrease capacity unnecessarily
238
+ const outputCapacity = ckbfsCellSize > capacity ? ckbfsCellSize : capacity;
268
239
  // Create initial transaction with the CKBFS cell input
269
240
  const tx = core_1.Transaction.from({
270
241
  inputs: [
@@ -283,10 +254,6 @@ async function createAppendTransaction(signer, options) {
283
254
  capacity: outputCapacity,
284
255
  }
285
256
  ],
286
- witnesses: [
287
- [], // Empty secp witness for signing
288
- ...ckbfsWitnesses.map(w => `0x${Buffer.from(w).toString('hex')}`),
289
- ],
290
257
  outputsData: [
291
258
  outputData,
292
259
  ]
@@ -301,13 +268,28 @@ async function createAppendTransaction(signer, options) {
301
268
  });
302
269
  // Get the recommended address to ensure lock script cell deps are included
303
270
  const address = await signer.getRecommendedAddressObj();
271
+ const inputsBefore = tx.inputs.length;
304
272
  // If we need more capacity than the original cell had, add additional inputs
305
- if (additionalCapacity > 0n) {
273
+ if (outputCapacity > capacity) {
274
+ console.log(`Need additional capacity: ${outputCapacity - capacity} shannons`);
306
275
  // Add more inputs to cover the increased capacity
307
276
  await tx.completeInputsByCapacity(signer);
308
277
  }
278
+ const witnesses = [];
279
+ // add empty witness for signer if ckbfs's lock is the same as signer's lock
280
+ if (address.script.hash() === lock.hash()) {
281
+ witnesses.push('0x');
282
+ }
283
+ // add ckbfs witnesses
284
+ witnesses.push(...ckbfsWitnesses.map(w => `0x${Buffer.from(w).toString('hex')}`));
285
+ // Add empty witnesses for signer's input
286
+ // This is to ensure that the transaction is valid and can be signed
287
+ for (let i = inputsBefore; i < tx.inputs.length; i++) {
288
+ witnesses.push('0x');
289
+ }
290
+ tx.witnesses = witnesses;
309
291
  // Complete fee
310
- await tx.completeFeeChangeToLock(signer, lock || address.script, feeRate || 2000);
292
+ await tx.completeFeeChangeToLock(signer, address.script, feeRate || 2000);
311
293
  return tx;
312
294
  }
313
295
  /**
@@ -7,14 +7,14 @@
7
7
  * @param version Optional version byte (default is 0)
8
8
  * @returns Uint8Array containing the witness data
9
9
  */
10
- export declare function createCKBFSWitness(content: Uint8Array, version?: number): Uint8Array;
10
+ export declare function createCKBFSWitness(content: Uint8Array): Uint8Array;
11
11
  /**
12
12
  * Creates a CKBFS witness with text content
13
13
  * @param text The text content to include in the witness
14
14
  * @param version Optional version byte (default is 0)
15
15
  * @returns Uint8Array containing the witness data
16
16
  */
17
- export declare function createTextCKBFSWitness(text: string, version?: number): Uint8Array;
17
+ export declare function createTextCKBFSWitness(text: string): Uint8Array;
18
18
  /**
19
19
  * Extracts content from a CKBFS witness
20
20
  * @param witness The CKBFS witness data
@@ -34,7 +34,6 @@ export declare function isCKBFSWitness(witness: Uint8Array): boolean;
34
34
  * Creates an array of witnesses for a CKBFS transaction from content chunks
35
35
  * @param contentChunks Array of content chunks
36
36
  * @param version Optional version byte (default is 0)
37
- * @param protocolVersion Optional protocol version (default is V2)
38
37
  * @returns Array of Uint8Array witnesses
39
38
  */
40
- export declare function createChunkedCKBFSWitnesses(contentChunks: Uint8Array[], version?: number, protocolVersion?: string): Uint8Array[];
39
+ export declare function createChunkedCKBFSWitnesses(contentChunks: Uint8Array[]): Uint8Array[];
@@ -15,9 +15,10 @@ const molecule_1 = require("./molecule");
15
15
  * @param version Optional version byte (default is 0)
16
16
  * @returns Uint8Array containing the witness data
17
17
  */
18
- function createCKBFSWitness(content, version = 0) {
18
+ function createCKBFSWitness(content) {
19
19
  // Create witness with CKBFS header, version byte, and content
20
- const versionByte = new Uint8Array([version]);
20
+ // Version byte must always be 0x00 per protocol
21
+ const versionByte = new Uint8Array([0]);
21
22
  return Buffer.concat([molecule_1.CKBFS_HEADER, versionByte, content]);
22
23
  }
23
24
  /**
@@ -26,10 +27,10 @@ function createCKBFSWitness(content, version = 0) {
26
27
  * @param version Optional version byte (default is 0)
27
28
  * @returns Uint8Array containing the witness data
28
29
  */
29
- function createTextCKBFSWitness(text, version = 0) {
30
+ function createTextCKBFSWitness(text) {
30
31
  const textEncoder = new TextEncoder();
31
32
  const contentBytes = textEncoder.encode(text);
32
- return createCKBFSWitness(contentBytes, version);
33
+ return createCKBFSWitness(contentBytes);
33
34
  }
34
35
  /**
35
36
  * Extracts content from a CKBFS witness
@@ -65,9 +66,8 @@ function isCKBFSWitness(witness) {
65
66
  * Creates an array of witnesses for a CKBFS transaction from content chunks
66
67
  * @param contentChunks Array of content chunks
67
68
  * @param version Optional version byte (default is 0)
68
- * @param protocolVersion Optional protocol version (default is V2)
69
69
  * @returns Array of Uint8Array witnesses
70
70
  */
71
- function createChunkedCKBFSWitnesses(contentChunks, version = 0, protocolVersion) {
72
- return contentChunks.map(chunk => createCKBFSWitness(chunk, version));
71
+ function createChunkedCKBFSWitnesses(contentChunks) {
72
+ return contentChunks.map(chunk => createCKBFSWitness(chunk));
73
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckbfs/api",
3
- "version": "1.2.0",
3
+ "version": "1.2.3",
4
4
  "description": "SDK for CKBFS protocol on CKB",
5
5
  "license": "MIT",
6
6
  "author": "Code Monad<code@lab-11.org>",
@@ -44,11 +44,16 @@ export async function updateChecksum(previousChecksum: number, newData: Uint8Arr
44
44
  }
45
45
 
46
46
  // Combine a and b to get the final checksum
47
- const updatedChecksum = (adlerB << 16) | adlerA;
47
+ // Use a Uint32Array to ensure we get a proper unsigned 32-bit integer
48
+ const buffer = new ArrayBuffer(4);
49
+ const view = new DataView(buffer);
50
+ view.setUint16(0, adlerA, true); // Set lower 16 bits (little endian)
51
+ view.setUint16(2, adlerB, true); // Set upper 16 bits (little endian)
48
52
 
49
- // The result should match what you'd get from the adler-32 package
50
- // You can verify this in testing by calculating a full checksum
51
- // of original + new data and comparing with this rolling result
53
+ // Read as an unsigned 32-bit integer
54
+ const updatedChecksum = view.getUint32(0, true);
55
+
56
+ console.log(`Updated checksum from ${previousChecksum} to ${updatedChecksum} for appended content`);
52
57
 
53
58
  return updatedChecksum;
54
59
  }
@@ -378,15 +378,12 @@ export async function createAppendTransaction(
378
378
  capacity: outputCapacity,
379
379
  }
380
380
  ],
381
- witnesses: [
382
- [], // Empty secp witness for signing
383
- ...ckbfsWitnesses.map(w => `0x${Buffer.from(w).toString('hex')}`),
384
- ],
385
381
  outputsData: [
386
382
  outputData,
387
383
  ]
388
384
  });
389
-
385
+
386
+
390
387
  // Add the CKBFS dep group cell dependency
391
388
  tx.addCellDeps({
392
389
  outPoint: {
@@ -398,16 +395,32 @@ export async function createAppendTransaction(
398
395
 
399
396
  // Get the recommended address to ensure lock script cell deps are included
400
397
  const address = await signer.getRecommendedAddressObj();
401
-
398
+
399
+ const inputsBefore = tx.inputs.length;
402
400
  // If we need more capacity than the original cell had, add additional inputs
403
401
  if (outputCapacity > capacity) {
404
402
  console.log(`Need additional capacity: ${outputCapacity - capacity} shannons`);
405
403
  // Add more inputs to cover the increased capacity
406
404
  await tx.completeInputsByCapacity(signer);
407
405
  }
408
-
406
+
407
+ const witnesses: any = []
408
+ // add empty witness for signer if ckbfs's lock is the same as signer's lock
409
+ if(address.script.hash() === lock.hash()) {
410
+ witnesses.push('0x')
411
+ }
412
+ // add ckbfs witnesses
413
+ witnesses.push(...ckbfsWitnesses.map(w => `0x${Buffer.from(w).toString('hex')}`))
414
+
415
+ // Add empty witnesses for signer's input
416
+ // This is to ensure that the transaction is valid and can be signed
417
+ for(let i = inputsBefore; i < tx.inputs.length; i++) {
418
+ witnesses.push('0x')
419
+ }
420
+ tx.witnesses = witnesses
421
+
409
422
  // Complete fee
410
- await tx.completeFeeChangeToLock(signer, lock || address.script, feeRate || 2000);
423
+ await tx.completeFeeChangeToLock(signer, address.script, feeRate || 2000);
411
424
 
412
425
  return tx;
413
426
  }