@ckbfs/api 1.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 +132 -0
- package/RFC.v2.md +341 -0
- package/append.txt +1 -0
- package/example.txt +1 -0
- package/examples/append.ts +222 -0
- package/examples/index.ts +43 -0
- package/examples/publish.ts +76 -0
- package/package.json +39 -0
- package/src/index.ts +363 -0
- package/src/utils/checksum.ts +74 -0
- package/src/utils/constants.ts +123 -0
- package/src/utils/file.ts +109 -0
- package/src/utils/molecule.ts +190 -0
- package/src/utils/transaction.ts +420 -0
- package/src/utils/witness.ts +76 -0
- package/tsconfig.json +15 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
import { CKBFS, NetworkType, ProtocolVersion } from '../src/index';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Main CKBFS Examples
|
5
|
+
*
|
6
|
+
* This file serves as the entry point for running all CKBFS SDK examples.
|
7
|
+
*
|
8
|
+
* To run all examples:
|
9
|
+
* npm run example
|
10
|
+
*
|
11
|
+
* To run specific examples:
|
12
|
+
* npm run example:publish
|
13
|
+
* npm run example:append -- --txhash=0x123456...
|
14
|
+
*/
|
15
|
+
|
16
|
+
console.log('CKBFS SDK Examples');
|
17
|
+
console.log('=================');
|
18
|
+
console.log('');
|
19
|
+
console.log('Available examples:');
|
20
|
+
console.log('1. Publish File Example - npm run example:publish');
|
21
|
+
console.log('2. Append File Example - npm run example:append -- --txhash=0x123456...');
|
22
|
+
console.log('');
|
23
|
+
console.log('Run all examples with: npm run example');
|
24
|
+
console.log('');
|
25
|
+
console.log('Note: For the append example, you need to provide a transaction hash');
|
26
|
+
console.log('of a previously published file using the --txhash parameter or');
|
27
|
+
console.log('by setting the PUBLISH_TX_HASH environment variable.');
|
28
|
+
console.log('');
|
29
|
+
|
30
|
+
// Check if we should run all examples
|
31
|
+
const runAll = process.argv.includes('--all');
|
32
|
+
|
33
|
+
if (runAll) {
|
34
|
+
console.log('Running all examples is not recommended. Please run specific examples instead.');
|
35
|
+
console.log('');
|
36
|
+
console.log('For example:');
|
37
|
+
console.log(' npm run example:publish');
|
38
|
+
console.log(' npm run example:append -- --txhash=0x123456...');
|
39
|
+
process.exit(1);
|
40
|
+
}
|
41
|
+
|
42
|
+
// Default behavior - just show instructions
|
43
|
+
console.log('For more information, see the README.md file.');
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { CKBFS, NetworkType, ProtocolVersion } 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 with network and version options
|
7
|
+
const ckbfs = new CKBFS(
|
8
|
+
privateKey,
|
9
|
+
NetworkType.Testnet, // Use testnet
|
10
|
+
{
|
11
|
+
version: ProtocolVersion.V2, // Use the latest version (V2)
|
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
|
19
|
+
*/
|
20
|
+
async function publishExample() {
|
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 config:', config);
|
29
|
+
|
30
|
+
// Publish a text file to CKBFS
|
31
|
+
const filePath = './example.txt';
|
32
|
+
|
33
|
+
// You can provide additional options
|
34
|
+
const options = {
|
35
|
+
contentType: 'text/plain',
|
36
|
+
filename: 'example.txt',
|
37
|
+
// Specify capacity if needed (default is 200 CKB)
|
38
|
+
// capacity: 250n * 100000000n
|
39
|
+
};
|
40
|
+
|
41
|
+
console.log(`Publishing file: ${filePath}`);
|
42
|
+
const txHash = await ckbfs.publishFile(filePath, options);
|
43
|
+
|
44
|
+
console.log(`File published successfully!`);
|
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:', error);
|
51
|
+
throw error;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Main function to run the example
|
57
|
+
*/
|
58
|
+
async function main() {
|
59
|
+
console.log('Running CKBFS publishing example...');
|
60
|
+
console.log('----------------------------------');
|
61
|
+
console.log(`Using CKBFS protocol version: ${ProtocolVersion.V2}`);
|
62
|
+
|
63
|
+
try {
|
64
|
+
await publishExample();
|
65
|
+
console.log('Example completed successfully!');
|
66
|
+
process.exit(0);
|
67
|
+
} catch (error) {
|
68
|
+
console.error('Example failed:', error);
|
69
|
+
process.exit(1);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
// Run the example if this file is executed directly
|
74
|
+
if (require.main === module) {
|
75
|
+
main().catch(console.error);
|
76
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
{
|
2
|
+
"name": "@ckbfs/api",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "SDK for CKBFS protocol on CKB",
|
5
|
+
"license": "MIT",
|
6
|
+
"author": "Code Monad<code@lab-11.org>",
|
7
|
+
"type": "commonjs",
|
8
|
+
"main": "dist/index.js",
|
9
|
+
"types": "dist/index.d.ts",
|
10
|
+
"scripts": {
|
11
|
+
"build": "tsc",
|
12
|
+
"start": "node dist/index.js",
|
13
|
+
"dev": "ts-node src/index.ts",
|
14
|
+
"test": "jest",
|
15
|
+
"example": "ts-node examples/index.ts",
|
16
|
+
"example:publish": "ts-node examples/publish.ts",
|
17
|
+
"example:append": "ts-node examples/append.ts"
|
18
|
+
},
|
19
|
+
"keywords": [
|
20
|
+
"ckb",
|
21
|
+
"ckbfs",
|
22
|
+
"nervos",
|
23
|
+
"sdk"
|
24
|
+
],
|
25
|
+
"devDependencies": {
|
26
|
+
"@types/node": "^22.7.9",
|
27
|
+
"ts-node": "^10.9.2",
|
28
|
+
"typescript": "^5.6.3",
|
29
|
+
"jest": "^29.7.0",
|
30
|
+
"ts-jest": "^29.1.2",
|
31
|
+
"@types/jest": "^29.5.12"
|
32
|
+
},
|
33
|
+
"dependencies": {
|
34
|
+
"@ckb-ccc/core": "^0.1.0-alpha.4",
|
35
|
+
"@ckb-lumos/base": "^0.23.0",
|
36
|
+
"@ckb-lumos/codec": "^0.23.0",
|
37
|
+
"hash-wasm": "^4.11.0"
|
38
|
+
}
|
39
|
+
}
|
package/src/index.ts
ADDED
@@ -0,0 +1,363 @@
|
|
1
|
+
import { Script, Signer, Transaction, ClientPublicTestnet, SignerCkbPrivateKey } from "@ckb-ccc/core";
|
2
|
+
import {
|
3
|
+
calculateChecksum,
|
4
|
+
verifyChecksum,
|
5
|
+
updateChecksum,
|
6
|
+
verifyWitnessChecksum
|
7
|
+
} from './utils/checksum';
|
8
|
+
import {
|
9
|
+
createCKBFSCell,
|
10
|
+
createPublishTransaction,
|
11
|
+
createAppendTransaction,
|
12
|
+
publishCKBFS,
|
13
|
+
appendCKBFS,
|
14
|
+
CKBFSCellOptions,
|
15
|
+
PublishOptions,
|
16
|
+
AppendOptions
|
17
|
+
} from './utils/transaction';
|
18
|
+
import {
|
19
|
+
readFile,
|
20
|
+
readFileAsText,
|
21
|
+
readFileAsUint8Array,
|
22
|
+
writeFile,
|
23
|
+
getContentType,
|
24
|
+
splitFileIntoChunks,
|
25
|
+
combineChunksToFile
|
26
|
+
} from './utils/file';
|
27
|
+
import {
|
28
|
+
createCKBFSWitness,
|
29
|
+
createTextCKBFSWitness,
|
30
|
+
extractCKBFSWitnessContent,
|
31
|
+
isCKBFSWitness,
|
32
|
+
createChunkedCKBFSWitnesses
|
33
|
+
} from './utils/witness';
|
34
|
+
import {
|
35
|
+
CKBFSData,
|
36
|
+
BackLinkV1,
|
37
|
+
BackLinkV2,
|
38
|
+
CKBFSDataType,
|
39
|
+
BackLinkType,
|
40
|
+
CKBFS_HEADER,
|
41
|
+
CKBFS_HEADER_STRING
|
42
|
+
} from './utils/molecule';
|
43
|
+
import {
|
44
|
+
NetworkType,
|
45
|
+
ProtocolVersion,
|
46
|
+
DEFAULT_NETWORK,
|
47
|
+
DEFAULT_VERSION,
|
48
|
+
CKBFS_CODE_HASH,
|
49
|
+
CKBFS_TYPE_ID,
|
50
|
+
ADLER32_CODE_HASH,
|
51
|
+
ADLER32_TYPE_ID,
|
52
|
+
DEP_GROUP_TX_HASH,
|
53
|
+
DEPLOY_TX_HASH,
|
54
|
+
getCKBFSScriptConfig,
|
55
|
+
CKBFSScriptConfig
|
56
|
+
} from './utils/constants';
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Custom options for file publishing and appending
|
60
|
+
*/
|
61
|
+
export interface FileOptions {
|
62
|
+
contentType?: string;
|
63
|
+
filename?: string;
|
64
|
+
capacity?: bigint;
|
65
|
+
feeRate?: number;
|
66
|
+
network?: NetworkType;
|
67
|
+
version?: string;
|
68
|
+
useTypeID?: boolean;
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Configuration options for the CKBFS SDK
|
73
|
+
*/
|
74
|
+
export interface CKBFSOptions {
|
75
|
+
chunkSize?: number;
|
76
|
+
version?: string;
|
77
|
+
useTypeID?: boolean;
|
78
|
+
network?: NetworkType;
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Main CKBFS SDK class
|
83
|
+
*/
|
84
|
+
export class CKBFS {
|
85
|
+
private signer: Signer;
|
86
|
+
private chunkSize: number;
|
87
|
+
private network: NetworkType;
|
88
|
+
private version: string;
|
89
|
+
private useTypeID: boolean;
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Creates a new CKBFS SDK instance
|
93
|
+
* @param signerOrPrivateKey The signer instance or CKB private key to use for signing transactions
|
94
|
+
* @param networkOrOptions The network type or configuration options
|
95
|
+
* @param options Additional configuration options when using privateKey
|
96
|
+
*/
|
97
|
+
constructor(
|
98
|
+
signerOrPrivateKey: Signer | string,
|
99
|
+
networkOrOptions: NetworkType | CKBFSOptions = DEFAULT_NETWORK,
|
100
|
+
options?: CKBFSOptions
|
101
|
+
) {
|
102
|
+
// Determine if first parameter is a Signer or privateKey
|
103
|
+
if (typeof signerOrPrivateKey === 'string') {
|
104
|
+
// Initialize with private key
|
105
|
+
const privateKey = signerOrPrivateKey;
|
106
|
+
const network = typeof networkOrOptions === 'string' ? networkOrOptions : DEFAULT_NETWORK;
|
107
|
+
const opts = options || (typeof networkOrOptions === 'object' ? networkOrOptions : {});
|
108
|
+
|
109
|
+
const client = new ClientPublicTestnet();
|
110
|
+
this.signer = new SignerCkbPrivateKey(client, privateKey);
|
111
|
+
this.network = network;
|
112
|
+
this.chunkSize = opts.chunkSize || 30 * 1024;
|
113
|
+
this.version = opts.version || DEFAULT_VERSION;
|
114
|
+
this.useTypeID = opts.useTypeID || false;
|
115
|
+
} else {
|
116
|
+
// Initialize with signer
|
117
|
+
this.signer = signerOrPrivateKey;
|
118
|
+
const opts = typeof networkOrOptions === 'object' ? networkOrOptions : {};
|
119
|
+
|
120
|
+
this.network = opts.network || DEFAULT_NETWORK;
|
121
|
+
this.chunkSize = opts.chunkSize || 30 * 1024;
|
122
|
+
this.version = opts.version || DEFAULT_VERSION;
|
123
|
+
this.useTypeID = opts.useTypeID || false;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Gets the recommended address object for the signer
|
129
|
+
* @returns Promise resolving to the address object
|
130
|
+
*/
|
131
|
+
async getAddress() {
|
132
|
+
return this.signer.getRecommendedAddressObj();
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* Gets the lock script for the signer
|
137
|
+
* @returns Promise resolving to the lock script
|
138
|
+
*/
|
139
|
+
async getLock(): Promise<Script> {
|
140
|
+
const address = await this.getAddress();
|
141
|
+
return address.script;
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* Gets the CKBFS script configuration for the current settings
|
146
|
+
* @returns The CKBFS script configuration
|
147
|
+
*/
|
148
|
+
getCKBFSConfig(): CKBFSScriptConfig {
|
149
|
+
return getCKBFSScriptConfig(this.network, this.version, this.useTypeID);
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Publishes a file to CKBFS
|
154
|
+
* @param filePath The path to the file to publish
|
155
|
+
* @param options Options for publishing the file
|
156
|
+
* @returns Promise resolving to the transaction hash
|
157
|
+
*/
|
158
|
+
async publishFile(filePath: string, options: FileOptions = {}): Promise<string> {
|
159
|
+
// Read the file and split into chunks
|
160
|
+
const fileContent = readFileAsUint8Array(filePath);
|
161
|
+
const contentChunks = [];
|
162
|
+
|
163
|
+
for (let i = 0; i < fileContent.length; i += this.chunkSize) {
|
164
|
+
contentChunks.push(fileContent.slice(i, i + this.chunkSize));
|
165
|
+
}
|
166
|
+
|
167
|
+
// Get the lock script
|
168
|
+
const lock = await this.getLock();
|
169
|
+
|
170
|
+
// Determine content type if not provided
|
171
|
+
const contentType = options.contentType || getContentType(filePath);
|
172
|
+
|
173
|
+
// Use the filename from the path if not provided
|
174
|
+
const pathParts = filePath.split(/[\\\/]/);
|
175
|
+
const filename = options.filename || pathParts[pathParts.length - 1];
|
176
|
+
|
177
|
+
// Create and sign the transaction
|
178
|
+
const tx = await publishCKBFS(this.signer, {
|
179
|
+
contentChunks,
|
180
|
+
contentType,
|
181
|
+
filename,
|
182
|
+
lock,
|
183
|
+
capacity: options.capacity,
|
184
|
+
feeRate: options.feeRate,
|
185
|
+
network: options.network || this.network,
|
186
|
+
version: options.version || this.version,
|
187
|
+
useTypeID: options.useTypeID !== undefined ? options.useTypeID : this.useTypeID
|
188
|
+
});
|
189
|
+
|
190
|
+
console.log('tx', tx.stringify());
|
191
|
+
|
192
|
+
// Send the transaction
|
193
|
+
const txHash = await this.signer.sendTransaction(tx);
|
194
|
+
|
195
|
+
return txHash;
|
196
|
+
}
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Appends content to an existing CKBFS file
|
200
|
+
* @param filePath The path to the file containing the content to append
|
201
|
+
* @param ckbfsCell The CKBFS cell to append to
|
202
|
+
* @param options Additional options for the append operation
|
203
|
+
* @returns Promise resolving to the transaction hash
|
204
|
+
*/
|
205
|
+
async appendFile(
|
206
|
+
filePath: string,
|
207
|
+
ckbfsCell: AppendOptions['ckbfsCell'],
|
208
|
+
options: Omit<FileOptions, 'contentType' | 'filename'> = {}
|
209
|
+
): Promise<string> {
|
210
|
+
// Read the file and split into chunks
|
211
|
+
const fileContent = readFileAsUint8Array(filePath);
|
212
|
+
const contentChunks = [];
|
213
|
+
|
214
|
+
for (let i = 0; i < fileContent.length; i += this.chunkSize) {
|
215
|
+
contentChunks.push(fileContent.slice(i, i + this.chunkSize));
|
216
|
+
}
|
217
|
+
|
218
|
+
// Create and sign the transaction
|
219
|
+
const tx = await appendCKBFS(this.signer, {
|
220
|
+
ckbfsCell,
|
221
|
+
contentChunks,
|
222
|
+
feeRate: options.feeRate,
|
223
|
+
network: options.network || this.network,
|
224
|
+
version: options.version || this.version
|
225
|
+
});
|
226
|
+
|
227
|
+
// Send the transaction
|
228
|
+
const txHash = await this.signer.sendTransaction(tx);
|
229
|
+
|
230
|
+
return txHash;
|
231
|
+
}
|
232
|
+
|
233
|
+
/**
|
234
|
+
* Creates a new transaction for publishing a file but doesn't sign or send it
|
235
|
+
* @param filePath The path to the file to publish
|
236
|
+
* @param options Options for publishing the file
|
237
|
+
* @returns Promise resolving to the unsigned transaction
|
238
|
+
*/
|
239
|
+
async createPublishTransaction(filePath: string, options: FileOptions = {}): Promise<Transaction> {
|
240
|
+
// Read the file and split into chunks
|
241
|
+
const fileContent = readFileAsUint8Array(filePath);
|
242
|
+
const contentChunks = [];
|
243
|
+
|
244
|
+
for (let i = 0; i < fileContent.length; i += this.chunkSize) {
|
245
|
+
contentChunks.push(fileContent.slice(i, i + this.chunkSize));
|
246
|
+
}
|
247
|
+
|
248
|
+
// Get the lock script
|
249
|
+
const lock = await this.getLock();
|
250
|
+
|
251
|
+
// Determine content type if not provided
|
252
|
+
const contentType = options.contentType || getContentType(filePath);
|
253
|
+
|
254
|
+
// Use the filename from the path if not provided
|
255
|
+
const pathParts = filePath.split(/[\\\/]/);
|
256
|
+
const filename = options.filename || pathParts[pathParts.length - 1];
|
257
|
+
|
258
|
+
// Create the transaction
|
259
|
+
return createPublishTransaction(this.signer, {
|
260
|
+
contentChunks,
|
261
|
+
contentType,
|
262
|
+
filename,
|
263
|
+
lock,
|
264
|
+
capacity: options.capacity,
|
265
|
+
feeRate: options.feeRate,
|
266
|
+
network: options.network || this.network,
|
267
|
+
version: options.version || this.version,
|
268
|
+
useTypeID: options.useTypeID !== undefined ? options.useTypeID : this.useTypeID
|
269
|
+
});
|
270
|
+
}
|
271
|
+
|
272
|
+
/**
|
273
|
+
* Creates a new transaction for appending content but doesn't sign or send it
|
274
|
+
* @param filePath The path to the file containing the content to append
|
275
|
+
* @param ckbfsCell The CKBFS cell to append to
|
276
|
+
* @param options Additional options for the append operation
|
277
|
+
* @returns Promise resolving to the unsigned transaction
|
278
|
+
*/
|
279
|
+
async createAppendTransaction(
|
280
|
+
filePath: string,
|
281
|
+
ckbfsCell: AppendOptions['ckbfsCell'],
|
282
|
+
options: Omit<FileOptions, 'contentType' | 'filename'> = {}
|
283
|
+
): Promise<Transaction> {
|
284
|
+
// Read the file and split into chunks
|
285
|
+
const fileContent = readFileAsUint8Array(filePath);
|
286
|
+
const contentChunks = [];
|
287
|
+
|
288
|
+
for (let i = 0; i < fileContent.length; i += this.chunkSize) {
|
289
|
+
contentChunks.push(fileContent.slice(i, i + this.chunkSize));
|
290
|
+
}
|
291
|
+
|
292
|
+
// Create the transaction
|
293
|
+
return createAppendTransaction(this.signer, {
|
294
|
+
ckbfsCell,
|
295
|
+
contentChunks,
|
296
|
+
feeRate: options.feeRate,
|
297
|
+
network: options.network || this.network,
|
298
|
+
version: options.version || this.version
|
299
|
+
});
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
// Export utility functions
|
304
|
+
export {
|
305
|
+
// Checksum utilities
|
306
|
+
calculateChecksum,
|
307
|
+
verifyChecksum,
|
308
|
+
updateChecksum,
|
309
|
+
verifyWitnessChecksum,
|
310
|
+
|
311
|
+
// Transaction utilities
|
312
|
+
createCKBFSCell,
|
313
|
+
createPublishTransaction,
|
314
|
+
createAppendTransaction,
|
315
|
+
publishCKBFS,
|
316
|
+
appendCKBFS,
|
317
|
+
|
318
|
+
// File utilities
|
319
|
+
readFile,
|
320
|
+
readFileAsText,
|
321
|
+
readFileAsUint8Array,
|
322
|
+
writeFile,
|
323
|
+
getContentType,
|
324
|
+
splitFileIntoChunks,
|
325
|
+
combineChunksToFile,
|
326
|
+
|
327
|
+
// Witness utilities
|
328
|
+
createCKBFSWitness,
|
329
|
+
createTextCKBFSWitness,
|
330
|
+
extractCKBFSWitnessContent,
|
331
|
+
isCKBFSWitness,
|
332
|
+
createChunkedCKBFSWitnesses,
|
333
|
+
|
334
|
+
// Molecule definitions
|
335
|
+
CKBFSData,
|
336
|
+
BackLinkV1,
|
337
|
+
BackLinkV2,
|
338
|
+
|
339
|
+
// Types
|
340
|
+
CKBFSDataType,
|
341
|
+
BackLinkType,
|
342
|
+
CKBFSCellOptions,
|
343
|
+
PublishOptions,
|
344
|
+
AppendOptions,
|
345
|
+
|
346
|
+
// Constants
|
347
|
+
CKBFS_HEADER,
|
348
|
+
CKBFS_HEADER_STRING,
|
349
|
+
|
350
|
+
// CKBFS Protocol Constants & Configuration
|
351
|
+
NetworkType,
|
352
|
+
ProtocolVersion,
|
353
|
+
DEFAULT_NETWORK,
|
354
|
+
DEFAULT_VERSION,
|
355
|
+
CKBFS_CODE_HASH,
|
356
|
+
CKBFS_TYPE_ID,
|
357
|
+
ADLER32_CODE_HASH,
|
358
|
+
ADLER32_TYPE_ID,
|
359
|
+
DEP_GROUP_TX_HASH,
|
360
|
+
DEPLOY_TX_HASH,
|
361
|
+
getCKBFSScriptConfig,
|
362
|
+
CKBFSScriptConfig
|
363
|
+
};
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import { adler32 } from 'hash-wasm';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Utility functions for Adler32 checksum generation and verification
|
5
|
+
*/
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Calculates Adler32 checksum for the provided data
|
9
|
+
* @param data The data to calculate checksum for
|
10
|
+
* @returns Promise resolving to the calculated checksum as a number
|
11
|
+
*/
|
12
|
+
export async function calculateChecksum(data: Uint8Array): Promise<number> {
|
13
|
+
const checksumString = await adler32(data);
|
14
|
+
const checksumBuffer = Buffer.from(checksumString, 'hex');
|
15
|
+
return checksumBuffer.readUInt32BE();
|
16
|
+
}
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Updates an existing checksum with new data
|
20
|
+
* @param previousChecksum The existing checksum to update
|
21
|
+
* @param newData The new data to add to the checksum
|
22
|
+
* @returns Promise resolving to the updated checksum as a number
|
23
|
+
*/
|
24
|
+
export async function updateChecksum(previousChecksum: number, newData: Uint8Array): Promise<number> {
|
25
|
+
// In a real implementation, this would require the actual Adler32 state recovery
|
26
|
+
// For now, we're simply concatenating the previousChecksum as a hex string with the new data
|
27
|
+
// and calculating a new checksum
|
28
|
+
|
29
|
+
const checksumBytes = Buffer.alloc(4);
|
30
|
+
checksumBytes.writeUInt32BE(previousChecksum);
|
31
|
+
|
32
|
+
// Concatenate the previous checksum bytes with the new data
|
33
|
+
const combinedData = Buffer.concat([checksumBytes, Buffer.from(newData)]);
|
34
|
+
|
35
|
+
// Calculate the new checksum
|
36
|
+
return calculateChecksum(combinedData);
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Verifies if a given checksum matches the expected checksum for the data
|
41
|
+
* @param data The data to verify
|
42
|
+
* @param expectedChecksum The expected checksum
|
43
|
+
* @returns Promise resolving to a boolean indicating whether the checksum is valid
|
44
|
+
*/
|
45
|
+
export async function verifyChecksum(data: Uint8Array, expectedChecksum: number): Promise<boolean> {
|
46
|
+
const calculatedChecksum = await calculateChecksum(data);
|
47
|
+
return calculatedChecksum === expectedChecksum;
|
48
|
+
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Verifies the checksum of a CKBFS witness
|
52
|
+
* @param witness The witness bytes
|
53
|
+
* @param expectedChecksum The expected checksum
|
54
|
+
* @param backlinks Optional backlinks to use for checksum verification
|
55
|
+
* @returns Promise resolving to a boolean indicating whether the checksum is valid
|
56
|
+
*/
|
57
|
+
export async function verifyWitnessChecksum(
|
58
|
+
witness: Uint8Array,
|
59
|
+
expectedChecksum: number,
|
60
|
+
backlinks: { checksum: number }[] = []
|
61
|
+
): Promise<boolean> {
|
62
|
+
// Extract the content bytes from the witness (skip the CKBFS header and version)
|
63
|
+
const contentBytes = witness.slice(6);
|
64
|
+
|
65
|
+
// If backlinks are provided, use the last backlink's checksum
|
66
|
+
if (backlinks.length > 0) {
|
67
|
+
const lastBacklink = backlinks[backlinks.length - 1];
|
68
|
+
const updatedChecksum = await updateChecksum(lastBacklink.checksum, contentBytes);
|
69
|
+
return updatedChecksum === expectedChecksum;
|
70
|
+
}
|
71
|
+
|
72
|
+
// Otherwise, calculate checksum from scratch
|
73
|
+
return verifyChecksum(contentBytes, expectedChecksum);
|
74
|
+
}
|