@ckbfs/api 2.0.8 → 2.0.10
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/dist/utils/transactions/v3.js +4 -3
- package/examples/retrieve-v3.ts +16 -56
- package/package.json +1 -1
- package/src/utils/transactions/v3.ts +5 -5
- package/RFC.v3.md +0 -210
- package/code.png +0 -0
- package/demo-output.txt +0 -1
- package/direct_direct_content_example.txt +0 -1
- package/examples/nervape.ts +0 -409
- package/identifier_direct_content_example.txt +0 -1
- package/small-example.txt +0 -1
- package/traditional_direct_content_example.txt +0 -1
- package/typeid_direct_content_example.txt +0 -1
|
@@ -222,7 +222,7 @@ async function prepareAppendV3Transaction(options) {
|
|
|
222
222
|
const combinedContent = Buffer.concat(contentChunks);
|
|
223
223
|
const newChecksum = await (0, checksum_1.updateChecksum)(previousChecksum, combinedContent);
|
|
224
224
|
// Calculate the actual witness indices where our content is placed
|
|
225
|
-
const contentStartIndex =
|
|
225
|
+
const contentStartIndex = from?.witnesses.length || witnessStartIndex || 0;
|
|
226
226
|
// Create CKBFS v3 witnesses with backlink info
|
|
227
227
|
const ckbfsWitnesses = (0, witness_1.createChunkedCKBFSV3Witnesses)(contentChunks, {
|
|
228
228
|
previousTxHash,
|
|
@@ -350,14 +350,15 @@ async function createAppendV3Transaction(signer, options) {
|
|
|
350
350
|
// Add more inputs to cover the increased capacity
|
|
351
351
|
await preTx.completeInputsByCapacity(signer);
|
|
352
352
|
}
|
|
353
|
-
await preTx.completeInputsByCapacity(signer);
|
|
354
353
|
const witnesses = [];
|
|
354
|
+
var witnessOffsetIndex = 0;
|
|
355
355
|
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
356
356
|
if (address.script.hash() === lock.hash()) {
|
|
357
357
|
witnesses.push("0x");
|
|
358
|
+
witnessOffsetIndex = 1;
|
|
358
359
|
}
|
|
359
360
|
// add ckbfs witnesses (skip the first witness which is for signing)
|
|
360
|
-
witnesses.push(...preTx.witnesses.slice(
|
|
361
|
+
witnesses.push(...preTx.witnesses.slice(witnessOffsetIndex));
|
|
361
362
|
// Add empty witnesses for additional signer inputs
|
|
362
363
|
// This is to ensure that the transaction is valid and can be signed
|
|
363
364
|
for (let i = inputsBefore; i < preTx.inputs.length; i++) {
|
package/examples/retrieve-v3.ts
CHANGED
|
@@ -2,10 +2,6 @@ import {
|
|
|
2
2
|
getFileContentFromChainByIdentifierV3,
|
|
3
3
|
saveFileFromChainByIdentifierV3,
|
|
4
4
|
ProtocolVersion,
|
|
5
|
-
CKBFSData,
|
|
6
|
-
getCKBFSScriptConfig,
|
|
7
|
-
NetworkType,
|
|
8
|
-
resolveCKBFSCell,
|
|
9
5
|
} from '../src/index';
|
|
10
6
|
import { ClientPublicTestnet } from '@ckb-ccc/core';
|
|
11
7
|
|
|
@@ -18,7 +14,7 @@ const client = new ClientPublicTestnet(); // Use testnet
|
|
|
18
14
|
async function retrieveFileByTypeIdV3Example() {
|
|
19
15
|
try {
|
|
20
16
|
// Example TypeID (replace with actual TypeID from a v3 published file)
|
|
21
|
-
const typeId = "
|
|
17
|
+
const typeId = "0xc387111856be837b1f358d49a9d7e4461dc18244c2b7b59f9012617b1c09d7be";
|
|
22
18
|
|
|
23
19
|
console.log(`Retrieving CKBFS v3 file by TypeID: ${typeId}`);
|
|
24
20
|
|
|
@@ -110,7 +106,7 @@ async function retrieveAndSaveFileV3Example() {
|
|
|
110
106
|
async function retrieveFileByURIV3Example() {
|
|
111
107
|
try {
|
|
112
108
|
// Example CKBFS URI (replace with actual URI)
|
|
113
|
-
const ckbfsUri = "ckbfs://
|
|
109
|
+
const ckbfsUri = "ckbfs://c387111856be837b1f358d49a9d7e4461dc18244c2b7b59f9012617b1c09d7be";
|
|
114
110
|
|
|
115
111
|
console.log(`Retrieving CKBFS v3 file by URI: ${ckbfsUri}`);
|
|
116
112
|
|
|
@@ -149,7 +145,7 @@ async function retrieveFileByURIV3Example() {
|
|
|
149
145
|
async function retrieveFileByOutPointV3Example() {
|
|
150
146
|
try {
|
|
151
147
|
// Example OutPoint URI (replace with actual transaction hash and index)
|
|
152
|
-
const outPointUri = "ckbfs://
|
|
148
|
+
const outPointUri = "ckbfs://1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefi0";
|
|
153
149
|
|
|
154
150
|
console.log(`Retrieving CKBFS v3 file by OutPoint: ${outPointUri}`);
|
|
155
151
|
|
|
@@ -173,7 +169,6 @@ async function retrieveFileByOutPointV3Example() {
|
|
|
173
169
|
console.log(`Content type: ${fileData.contentType}`);
|
|
174
170
|
console.log(`Size: ${fileData.size} bytes`);
|
|
175
171
|
console.log(`Checksum: ${fileData.checksum}`);
|
|
176
|
-
console.log(`Content: ${fileData.content}`);
|
|
177
172
|
|
|
178
173
|
return fileData;
|
|
179
174
|
} catch (error) {
|
|
@@ -182,39 +177,6 @@ async function retrieveFileByOutPointV3Example() {
|
|
|
182
177
|
}
|
|
183
178
|
}
|
|
184
179
|
|
|
185
|
-
async function testReresolve(ckbfsId: string) {
|
|
186
|
-
const data = (await resolveCKBFSCell(client, ckbfsId, {network: NetworkType.Testnet}))!
|
|
187
|
-
return data
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export async function getCkbfsCellInfo(ckbfsId: string) {
|
|
191
|
-
const { codeHash, hashType } = getCKBFSScriptConfig(NetworkType.Testnet, ProtocolVersion.V3)
|
|
192
|
-
console.log("codeHash, hashType=", codeHash, hashType, ckbfsId)
|
|
193
|
-
const cell = await client.findSingletonCellByType({
|
|
194
|
-
codeHash,
|
|
195
|
-
hashType,
|
|
196
|
-
args: ckbfsId
|
|
197
|
-
}, true)
|
|
198
|
-
|
|
199
|
-
if (!cell || !cell.cellOutput.type) {
|
|
200
|
-
throw new Error('No CKBFS cell found');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const rawData = cell.outputData.startsWith('0x')
|
|
204
|
-
? Buffer.from(cell.outputData.slice(2), 'hex')
|
|
205
|
-
: Buffer.from(cell.outputData, 'hex');
|
|
206
|
-
|
|
207
|
-
const ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V3)
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
outPoint: { txHash: cell.outPoint.txHash as string, index: Number(cell.outPoint.index) },
|
|
211
|
-
type: cell.cellOutput.type,
|
|
212
|
-
lock: cell.cellOutput.lock,
|
|
213
|
-
capacity: cell.cellOutput.capacity,
|
|
214
|
-
data: ckbfsData,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
180
|
/**
|
|
219
181
|
* Main function to run the v3 retrieval examples
|
|
220
182
|
*/
|
|
@@ -229,26 +191,24 @@ async function main() {
|
|
|
229
191
|
console.log('============================================');
|
|
230
192
|
|
|
231
193
|
try {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
194
|
+
console.log('Note: These examples require existing CKBFS v3 files.');
|
|
195
|
+
console.log('Please run publish-v3.ts first to create v3 files,');
|
|
196
|
+
console.log('then update the identifiers in this example.');
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log('Supported identifier formats:');
|
|
199
|
+
console.log('- TypeID: 0x1234...abcdef');
|
|
200
|
+
console.log('- CKBFS URI: ckbfs://1234...abcdef');
|
|
201
|
+
console.log('- OutPoint URI: ckbfs://1234...abcdefi0');
|
|
202
|
+
console.log('');
|
|
241
203
|
|
|
242
204
|
// Uncomment to test different retrieval methods:
|
|
243
|
-
|
|
205
|
+
await retrieveFileByTypeIdV3Example();
|
|
244
206
|
// await retrieveAndSaveFileV3Example();
|
|
245
207
|
await retrieveFileByURIV3Example();
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
// await testReresolve("0x0084106b9bea08f512e35b725c4a710ebf09cb65c05601ebde8d911bc6c662bf").then(console.log)
|
|
208
|
+
// await retrieveFileByOutPointV3Example();
|
|
249
209
|
|
|
250
|
-
|
|
251
|
-
|
|
210
|
+
console.log('Retrieval example structure completed successfully!');
|
|
211
|
+
console.log('Update the identifiers and uncomment methods to test.');
|
|
252
212
|
process.exit(0);
|
|
253
213
|
} catch (error) {
|
|
254
214
|
console.error('CKBFS v3 retrieval examples failed:', error);
|
package/package.json
CHANGED
|
@@ -366,7 +366,7 @@ export async function prepareAppendV3Transaction(
|
|
|
366
366
|
const newChecksum = await updateChecksum(previousChecksum, combinedContent);
|
|
367
367
|
|
|
368
368
|
// Calculate the actual witness indices where our content is placed
|
|
369
|
-
const contentStartIndex =
|
|
369
|
+
const contentStartIndex = from?.witnesses.length || witnessStartIndex || 0;
|
|
370
370
|
|
|
371
371
|
// Create CKBFS v3 witnesses with backlink info
|
|
372
372
|
const ckbfsWitnesses = createChunkedCKBFSV3Witnesses(contentChunks, {
|
|
@@ -519,15 +519,15 @@ export async function createAppendV3Transaction(
|
|
|
519
519
|
await preTx.completeInputsByCapacity(signer);
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
-
await preTx.completeInputsByCapacity(signer);
|
|
523
|
-
|
|
524
522
|
const witnesses: any = [];
|
|
523
|
+
var witnessOffsetIndex = 0;
|
|
525
524
|
// add empty witness for signer if ckbfs's lock is the same as signer's lock
|
|
526
525
|
if (address.script.hash() === lock.hash()) {
|
|
527
526
|
witnesses.push("0x");
|
|
527
|
+
witnessOffsetIndex = 1;
|
|
528
528
|
}
|
|
529
529
|
// add ckbfs witnesses (skip the first witness which is for signing)
|
|
530
|
-
witnesses.push(...preTx.witnesses.slice(
|
|
530
|
+
witnesses.push(...preTx.witnesses.slice(witnessOffsetIndex));
|
|
531
531
|
|
|
532
532
|
// Add empty witnesses for additional signer inputs
|
|
533
533
|
// This is to ensure that the transaction is valid and can be signed
|
|
@@ -781,4 +781,4 @@ export async function transferCKBFSV3(
|
|
|
781
781
|
): Promise<Transaction> {
|
|
782
782
|
const tx = await createTransferV3Transaction(signer, options);
|
|
783
783
|
return signer.signTransaction(tx);
|
|
784
|
-
}
|
|
784
|
+
}
|
package/RFC.v3.md
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
# CKBFS Protocol V3
|
|
2
|
-
|
|
3
|
-
This is a next generation for CKBFS Protocol, aimed to provide a more affordable storage price -- comparing to similar solutions like IPFS.
|
|
4
|
-
|
|
5
|
-
The most important change of V3 is that there's no longer `Backlinks` inside the Cell data. Instead, it was moved into witnesses.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## Protocol Standard
|
|
9
|
-
|
|
10
|
-
### Data Structure
|
|
11
|
-
|
|
12
|
-
#### CKBFS v3 Cell
|
|
13
|
-
|
|
14
|
-
CKBFS Cell is a cell that stores metadata of the file:
|
|
15
|
-
|
|
16
|
-
```yaml
|
|
17
|
-
Data:
|
|
18
|
-
content_type: Bytes # String Bytes
|
|
19
|
-
filename: Bytes # String Bytes
|
|
20
|
-
index: Uint32 # Reference of the first witnesses index.
|
|
21
|
-
checksum: Uint32 # Adler32 checksum
|
|
22
|
-
|
|
23
|
-
Type:
|
|
24
|
-
hash_type: "data1"
|
|
25
|
-
code_hash: CKBFS_V3_TYPE_DATA_HASH
|
|
26
|
-
args: <TypeID, 32 bytes>,[<hasher_code_hash>, optional]
|
|
27
|
-
Lock:
|
|
28
|
-
<user_defined>
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
The following rules should be met in a CKBFS cell:
|
|
32
|
-
|
|
33
|
-
- Rule 1: data structure of a CKBFS cell is molecule encoded. See [Molecule](https://github.com/nervosnetwork/molecule) definitions below.
|
|
34
|
-
- Rule 2: checksum must match with specified witnesses. Default checksum algorithm will be Alder32 if not specify `hasher_code_hash` in Type script args.
|
|
35
|
-
- Rule 3: if `hasher_code_hash` is specified, then it will use hasher binary from CellDeps that matches `code_hash`, with same input parameter.
|
|
36
|
-
- Rule 4: Once created, a CKBFS cell can only be updated/transfered, which means it can not be destroyed.
|
|
37
|
-
- Rule 5: **`index` is the first witness index of the stored CKBFS structured contents in splited witnesses.**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
### Witnesses
|
|
41
|
-
|
|
42
|
-
File contents are stored in witnesses. In a single transaction, witnesses can be splitted into multiple parts and concat together while verification.
|
|
43
|
-
|
|
44
|
-
```yaml
|
|
45
|
-
Witnesses:
|
|
46
|
-
<"CKBFS"> <0x03> <PREVIOUS_POSITION(TX_HASH, Witness_index)> <PREVIOUS_CHECKSUM(4 bytes) | 0x00000000> <NEXT_INDEX | 0x00000000> [CONTENT_BYTES_PART_1]
|
|
47
|
-
<NEXT_INDEX | 0x00000000> <CONTENT_BYTES_PART_2>
|
|
48
|
-
<NEXT_INDEX | 0x00000000> <CONTENT_BYTES_PART_3> ...
|
|
49
|
-
<0x00000000> <CONTENT_BYTES_PART_N>
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
The following rules should be met for witnesses used in CKBFS:
|
|
53
|
-
- Rule 6: Witnesses are different in `Head Witness`, `Middle Witnesses`, and `Tail Witness`, their form should follow bellow's rules
|
|
54
|
-
- Rule 7: The first 5 bytes of `Head Witness` must be UTF8 coded string bytes of `CKBFS`, which should be: `0x434b424653`
|
|
55
|
-
- Rule 8: The 6th byte of `Head Witness` must be the version of CKBFS protocol, which should be: `0x03`.
|
|
56
|
-
- Rule 9: Previous position of this CKBFS content stores from 7th bytes to 42th bytes in `Head Witness`. it should be previous transaction hash(H256) and previous Head Witness index(Uint32).
|
|
57
|
-
- Rule 10: Previous checksum value must stored in `Head Witness`, position from 43rd to 46th bytes. If there's no previous status, then it should be `0x00000000`.
|
|
58
|
-
- Rule 10: File contents bytes are stored from:
|
|
59
|
-
- 51st byte from the Head Witness.
|
|
60
|
-
- 5th byte from the `Middle Witnesses` and `Tail Witness`
|
|
61
|
-
|
|
62
|
-
----
|
|
63
|
-
|
|
64
|
-
### Operations
|
|
65
|
-
|
|
66
|
-
This section describes operations and restrictions in CKBFS v3 implementation.
|
|
67
|
-
|
|
68
|
-
#### Publish
|
|
69
|
-
|
|
70
|
-
Publish operation creates one or more new CKBFS v3 cell.
|
|
71
|
-
|
|
72
|
-
```yaml
|
|
73
|
-
Witnesses:
|
|
74
|
-
<...>
|
|
75
|
-
<0x434b424653, 0x03, 32bytes 0x00, 0x00000000, 0x00000000, 0x00000002,CKBFS_CONTENT_BYTES_PART_1>
|
|
76
|
-
<0x00000003, CKBFS_CONTENT_BYTES_PART_2>
|
|
77
|
-
<...>
|
|
78
|
-
<0x00000000, CKBFS_CONTENT_BYTES_PART_TAIL>
|
|
79
|
-
<...>
|
|
80
|
-
Inputs:
|
|
81
|
-
<...>
|
|
82
|
-
Outputs:
|
|
83
|
-
<vec> CKBFS_V3_CELL
|
|
84
|
-
Data:
|
|
85
|
-
content-type: string
|
|
86
|
-
filename: string
|
|
87
|
-
indexes: uint32
|
|
88
|
-
checksum: uint32
|
|
89
|
-
Type:
|
|
90
|
-
code_hash: ckbfs v3 type script
|
|
91
|
-
args: 32 bytes type_id, (...)
|
|
92
|
-
<...>
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Publish operation must satisfy following rule:
|
|
96
|
-
|
|
97
|
-
- Rule 11: in a publish operation, checksum in cell data must be equal with `hash(Witnesses[ALL_CONTENT_PARTS])`.
|
|
98
|
-
- Rule 12: Previous position value, previous checksum value should be all zero in `Head Witnesses`
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
#### Append
|
|
104
|
-
|
|
105
|
-
Append operation updates exist live CKBFS v3 cell, validates the latest checksum.
|
|
106
|
-
|
|
107
|
-
```yaml
|
|
108
|
-
// Append
|
|
109
|
-
Witnesses:
|
|
110
|
-
<...>
|
|
111
|
-
<0x434b424653, 0x03, PREVIOUS_TX_HASH_VALUE, PREVIOUS_INDEX_IN_CKBFS_V3_CELL, PREIVOUS_CHECKSUM, 0x00000002,CKBFS_CONTENT_BYTES_PART_1>
|
|
112
|
-
<0x00000003, CKBFS_CONTENT_BYTES_PART_2>
|
|
113
|
-
<...>
|
|
114
|
-
<0x00000000, CKBFS_CONTENT_BYTES_PART_TAIL>
|
|
115
|
-
<...>
|
|
116
|
-
Inputs:
|
|
117
|
-
<...>
|
|
118
|
-
CKBFS_V3_CELL
|
|
119
|
-
Data:
|
|
120
|
-
content-type: string
|
|
121
|
-
filename: string
|
|
122
|
-
index: uint32
|
|
123
|
-
checksum: uint32 # previous checksum
|
|
124
|
-
Type:
|
|
125
|
-
code_hash: ckbfs v3 type script
|
|
126
|
-
args: 32 bytes type_id, (...)
|
|
127
|
-
<...>
|
|
128
|
-
Outputs:
|
|
129
|
-
<...>
|
|
130
|
-
CKBFS_V3_CELL:
|
|
131
|
-
Data:
|
|
132
|
-
content-type: string
|
|
133
|
-
filename: string
|
|
134
|
-
index: uint32
|
|
135
|
-
checksum: uint32 # updated checksum
|
|
136
|
-
Type:
|
|
137
|
-
code_hash: ckbfs v3 type script
|
|
138
|
-
args: 32 bytes type_id, (...)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
- Rule 13: new checksum of updated CKBFS cell should be equal to: `hasher.recover_from(previous_checksum).update(new_content_bytes)`
|
|
142
|
-
- Rule 14: `content-type`, `filename`, and Type args of a CKBFS cell CAN NOT be updated in ANY condition
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
#### Transfer
|
|
148
|
-
|
|
149
|
-
Transfer operation transfers ownership of a CKBFS cell, and ensure it did not lost tracking of backlinks.
|
|
150
|
-
|
|
151
|
-
```yaml
|
|
152
|
-
// Transfer
|
|
153
|
-
Witnesses:
|
|
154
|
-
<...>
|
|
155
|
-
<0x434b424653, 0x03, PREVIOUS_TX_HASH_VALUE, PREVIOUS_INDEX_IN_CKBFS_V3_CELL, PREIVOUS_CHECKSUM, 0x00000000>
|
|
156
|
-
<...>
|
|
157
|
-
|
|
158
|
-
Inputs:
|
|
159
|
-
<...>
|
|
160
|
-
CKBFS_V3_CELL
|
|
161
|
-
Data:
|
|
162
|
-
content-type: string
|
|
163
|
-
filename: string
|
|
164
|
-
index: uint32
|
|
165
|
-
checksum: uint32
|
|
166
|
-
Type:
|
|
167
|
-
code_hash: ckbfs type script
|
|
168
|
-
args: 32 bytes type_id, (...)
|
|
169
|
-
Lock:
|
|
170
|
-
<USER_DEFINED>
|
|
171
|
-
<...>
|
|
172
|
-
Outputs:
|
|
173
|
-
<...>
|
|
174
|
-
CKBFS_V3_CELL:
|
|
175
|
-
Data:
|
|
176
|
-
content-type: string
|
|
177
|
-
filename: string
|
|
178
|
-
index: uint32
|
|
179
|
-
checksum: uint32
|
|
180
|
-
Type:
|
|
181
|
-
code_hash: ckbfs type script
|
|
182
|
-
args: 32 bytes type_id, (...)
|
|
183
|
-
Lock:
|
|
184
|
-
<USER_DEFINED>
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
- Rule 15: The `Head Witness` should not contain any content part bytes
|
|
188
|
-
- Rule 16: in a transfer operation, `checksum` CAN NOT be updated
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## Other Notes
|
|
194
|
-
|
|
195
|
-
### Molecule Definitions:
|
|
196
|
-
|
|
197
|
-
Here’s molecule definitions of CKBFS data structures
|
|
198
|
-
|
|
199
|
-
```jsx
|
|
200
|
-
vector Bytes <byte>;
|
|
201
|
-
option BytesOpt (Bytes);
|
|
202
|
-
option Uint32Opt (Uint32);
|
|
203
|
-
|
|
204
|
-
table CKBFSData {
|
|
205
|
-
index: Uint32,
|
|
206
|
-
checksum: Uint32,
|
|
207
|
-
content_type: Bytes,
|
|
208
|
-
filename: Bytes,
|
|
209
|
-
}
|
|
210
|
-
```
|
package/code.png
DELETED
|
Binary file
|
package/demo-output.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Hollo, WKBFS! This is a test file. More content in second chunk. Last churk of the test file.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Hello CKBFS from direct content!
|
package/examples/nervape.ts
DELETED
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import fs from "fs"
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import {
|
|
4
|
-
CKBFS,
|
|
5
|
-
NetworkType,
|
|
6
|
-
CKBFSDataType,
|
|
7
|
-
ProtocolVersion,
|
|
8
|
-
CKBFSData,
|
|
9
|
-
getFileContentFromChain,
|
|
10
|
-
getCKBFSScriptConfig,
|
|
11
|
-
extractCKBFSV3WitnessContent,
|
|
12
|
-
isCKBFSV3Witness,
|
|
13
|
-
resolveCKBFSCell,
|
|
14
|
-
} from '../src';
|
|
15
|
-
import {
|
|
16
|
-
Script,
|
|
17
|
-
Address,
|
|
18
|
-
ClientPublicTestnet,
|
|
19
|
-
ClientPublicMainnet,
|
|
20
|
-
Transaction,
|
|
21
|
-
ccc,
|
|
22
|
-
SignerCkbPrivateKey,
|
|
23
|
-
fixedPointFrom,
|
|
24
|
-
Signer
|
|
25
|
-
} from "@ckb-ccc/core";
|
|
26
|
-
import {
|
|
27
|
-
transferSpore
|
|
28
|
-
} from "@ckb-ccc/spore";
|
|
29
|
-
//import { isMainnet, network, CKB_PRIVATE_KEY } from "../config"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const client = new ClientPublicTestnet()
|
|
33
|
-
|
|
34
|
-
export const TestnetTraceLockDep = {
|
|
35
|
-
outPoint: {
|
|
36
|
-
txHash: "0x7a3e492188438ca4b260fed0f7a00d18ec9e4671a8c3b54fc2b3b49447ab2e2f",
|
|
37
|
-
index: 0x0,
|
|
38
|
-
},
|
|
39
|
-
depType: "code",
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const MainnetTraceLockDep = {
|
|
43
|
-
outPoint: {
|
|
44
|
-
txHash: "0x...", //TODO: fill mainnet tracelock txhash
|
|
45
|
-
index: 0x0,
|
|
46
|
-
},
|
|
47
|
-
depType: "code",
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const TestnetShadowDep = {
|
|
51
|
-
outPoint: {
|
|
52
|
-
txHash: "0x2d8a311a55e42d7d6810610149f6e125f0d292112f8ed4a21177aec05da2b905",
|
|
53
|
-
index: 0x0,
|
|
54
|
-
},
|
|
55
|
-
depType: "code",
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export const MainnetShadowDep = {
|
|
59
|
-
outPoint: {
|
|
60
|
-
txHash: "0x5b20ec7d7d5c6410ac4924c57d1eac1dd50129b6263be3dc93055397b5724dc2",
|
|
61
|
-
index: 0x0,
|
|
62
|
-
},
|
|
63
|
-
depType: "code",
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const TraceLockDep = TestnetTraceLockDep
|
|
68
|
-
const ShadowDep = TestnetShadowDep
|
|
69
|
-
|
|
70
|
-
const TraceLogCodeHash = "0xbd7e56daaa4cc9ecf488a50d2f4db2a55a7c86eb2df2157d24a62be9be5b34ab"
|
|
71
|
-
const ShadowSporeCodeHash = "0x6361d4b20d845953d9c9431bbba08905573005a71e2a2432e7e0e7c685666f24"
|
|
72
|
-
const CKB_PRIVATE_KEY = "0x620ab1e4273b937032380e9f851c3119427ba965805bebcdce60a4494fd3074d"
|
|
73
|
-
|
|
74
|
-
const ckbfs = new CKBFS(
|
|
75
|
-
CKB_PRIVATE_KEY,
|
|
76
|
-
NetworkType.Testnet,
|
|
77
|
-
{
|
|
78
|
-
version: ProtocolVersion.V3,
|
|
79
|
-
chunkSize: 30 * 1024, // 30KB chunks
|
|
80
|
-
useTypeID: false // Use code hash instead of type ID
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
async function getAddressScriptHash(addr: string | Address) {
|
|
85
|
-
if (typeof addr === 'string') {
|
|
86
|
-
addr = await Address.fromString(addr, client)
|
|
87
|
-
console.log("addr=", addr)
|
|
88
|
-
}
|
|
89
|
-
return `0x03${addr.script.hash().slice(2)}`
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export async function mint(physicalArtifactNo: string) {
|
|
93
|
-
// Get address info
|
|
94
|
-
const address = await ckbfs.getAddress();
|
|
95
|
-
const ownerHash = await getAddressScriptHash(address)
|
|
96
|
-
|
|
97
|
-
const content = `MINT,TO:${ownerHash}\n`
|
|
98
|
-
|
|
99
|
-
// You can provide additional options
|
|
100
|
-
const options = {
|
|
101
|
-
contentType: 'text/plain',
|
|
102
|
-
filename: `${physicalArtifactNo}.LOGS`,
|
|
103
|
-
};
|
|
104
|
-
const tx = await ckbfs.createPublishContentTransaction(content, options);
|
|
105
|
-
const signer = ckbfs['signer']
|
|
106
|
-
const txHash = await signer.sendTransaction(tx)
|
|
107
|
-
const ckbfsId = tx.outputs[0].type?.args.toString()
|
|
108
|
-
return { txHash, log: content, ckbfsId };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export async function claim(to: string, sporeId: string, ckbfsId: string) {
|
|
112
|
-
const from = await ckbfs.getAddress();
|
|
113
|
-
const ckbfsCell = await getCkbfsCellInfo(ckbfsId);
|
|
114
|
-
const fromHash = await getAddressScriptHash(from)
|
|
115
|
-
const toHash = await getAddressScriptHash(to)
|
|
116
|
-
|
|
117
|
-
const content = `TRANSFER,FROM:${fromHash},TO:${toHash}\n`
|
|
118
|
-
|
|
119
|
-
let tx = await ckbfs.createAppendContentTransaction(content, ckbfsCell);
|
|
120
|
-
tx.addCellDeps(TraceLockDep)
|
|
121
|
-
|
|
122
|
-
const sizeBefore = tx.outputs[0].lock.occupiedSize
|
|
123
|
-
const traceLogLock = Script.from({
|
|
124
|
-
codeHash: TraceLogCodeHash,
|
|
125
|
-
hashType: "type",
|
|
126
|
-
args: toHash
|
|
127
|
-
})
|
|
128
|
-
tx.outputs[0].lock = traceLogLock
|
|
129
|
-
const sizeAfter = traceLogLock.occupiedSize
|
|
130
|
-
|
|
131
|
-
const diffSize = BigInt(sizeAfter - sizeBefore) * BigInt(100000000)
|
|
132
|
-
tx.outputs[0].capacity = tx.outputs[0].capacity + diffSize
|
|
133
|
-
|
|
134
|
-
const sporeLockArgs = `0x01${ tx.outputs[0].type?.hash().slice(2) }`
|
|
135
|
-
const newSporeLock = Script.from({
|
|
136
|
-
codeHash: ShadowSporeCodeHash,
|
|
137
|
-
args: sporeLockArgs,
|
|
138
|
-
hashType: "data1",
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
tx.outputs = [
|
|
142
|
-
tx.outputs[0]
|
|
143
|
-
]
|
|
144
|
-
|
|
145
|
-
const signer = ckbfs['signer'] as any
|
|
146
|
-
const sporeTx = await transferSpore({
|
|
147
|
-
signer,
|
|
148
|
-
id: sporeId,
|
|
149
|
-
to: newSporeLock,
|
|
150
|
-
tx: tx,
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
tx.addCellDeps(ShadowDep)
|
|
154
|
-
|
|
155
|
-
const finalTx = sporeTx.tx
|
|
156
|
-
await finalTx.completeFeeBy(signer)
|
|
157
|
-
const txHash = await signer.sendTransaction(finalTx)
|
|
158
|
-
console.log(`File appended successfully! Transaction Hash: ${txHash}`);
|
|
159
|
-
return { txHash, log: content }
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export async function giveName(name: string, ckbfsId: string) {
|
|
163
|
-
const ckbfsCell = await getCkbfsCellInfo(ckbfsId)
|
|
164
|
-
const content = `GIVE_NAME,NEW_NAME:${name}\n`
|
|
165
|
-
|
|
166
|
-
console.log("ckbfsCell=", ckbfsCell)
|
|
167
|
-
|
|
168
|
-
const signer = ckbfs['signer']
|
|
169
|
-
const address = await signer.getRecommendedAddress()
|
|
170
|
-
console.log("address=", address)
|
|
171
|
-
const addressHash = await getAddressScriptHash(address)
|
|
172
|
-
console.log("addressHash=", addressHash)
|
|
173
|
-
let tx = await ckbfs.createAppendContentTransaction(content, ckbfsCell, {
|
|
174
|
-
witnessStartIndex: 1,
|
|
175
|
-
});
|
|
176
|
-
tx.addCellDeps(TestnetTraceLockDep)
|
|
177
|
-
// console.log("tx=", JSON.stringify(JSON.parse(tx.stringify()), null, 2))
|
|
178
|
-
const txHash = await signer.sendTransaction(tx)
|
|
179
|
-
console.log(`File appended successfully! Transaction Hash: ${txHash}`);
|
|
180
|
-
return { txHash, log: content }
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// export async function transfer(to: string, ckbfsId: string) {
|
|
184
|
-
// const ckbfsCell = await getCkbfsCellInfo(ckbfsId)
|
|
185
|
-
// const from = await ckbfs.getAddress();
|
|
186
|
-
// const fromHash = await getAddressScriptHash(from)
|
|
187
|
-
// const toHash = await getAddressScriptHash(to)
|
|
188
|
-
// const content = `TRANSFER,FROM:${fromHash},TO:${toHash}\n`
|
|
189
|
-
|
|
190
|
-
// const signer = ckbfs['signer']
|
|
191
|
-
// let tx = await ckbfs.createAppendContentTransaction(content, ckbfsCell);
|
|
192
|
-
// tx.outputs[0].lock.args = toHash as `0x{string}`;
|
|
193
|
-
// tx.addCellDeps(TestnetTraceLockDep)
|
|
194
|
-
// const txHash = await signer.sendTransaction(tx)
|
|
195
|
-
// console.log(`File appended successfully! Transaction Hash: ${txHash}`);
|
|
196
|
-
|
|
197
|
-
// return { txHash, log: content };
|
|
198
|
-
// }
|
|
199
|
-
|
|
200
|
-
// export async function release(from: string, ckbfsId: string) {
|
|
201
|
-
// const ckbfsCell = await await getCkbfsCellInfo(ckbfsId)
|
|
202
|
-
// const fromHash = await getAddressScriptHash(from)
|
|
203
|
-
// const content = `RELEASE,FROM:${fromHash}\n`
|
|
204
|
-
// const signer = ckbfs['signer']
|
|
205
|
-
// let tx = await ckbfs.createAppendContentTransaction(content, ckbfsCell);
|
|
206
|
-
|
|
207
|
-
// tx.addCellDeps(TestnetTraceLockDep)
|
|
208
|
-
// const txHash = await signer.sendTransaction(tx)
|
|
209
|
-
// console.log(`File appended successfully! Transaction Hash: ${txHash}`);
|
|
210
|
-
// return { txHash, log: content };
|
|
211
|
-
// }
|
|
212
|
-
|
|
213
|
-
// export async function retrieveLogsFromChain(ckbfsId: string) {
|
|
214
|
-
// const { outPoint, data } = await getCkbfsCellInfo(ckbfsId)
|
|
215
|
-
// const content = await getFileContentFromChain(client, outPoint, data)
|
|
216
|
-
// const logs = content.toString().trim().split("\n")
|
|
217
|
-
// return logs
|
|
218
|
-
// }
|
|
219
|
-
|
|
220
|
-
export async function getCkbfsCellInfo(ckbfsId: string) {
|
|
221
|
-
const { codeHash, hashType } = getCKBFSScriptConfig(NetworkType.Testnet, ProtocolVersion.V3)
|
|
222
|
-
console.log("codeHash, hashType=", codeHash, hashType, ckbfsId)
|
|
223
|
-
const cell = await client.findSingletonCellByType({
|
|
224
|
-
codeHash,
|
|
225
|
-
hashType,
|
|
226
|
-
args: ckbfsId
|
|
227
|
-
}, true)
|
|
228
|
-
|
|
229
|
-
if (!cell || !cell.cellOutput.type) {
|
|
230
|
-
throw new Error('No CKBFS cell found');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const rawData = cell.outputData.startsWith('0x')
|
|
234
|
-
? Buffer.from(cell.outputData.slice(2), 'hex')
|
|
235
|
-
: Buffer.from(cell.outputData, 'hex');
|
|
236
|
-
|
|
237
|
-
const ckbfsData = CKBFSData.unpack(rawData, ProtocolVersion.V3)
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
outPoint: { txHash: cell.outPoint.txHash as string, index: Number(cell.outPoint.index) },
|
|
241
|
-
type: cell.cellOutput.type,
|
|
242
|
-
lock: cell.cellOutput.lock,
|
|
243
|
-
capacity: cell.cellOutput.capacity,
|
|
244
|
-
data: ckbfsData,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
export function getCkbfsList() {
|
|
250
|
-
const file = path.join(__dirname, `./data/${'testnet'}/ckbfs-list.json`)
|
|
251
|
-
return JSON.parse(fs.readFileSync(file).toString())
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export async function mintAll() {
|
|
255
|
-
const ckbfsList = getCkbfsList()
|
|
256
|
-
for(const { no } of ckbfsList) {
|
|
257
|
-
const { txHash, ckbfsId } = await mint(no)
|
|
258
|
-
console.log(`minted ckbfs no=${no}, ckbfsId=${ckbfsId}, txHash=${txHash}`);
|
|
259
|
-
|
|
260
|
-
const interval = setInterval(async () => {
|
|
261
|
-
try {
|
|
262
|
-
const tx = await client.getTransaction(txHash)
|
|
263
|
-
if (tx && tx.status === 'committed') {
|
|
264
|
-
clearInterval(interval)
|
|
265
|
-
console.info(`tx confirmed, https://explorer.nervos.org/transaction/${txHash}`);
|
|
266
|
-
}
|
|
267
|
-
} catch (error) {
|
|
268
|
-
console.error(error)
|
|
269
|
-
}
|
|
270
|
-
}, 5 * 1000)
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
async function getCellInfoFromV3Transaction(txHash: string): Promise<{
|
|
275
|
-
outPoint: { txHash: string; index: number };
|
|
276
|
-
type: Script;
|
|
277
|
-
data: CKBFSDataType;
|
|
278
|
-
lock: Script;
|
|
279
|
-
capacity: bigint;
|
|
280
|
-
previousTxHash: string;
|
|
281
|
-
previousWitnessIndex: number;
|
|
282
|
-
previousChecksum: number;
|
|
283
|
-
}> {
|
|
284
|
-
console.log(`Retrieving v3 transaction data for: ${txHash}`);
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
// Get transaction from RPC
|
|
288
|
-
const txWithStatus = await client.getTransaction(txHash);
|
|
289
|
-
if (!txWithStatus || !txWithStatus.transaction) {
|
|
290
|
-
throw new Error(`Transaction ${txHash} not found`);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const tx = Transaction.from(txWithStatus.transaction);
|
|
294
|
-
console.log(`Transaction found with ${tx.outputs.length} outputs`);
|
|
295
|
-
|
|
296
|
-
// Find the CKBFS cell output (first output with type script)
|
|
297
|
-
let ckbfsCellIndex = 0;
|
|
298
|
-
const output = tx.outputs[ckbfsCellIndex];
|
|
299
|
-
if (!output || !output.type) {
|
|
300
|
-
throw new Error('No CKBFS cell found in the transaction');
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
console.log(`Found CKBFS v3 cell at index ${ckbfsCellIndex}`);
|
|
304
|
-
console.log(`Cell type script hash: ${output.type.hash()}`);
|
|
305
|
-
|
|
306
|
-
// Get output data
|
|
307
|
-
const outputData = tx.outputsData[ckbfsCellIndex];
|
|
308
|
-
if (!outputData) {
|
|
309
|
-
throw new Error('Output data not found');
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Parse the output data as CKBFS v3 data
|
|
313
|
-
const rawData = outputData.startsWith('0x')
|
|
314
|
-
? ccc.bytesFrom(outputData.slice(2), 'hex')
|
|
315
|
-
: Buffer.from(outputData, 'hex');
|
|
316
|
-
|
|
317
|
-
// Unpack the raw data using v3 format
|
|
318
|
-
const version = ProtocolVersion.V3;
|
|
319
|
-
console.log(`Using protocol version ${version} for unpacking v3 cell data`);
|
|
320
|
-
|
|
321
|
-
let ckbfsData: CKBFSDataType;
|
|
322
|
-
try {
|
|
323
|
-
ckbfsData = CKBFSData.unpack(rawData, version);
|
|
324
|
-
|
|
325
|
-
console.log('Successfully unpacked CKBFS v3 cell data:');
|
|
326
|
-
console.log(`- Checksum: ${ckbfsData.checksum}`);
|
|
327
|
-
console.log(`- File: ${ckbfsData.filename}`);
|
|
328
|
-
console.log(`- Content Type: ${ckbfsData.contentType}`);
|
|
329
|
-
console.log(`- Index: ${ckbfsData.index}`);
|
|
330
|
-
} catch (error) {
|
|
331
|
-
console.error('Error unpacking CKBFS v3 data:', error);
|
|
332
|
-
throw new Error(`Failed to unpack CKBFS v3 data: ${error}`);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Extract backlink information from v3 witness
|
|
336
|
-
let previousTxHash = '0x' + '00'.repeat(32);
|
|
337
|
-
let previousWitnessIndex = 0;
|
|
338
|
-
let previousChecksum = 0;
|
|
339
|
-
|
|
340
|
-
if (ckbfsData.index !== undefined && ckbfsData.index < tx.witnesses.length) {
|
|
341
|
-
const witnessHex = tx.witnesses[ckbfsData.index];
|
|
342
|
-
const witness = Buffer.from(witnessHex.slice(2), 'hex');
|
|
343
|
-
|
|
344
|
-
if (isCKBFSV3Witness(witness)) {
|
|
345
|
-
try {
|
|
346
|
-
const witnessData = extractCKBFSV3WitnessContent(witness, true);
|
|
347
|
-
previousTxHash = witnessData.previousTxHash || previousTxHash;
|
|
348
|
-
previousWitnessIndex = witnessData.previousWitnessIndex || 0;
|
|
349
|
-
previousChecksum = witnessData.previousChecksum || 0;
|
|
350
|
-
|
|
351
|
-
console.log('Extracted v3 backlink information:');
|
|
352
|
-
console.log(`- Previous TX Hash: ${previousTxHash}`);
|
|
353
|
-
console.log(`- Previous Witness Index: ${previousWitnessIndex}`);
|
|
354
|
-
console.log(`- Previous Checksum: ${previousChecksum}`);
|
|
355
|
-
} catch (error) {
|
|
356
|
-
console.warn('Could not extract backlink info from witness:', error);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return {
|
|
362
|
-
outPoint: {
|
|
363
|
-
txHash,
|
|
364
|
-
index: ckbfsCellIndex
|
|
365
|
-
},
|
|
366
|
-
type: output.type,
|
|
367
|
-
lock: output.lock,
|
|
368
|
-
capacity: output.capacity,
|
|
369
|
-
data: ckbfsData,
|
|
370
|
-
previousTxHash,
|
|
371
|
-
previousWitnessIndex,
|
|
372
|
-
previousChecksum
|
|
373
|
-
};
|
|
374
|
-
} catch (error) {
|
|
375
|
-
console.error('Error retrieving v3 transaction data:', error);
|
|
376
|
-
throw new Error(`Failed to retrieve or parse v3 cell data: ${error}`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
async function testReresolve(ckbfsId: string) {
|
|
381
|
-
const data = (await resolveCKBFSCell(client, ckbfsId, {network: NetworkType.Testnet}))!
|
|
382
|
-
return data
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// mintAll()
|
|
387
|
-
|
|
388
|
-
//mint("X30125NMNVAP000201").then(console.log)
|
|
389
|
-
|
|
390
|
-
//claim("ckt1qyq0zr47f0p8sm5d3cvtlpkljs7zvqcw7ngqnfscx0", "0xa3347c13f0419d9013a3f48a2b9ad993c2b6b5806827f3bf37480a58580d669d", "0x0084106b9bea08f512e35b725c4a710ebf09cb65c05601ebde8d911bc6c662bf")
|
|
391
|
-
|
|
392
|
-
// getCkbfsCellInfo("0xdb0b0b637060d2f6c42c4429e712fa043c1937968c03c54a5014b4595f88413a").then((data) => {
|
|
393
|
-
// console.log("ckbfs data=", data)
|
|
394
|
-
// console.log("mintAll done");
|
|
395
|
-
// })
|
|
396
|
-
|
|
397
|
-
//giveName("bbb", "0x0084106b9bea08f512e35b725c4a710ebf09cb65c05601ebde8d911bc6c662bf").then(console.log)
|
|
398
|
-
|
|
399
|
-
// getCkbfsCellInfo("0x52315569e5ff0d6d6392f047fcbea97bae1b32b688c29aca5251a157157278d3").then((data) => {
|
|
400
|
-
// console.log("ckbfs data=", data)
|
|
401
|
-
// })
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
// getCellInfoFromV3Transaction("0x54c86dbdc6bdafb4616221d54cfc0ff186a4a772eba509838684f8e1ea271b40").then(console.log)
|
|
405
|
-
|
|
406
|
-
//getCkbfsCellInfo("0xe58baa82c4844add60911bb890487fea3c45436b9d7b203cc1065301d0a9c503").then(console.log)
|
|
407
|
-
giveName("bbb", "0xe58baa82c4844add60911bb890487fea3c45436b9d7b203cc1065301d0a9c503").then(console.log)
|
|
408
|
-
|
|
409
|
-
// testReresolve("0x0084106b9bea08f512e35b725c4a710ebf09cb65c05601ebde8d911bc6c662bf").then(console.log)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Hello CKBFS from direct content!
|
package/small-example.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
This is a small test file that should be published directly.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Hello CKBFS from direct content!
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Hello CKBFS from direct content!
|