@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,123 @@
|
|
1
|
+
/**
|
2
|
+
* CKBFS protocol deployment constants
|
3
|
+
*/
|
4
|
+
|
5
|
+
export enum NetworkType {
|
6
|
+
Mainnet = 'mainnet',
|
7
|
+
Testnet = 'testnet'
|
8
|
+
}
|
9
|
+
|
10
|
+
// Use string literals for version values to avoid TypeScript indexing issues
|
11
|
+
export const ProtocolVersion = {
|
12
|
+
V1: '20240906.ce6724722cf6', // Original version, compact and simple, suitable for small files
|
13
|
+
V2: '20241025.db973a8e8032' // New version, more features and can do complex operations
|
14
|
+
} as const;
|
15
|
+
|
16
|
+
export type ProtocolVersionType = typeof ProtocolVersion[keyof typeof ProtocolVersion];
|
17
|
+
|
18
|
+
// CKBFS Type Script Constants
|
19
|
+
export const CKBFS_CODE_HASH: Record<NetworkType, Record<string, string>> = {
|
20
|
+
[NetworkType.Mainnet]: {
|
21
|
+
[ProtocolVersion.V2]: '0x31e6376287d223b8c0410d562fb422f04d1d617b2947596a14c3d2efb7218d3a'
|
22
|
+
},
|
23
|
+
[NetworkType.Testnet]: {
|
24
|
+
[ProtocolVersion.V1]: '0xe8905ad29a02cf8befa9c258f4f941773839a618d75a64afc22059de9413f712',
|
25
|
+
[ProtocolVersion.V2]: '0x31e6376287d223b8c0410d562fb422f04d1d617b2947596a14c3d2efb7218d3a'
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
29
|
+
export const CKBFS_TYPE_ID: Record<NetworkType, Record<string, string>> = {
|
30
|
+
[NetworkType.Mainnet]: {
|
31
|
+
[ProtocolVersion.V2]: '0xfd2058c9a0c0183354cf637e25d2707ffa9bb6fa2ba9b29f4ebc6be3e54ad7eb'
|
32
|
+
},
|
33
|
+
[NetworkType.Testnet]: {
|
34
|
+
[ProtocolVersion.V1]: '0x88ef4d436af35684a27edda0d44dd8771318330285f90f02d13606e095aea86f',
|
35
|
+
[ProtocolVersion.V2]: '0x7c6dcab8268201f064dc8676b5eafa60ca2569e5c6209dcbab0eb64a9cb3aaa3'
|
36
|
+
}
|
37
|
+
};
|
38
|
+
|
39
|
+
// Adler32 Hasher Constants
|
40
|
+
export const ADLER32_CODE_HASH: Record<NetworkType, Record<string, string>> = {
|
41
|
+
[NetworkType.Mainnet]: {
|
42
|
+
[ProtocolVersion.V2]: '0x2138683f76944437c0c643664120d620bdb5858dd6c9d1d156805e279c2c536f'
|
43
|
+
},
|
44
|
+
[NetworkType.Testnet]: {
|
45
|
+
[ProtocolVersion.V1]: '0x8af42cd329cf1bcffb4c73b48252e99cb32346fdbc1cdaa5ae1d000232d47e84',
|
46
|
+
[ProtocolVersion.V2]: '0x2138683f76944437c0c643664120d620bdb5858dd6c9d1d156805e279c2c536f'
|
47
|
+
}
|
48
|
+
};
|
49
|
+
|
50
|
+
export const ADLER32_TYPE_ID: Record<NetworkType, Record<string, string>> = {
|
51
|
+
[NetworkType.Mainnet]: {
|
52
|
+
[ProtocolVersion.V2]: '0x641c01d590833a3f5471bd441651d9f2a8a200141949cdfeef2d68d8094c5876'
|
53
|
+
},
|
54
|
+
[NetworkType.Testnet]: {
|
55
|
+
[ProtocolVersion.V1]: '0xccf29a0d8e860044a3d2f6a6e709f6572f77e4fe245fadd212fc342337048d60',
|
56
|
+
[ProtocolVersion.V2]: '0x5f73f128be76e397f5a3b56c94ca16883a8ee91b498bc0ee80473818318c05ac'
|
57
|
+
}
|
58
|
+
};
|
59
|
+
|
60
|
+
// Dep Group Transaction Constants
|
61
|
+
export const DEP_GROUP_TX_HASH: Record<NetworkType, Record<string, string>> = {
|
62
|
+
[NetworkType.Mainnet]: {
|
63
|
+
[ProtocolVersion.V2]: '0xfab07962ed7178ed88d450774e2a6ecd50bae856bdb9b692980be8c5147d1bfa'
|
64
|
+
},
|
65
|
+
[NetworkType.Testnet]: {
|
66
|
+
[ProtocolVersion.V1]: '0xc8fd44aba36f0c4b37536b6c7ea3b88df65fa97e02f77cd33b9bf20bf241a09b',
|
67
|
+
[ProtocolVersion.V2]: '0x469af0d961dcaaedd872968a9388b546717a6ccfa47b3165b3f9c981e9d66aaa'
|
68
|
+
}
|
69
|
+
};
|
70
|
+
|
71
|
+
// Deploy Transaction Constants
|
72
|
+
export const DEPLOY_TX_HASH: Record<NetworkType, Record<string, { ckbfs: string; adler32: string }>> = {
|
73
|
+
[NetworkType.Mainnet]: {
|
74
|
+
[ProtocolVersion.V2]: {
|
75
|
+
ckbfs: '0xc9b6698f44c3b80e7e1c48823b2714e432b93f0206ffaf9df885d23267ed2ebc',
|
76
|
+
adler32: '0xc9b6698f44c3b80e7e1c48823b2714e432b93f0206ffaf9df885d23267ed2ebc'
|
77
|
+
}
|
78
|
+
},
|
79
|
+
[NetworkType.Testnet]: {
|
80
|
+
[ProtocolVersion.V1]: {
|
81
|
+
ckbfs: '0xde8eb09151fbcdcba398423159ce348cc89a38a736de3fd0960b18b084465382',
|
82
|
+
adler32: '0x042f264d7397a181437b51ff9981cf536f252ab5740b61ce52ce31ada04ed54b'
|
83
|
+
},
|
84
|
+
[ProtocolVersion.V2]: {
|
85
|
+
ckbfs: '0x2c8c9ad3134743368b5a79977648f96c5bd0aba187021a72fb624301064d3616',
|
86
|
+
adler32: '0x2c8c9ad3134743368b5a79977648f96c5bd0aba187021a72fb624301064d3616'
|
87
|
+
}
|
88
|
+
}
|
89
|
+
};
|
90
|
+
|
91
|
+
// Default values - V2 is now the default
|
92
|
+
export const DEFAULT_VERSION = ProtocolVersion.V2;
|
93
|
+
export const DEFAULT_NETWORK = NetworkType.Testnet;
|
94
|
+
|
95
|
+
// Helper function to get CKBFS script configuration
|
96
|
+
export interface CKBFSScriptConfig {
|
97
|
+
codeHash: string;
|
98
|
+
hashType: 'data1' | 'type';
|
99
|
+
depTxHash: string;
|
100
|
+
depIndex?: number;
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* Get CKBFS script configuration for a specific network and version
|
105
|
+
* @param network Network type (mainnet or testnet)
|
106
|
+
* @param version Protocol version (default: latest version)
|
107
|
+
* @param useTypeID Whether to use type ID instead of code hash (default: false)
|
108
|
+
* @returns CKBFS script configuration
|
109
|
+
*/
|
110
|
+
export function getCKBFSScriptConfig(
|
111
|
+
network: NetworkType = DEFAULT_NETWORK,
|
112
|
+
version: string = DEFAULT_VERSION,
|
113
|
+
useTypeID: boolean = false
|
114
|
+
): CKBFSScriptConfig {
|
115
|
+
return {
|
116
|
+
codeHash: useTypeID
|
117
|
+
? CKBFS_TYPE_ID[network][version]
|
118
|
+
: CKBFS_CODE_HASH[network][version],
|
119
|
+
hashType: useTypeID ? 'type' : 'data1',
|
120
|
+
depTxHash: DEP_GROUP_TX_HASH[network][version],
|
121
|
+
depIndex: 0
|
122
|
+
};
|
123
|
+
}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import fs from 'fs';
|
2
|
+
import path from 'path';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Utility functions for file operations
|
6
|
+
*/
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Reads a file from the file system
|
10
|
+
* @param filePath The path to the file to read
|
11
|
+
* @returns Buffer containing the file contents
|
12
|
+
*/
|
13
|
+
export function readFile(filePath: string): Buffer {
|
14
|
+
return fs.readFileSync(filePath);
|
15
|
+
}
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Reads a file as text from the file system
|
19
|
+
* @param filePath The path to the file to read
|
20
|
+
* @returns String containing the file contents
|
21
|
+
*/
|
22
|
+
export function readFileAsText(filePath: string): string {
|
23
|
+
return fs.readFileSync(filePath, 'utf-8');
|
24
|
+
}
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Reads a file as Uint8Array from the file system
|
28
|
+
* @param filePath The path to the file to read
|
29
|
+
* @returns Uint8Array containing the file contents
|
30
|
+
*/
|
31
|
+
export function readFileAsUint8Array(filePath: string): Uint8Array {
|
32
|
+
const buffer = fs.readFileSync(filePath);
|
33
|
+
return new Uint8Array(buffer);
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Writes data to a file in the file system
|
38
|
+
* @param filePath The path to write the file to
|
39
|
+
* @param data The data to write to the file
|
40
|
+
*/
|
41
|
+
export function writeFile(filePath: string, data: Buffer | string): void {
|
42
|
+
// Ensure the directory exists
|
43
|
+
const dirPath = path.dirname(filePath);
|
44
|
+
if (!fs.existsSync(dirPath)) {
|
45
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
46
|
+
}
|
47
|
+
|
48
|
+
fs.writeFileSync(filePath, data);
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Gets the MIME content type based on file extension
|
53
|
+
* @param filePath The path to the file
|
54
|
+
* @returns The MIME content type for the file
|
55
|
+
*/
|
56
|
+
export function getContentType(filePath: string): string {
|
57
|
+
const extension = path.extname(filePath).toLowerCase();
|
58
|
+
|
59
|
+
const mimeTypes: { [key: string]: string } = {
|
60
|
+
'.txt': 'text/plain',
|
61
|
+
'.html': 'text/html',
|
62
|
+
'.htm': 'text/html',
|
63
|
+
'.css': 'text/css',
|
64
|
+
'.js': 'application/javascript',
|
65
|
+
'.json': 'application/json',
|
66
|
+
'.jpg': 'image/jpeg',
|
67
|
+
'.jpeg': 'image/jpeg',
|
68
|
+
'.png': 'image/png',
|
69
|
+
'.gif': 'image/gif',
|
70
|
+
'.svg': 'image/svg+xml',
|
71
|
+
'.pdf': 'application/pdf',
|
72
|
+
'.mp3': 'audio/mpeg',
|
73
|
+
'.mp4': 'video/mp4',
|
74
|
+
'.wav': 'audio/wav',
|
75
|
+
'.xml': 'application/xml',
|
76
|
+
'.zip': 'application/zip',
|
77
|
+
'.md': 'text/markdown',
|
78
|
+
'.markdown': 'text/markdown',
|
79
|
+
};
|
80
|
+
|
81
|
+
return mimeTypes[extension] || 'application/octet-stream';
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Splits a file into chunks of a specific size
|
86
|
+
* @param filePath The path to the file to split
|
87
|
+
* @param chunkSize The maximum size of each chunk in bytes
|
88
|
+
* @returns Array of Uint8Array chunks
|
89
|
+
*/
|
90
|
+
export function splitFileIntoChunks(filePath: string, chunkSize: number): Uint8Array[] {
|
91
|
+
const fileBuffer = fs.readFileSync(filePath);
|
92
|
+
const chunks: Uint8Array[] = [];
|
93
|
+
|
94
|
+
for (let i = 0; i < fileBuffer.length; i += chunkSize) {
|
95
|
+
chunks.push(new Uint8Array(fileBuffer.slice(i, i + chunkSize)));
|
96
|
+
}
|
97
|
+
|
98
|
+
return chunks;
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Combines chunks into a single file
|
103
|
+
* @param chunks Array of chunks to combine
|
104
|
+
* @param outputPath The path to write the combined file to
|
105
|
+
*/
|
106
|
+
export function combineChunksToFile(chunks: Uint8Array[], outputPath: string): void {
|
107
|
+
const combinedBuffer = Buffer.concat(chunks.map(chunk => Buffer.from(chunk)));
|
108
|
+
writeFile(outputPath, combinedBuffer);
|
109
|
+
}
|
@@ -0,0 +1,190 @@
|
|
1
|
+
import { molecule, number } from "@ckb-lumos/codec";
|
2
|
+
import { blockchain } from "@ckb-lumos/base";
|
3
|
+
import { ProtocolVersion } from "./constants";
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Molecule definitions for CKBFS data structures.
|
7
|
+
*/
|
8
|
+
|
9
|
+
// Define the Indexes vector
|
10
|
+
export const Indexes = molecule.vector(number.Uint32);
|
11
|
+
|
12
|
+
// Define the BackLink table structure for V1
|
13
|
+
export const BackLinkV1 = molecule.table(
|
14
|
+
{
|
15
|
+
txHash: blockchain.Byte32,
|
16
|
+
index: number.Uint32,
|
17
|
+
checksum: number.Uint32,
|
18
|
+
},
|
19
|
+
["txHash", "index", "checksum"]
|
20
|
+
);
|
21
|
+
|
22
|
+
// Define the BackLink table structure for V2
|
23
|
+
export const BackLinkV2 = molecule.table(
|
24
|
+
{
|
25
|
+
txHash: blockchain.Byte32,
|
26
|
+
indexes: Indexes,
|
27
|
+
checksum: number.Uint32,
|
28
|
+
},
|
29
|
+
["txHash", "indexes", "checksum"]
|
30
|
+
);
|
31
|
+
|
32
|
+
// Define the BackLinks vector for V1
|
33
|
+
export const BackLinksV1 = molecule.vector(BackLinkV1);
|
34
|
+
|
35
|
+
// Define the BackLinks vector for V2
|
36
|
+
export const BackLinksV2 = molecule.vector(BackLinkV2);
|
37
|
+
|
38
|
+
// Define the CKBFSData table structure for V1
|
39
|
+
export const CKBFSDataV1 = molecule.table(
|
40
|
+
{
|
41
|
+
index: Indexes,
|
42
|
+
checksum: number.Uint32,
|
43
|
+
contentType: blockchain.Bytes,
|
44
|
+
filename: blockchain.Bytes,
|
45
|
+
backLinks: BackLinksV1,
|
46
|
+
},
|
47
|
+
["index", "checksum", "contentType", "filename", "backLinks"]
|
48
|
+
);
|
49
|
+
|
50
|
+
// Define the CKBFSData table structure for V2
|
51
|
+
export const CKBFSDataV2 = molecule.table(
|
52
|
+
{
|
53
|
+
indexes: Indexes,
|
54
|
+
checksum: number.Uint32,
|
55
|
+
contentType: blockchain.Bytes,
|
56
|
+
filename: blockchain.Bytes,
|
57
|
+
backLinks: BackLinksV2,
|
58
|
+
},
|
59
|
+
["indexes", "checksum", "contentType", "filename", "backLinks"]
|
60
|
+
);
|
61
|
+
|
62
|
+
// Type definitions for TypeScript
|
63
|
+
export type BackLinkTypeV1 = {
|
64
|
+
txHash: string;
|
65
|
+
index: number;
|
66
|
+
checksum: number;
|
67
|
+
};
|
68
|
+
|
69
|
+
export type BackLinkTypeV2 = {
|
70
|
+
txHash: string;
|
71
|
+
indexes: number[];
|
72
|
+
checksum: number;
|
73
|
+
};
|
74
|
+
|
75
|
+
// Combined type that works with both versions
|
76
|
+
export type BackLinkType = {
|
77
|
+
txHash: string;
|
78
|
+
index?: number;
|
79
|
+
indexes?: number[];
|
80
|
+
checksum: number;
|
81
|
+
};
|
82
|
+
|
83
|
+
// Combined CKBFSData type that works with both versions
|
84
|
+
export type CKBFSDataType = {
|
85
|
+
index?: number[];
|
86
|
+
indexes?: number[];
|
87
|
+
checksum: number;
|
88
|
+
contentType: Uint8Array;
|
89
|
+
filename: Uint8Array;
|
90
|
+
backLinks: BackLinkType[];
|
91
|
+
};
|
92
|
+
|
93
|
+
// Helper function to safely get either index or indexes
|
94
|
+
function getIndexes(data: CKBFSDataType): number[] {
|
95
|
+
return data.indexes || data.index || [];
|
96
|
+
}
|
97
|
+
|
98
|
+
// Helper function to safely get either index or indexes from BackLinkType
|
99
|
+
function getBackLinkIndex(bl: BackLinkType): number {
|
100
|
+
if (typeof bl.index === 'number') {
|
101
|
+
return bl.index;
|
102
|
+
}
|
103
|
+
if (Array.isArray(bl.indexes) && bl.indexes.length > 0) {
|
104
|
+
return bl.indexes[0];
|
105
|
+
}
|
106
|
+
return 0;
|
107
|
+
}
|
108
|
+
|
109
|
+
// Helper function to safely get indexes array from BackLinkType
|
110
|
+
function getBackLinkIndexes(bl: BackLinkType): number[] {
|
111
|
+
if (Array.isArray(bl.indexes)) {
|
112
|
+
return bl.indexes;
|
113
|
+
}
|
114
|
+
if (typeof bl.index === 'number') {
|
115
|
+
return [bl.index];
|
116
|
+
}
|
117
|
+
return [0];
|
118
|
+
}
|
119
|
+
|
120
|
+
// Helper function to get the right CKBFSData based on version
|
121
|
+
export const CKBFSData = {
|
122
|
+
pack: (data: CKBFSDataType, version: string = ProtocolVersion.V2): Uint8Array => {
|
123
|
+
if (version === ProtocolVersion.V1) {
|
124
|
+
// V1 formatting
|
125
|
+
return CKBFSDataV1.pack({
|
126
|
+
index: getIndexes(data),
|
127
|
+
checksum: data.checksum,
|
128
|
+
contentType: data.contentType,
|
129
|
+
filename: data.filename,
|
130
|
+
backLinks: data.backLinks.map(bl => ({
|
131
|
+
txHash: bl.txHash,
|
132
|
+
index: getBackLinkIndex(bl),
|
133
|
+
checksum: bl.checksum,
|
134
|
+
})),
|
135
|
+
});
|
136
|
+
} else {
|
137
|
+
// V2 formatting
|
138
|
+
return CKBFSDataV2.pack({
|
139
|
+
indexes: getIndexes(data),
|
140
|
+
checksum: data.checksum,
|
141
|
+
contentType: data.contentType,
|
142
|
+
filename: data.filename,
|
143
|
+
backLinks: data.backLinks.map(bl => ({
|
144
|
+
txHash: bl.txHash,
|
145
|
+
indexes: getBackLinkIndexes(bl),
|
146
|
+
checksum: bl.checksum,
|
147
|
+
})),
|
148
|
+
});
|
149
|
+
}
|
150
|
+
},
|
151
|
+
unpack: (buf: Uint8Array, version: string = ProtocolVersion.V2): CKBFSDataType => {
|
152
|
+
try {
|
153
|
+
if (version === ProtocolVersion.V1) {
|
154
|
+
const unpacked = CKBFSDataV1.unpack(buf);
|
155
|
+
return {
|
156
|
+
index: unpacked.index,
|
157
|
+
checksum: unpacked.checksum,
|
158
|
+
contentType: new Uint8Array(Buffer.from(unpacked.contentType)),
|
159
|
+
filename: new Uint8Array(Buffer.from(unpacked.filename)),
|
160
|
+
backLinks: unpacked.backLinks.map(bl => ({
|
161
|
+
txHash: bl.txHash,
|
162
|
+
index: bl.index,
|
163
|
+
checksum: bl.checksum,
|
164
|
+
})),
|
165
|
+
};
|
166
|
+
} else {
|
167
|
+
// V2 format
|
168
|
+
const unpacked = CKBFSDataV2.unpack(buf);
|
169
|
+
return {
|
170
|
+
indexes: unpacked.indexes,
|
171
|
+
checksum: unpacked.checksum,
|
172
|
+
contentType: new Uint8Array(Buffer.from(unpacked.contentType)),
|
173
|
+
filename: new Uint8Array(Buffer.from(unpacked.filename)),
|
174
|
+
backLinks: unpacked.backLinks.map(bl => ({
|
175
|
+
txHash: bl.txHash,
|
176
|
+
indexes: bl.indexes,
|
177
|
+
checksum: bl.checksum,
|
178
|
+
})),
|
179
|
+
};
|
180
|
+
}
|
181
|
+
} catch (error) {
|
182
|
+
console.error('Error unpacking CKBFSData:', error);
|
183
|
+
throw new Error('Failed to unpack CKBFSData: ' + error);
|
184
|
+
}
|
185
|
+
}
|
186
|
+
};
|
187
|
+
|
188
|
+
// Constants for CKBFS protocol
|
189
|
+
export const CKBFS_HEADER = new Uint8Array([0x43, 0x4B, 0x42, 0x46, 0x53]); // "CKBFS" in ASCII
|
190
|
+
export const CKBFS_HEADER_STRING = "CKBFS";
|