@ckb-ccc/core 1.4.0 → 1.6.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/ckb/transaction.d.ts +57 -4
  3. package/dist/ckb/transaction.d.ts.map +1 -1
  4. package/dist/ckb/transaction.js +83 -26
  5. package/dist/client/cache/cache.d.ts +67 -11
  6. package/dist/client/cache/cache.d.ts.map +1 -1
  7. package/dist/client/cache/cache.js +94 -9
  8. package/dist/client/cache/memory.advanced.d.ts +2 -2
  9. package/dist/client/cache/memory.advanced.d.ts.map +1 -1
  10. package/dist/client/cache/memory.advanced.js +19 -7
  11. package/dist/client/cache/memory.d.ts +24 -7
  12. package/dist/client/cache/memory.d.ts.map +1 -1
  13. package/dist/client/cache/memory.js +76 -18
  14. package/dist/client/client.d.ts +27 -6
  15. package/dist/client/client.d.ts.map +1 -1
  16. package/dist/client/client.js +113 -22
  17. package/dist/client/clientPublicMainnet.d.ts.map +1 -1
  18. package/dist/client/clientPublicMainnet.js +13 -2
  19. package/dist/client/clientPublicTestnet.d.ts.map +1 -1
  20. package/dist/client/clientPublicTestnet.js +13 -2
  21. package/dist/client/clientTypes.advanced.d.ts +3 -0
  22. package/dist/client/clientTypes.advanced.d.ts.map +1 -1
  23. package/dist/client/clientTypes.advanced.js +3 -0
  24. package/dist/client/clientTypes.d.ts +110 -13
  25. package/dist/client/clientTypes.d.ts.map +1 -1
  26. package/dist/client/clientTypes.js +118 -4
  27. package/dist/client/jsonRpc/client.d.ts +4 -4
  28. package/dist/client/jsonRpc/client.d.ts.map +1 -1
  29. package/dist/client/jsonRpc/client.js +4 -4
  30. package/dist/client/jsonRpc/transformers.js +3 -3
  31. package/dist/jsonRpc/requestor.d.ts +1 -0
  32. package/dist/jsonRpc/requestor.d.ts.map +1 -1
  33. package/dist/jsonRpc/requestor.js +4 -2
  34. package/dist/jsonRpc/transports/advanced.d.ts +1 -0
  35. package/dist/jsonRpc/transports/advanced.d.ts.map +1 -1
  36. package/dist/jsonRpc/transports/advanced.js +1 -0
  37. package/dist/jsonRpc/transports/fallback.d.ts +8 -0
  38. package/dist/jsonRpc/transports/fallback.d.ts.map +1 -0
  39. package/dist/jsonRpc/transports/fallback.js +22 -0
  40. package/dist/jsonRpc/transports/webSocket.d.ts.map +1 -1
  41. package/dist/jsonRpc/transports/webSocket.js +7 -3
  42. package/dist/molecule/predefined.d.ts +10 -10
  43. package/dist/molecule/predefined.d.ts.map +1 -1
  44. package/dist/utils/index.d.ts +4 -0
  45. package/dist/utils/index.d.ts.map +1 -1
  46. package/dist/utils/index.js +13 -1
  47. package/dist.commonjs/advanced.js +17 -7
  48. package/dist.commonjs/ckb/transaction.d.ts +57 -4
  49. package/dist.commonjs/ckb/transaction.d.ts.map +1 -1
  50. package/dist.commonjs/ckb/transaction.js +83 -26
  51. package/dist.commonjs/client/cache/cache.d.ts +67 -11
  52. package/dist.commonjs/client/cache/cache.d.ts.map +1 -1
  53. package/dist.commonjs/client/cache/cache.js +94 -9
  54. package/dist.commonjs/client/cache/memory.advanced.d.ts +2 -2
  55. package/dist.commonjs/client/cache/memory.advanced.d.ts.map +1 -1
  56. package/dist.commonjs/client/cache/memory.advanced.js +19 -7
  57. package/dist.commonjs/client/cache/memory.d.ts +24 -7
  58. package/dist.commonjs/client/cache/memory.d.ts.map +1 -1
  59. package/dist.commonjs/client/cache/memory.js +75 -17
  60. package/dist.commonjs/client/client.d.ts +27 -6
  61. package/dist.commonjs/client/client.d.ts.map +1 -1
  62. package/dist.commonjs/client/client.js +111 -20
  63. package/dist.commonjs/client/clientPublicMainnet.d.ts.map +1 -1
  64. package/dist.commonjs/client/clientPublicMainnet.js +13 -2
  65. package/dist.commonjs/client/clientPublicTestnet.d.ts.map +1 -1
  66. package/dist.commonjs/client/clientPublicTestnet.js +13 -2
  67. package/dist.commonjs/client/clientTypes.advanced.d.ts +3 -0
  68. package/dist.commonjs/client/clientTypes.advanced.d.ts.map +1 -1
  69. package/dist.commonjs/client/clientTypes.advanced.js +4 -0
  70. package/dist.commonjs/client/clientTypes.d.ts +110 -13
  71. package/dist.commonjs/client/clientTypes.d.ts.map +1 -1
  72. package/dist.commonjs/client/clientTypes.js +123 -4
  73. package/dist.commonjs/client/jsonRpc/client.d.ts +4 -4
  74. package/dist.commonjs/client/jsonRpc/client.d.ts.map +1 -1
  75. package/dist.commonjs/client/jsonRpc/client.js +4 -4
  76. package/dist.commonjs/client/jsonRpc/transformers.js +2 -2
  77. package/dist.commonjs/index.js +17 -7
  78. package/dist.commonjs/jsonRpc/requestor.d.ts +1 -0
  79. package/dist.commonjs/jsonRpc/requestor.d.ts.map +1 -1
  80. package/dist.commonjs/jsonRpc/requestor.js +3 -1
  81. package/dist.commonjs/jsonRpc/transports/advanced.d.ts +1 -0
  82. package/dist.commonjs/jsonRpc/transports/advanced.d.ts.map +1 -1
  83. package/dist.commonjs/jsonRpc/transports/advanced.js +1 -0
  84. package/dist.commonjs/jsonRpc/transports/fallback.d.ts +8 -0
  85. package/dist.commonjs/jsonRpc/transports/fallback.d.ts.map +1 -0
  86. package/dist.commonjs/jsonRpc/transports/fallback.js +26 -0
  87. package/dist.commonjs/jsonRpc/transports/webSocket.d.ts.map +1 -1
  88. package/dist.commonjs/jsonRpc/transports/webSocket.js +7 -3
  89. package/dist.commonjs/molecule/index.js +17 -7
  90. package/dist.commonjs/utils/index.d.ts +4 -0
  91. package/dist.commonjs/utils/index.d.ts.map +1 -1
  92. package/dist.commonjs/utils/index.js +13 -0
  93. package/package.json +1 -1
  94. package/src/ckb/transaction.ts +112 -41
  95. package/src/client/cache/cache.ts +126 -13
  96. package/src/client/cache/memory.advanced.ts +24 -7
  97. package/src/client/cache/memory.ts +130 -30
  98. package/src/client/client.ts +170 -27
  99. package/src/client/clientPublicMainnet.ts +14 -2
  100. package/src/client/clientPublicTestnet.ts +14 -2
  101. package/src/client/clientTypes.advanced.ts +4 -0
  102. package/src/client/clientTypes.ts +224 -33
  103. package/src/client/jsonRpc/client.ts +11 -9
  104. package/src/client/jsonRpc/transformers.ts +2 -2
  105. package/src/jsonRpc/requestor.ts +10 -1
  106. package/src/jsonRpc/transports/advanced.ts +1 -0
  107. package/src/jsonRpc/transports/fallback.ts +27 -0
  108. package/src/jsonRpc/transports/webSocket.ts +17 -13
  109. package/src/utils/index.ts +14 -1
@@ -229,8 +229,12 @@ export const CellOutputVec = mol.vector(CellOutput);
229
229
  /**
230
230
  * @public
231
231
  */
232
- export type CellLike = {
233
- outPoint: OutPointLike;
232
+ export type CellLike = (
233
+ | {
234
+ outPoint: OutPointLike;
235
+ }
236
+ | { previousOutput: OutPointLike }
237
+ ) & {
234
238
  cellOutput: CellOutputLike;
235
239
  outputData: HexLike;
236
240
  };
@@ -265,7 +269,7 @@ export class Cell {
265
269
  }
266
270
 
267
271
  return new Cell(
268
- OutPoint.from(cell.outPoint),
272
+ OutPoint.from("outPoint" in cell ? cell.outPoint : cell.previousOutput),
269
273
  CellOutput.from(cell.cellOutput),
270
274
  hexFrom(cell.outputData),
271
275
  );
@@ -450,8 +454,12 @@ export class Since extends mol.Entity.Base<SinceLike, Since>() {
450
454
  /**
451
455
  * @public
452
456
  */
453
- export type CellInputLike = {
454
- previousOutput: OutPointLike;
457
+ export type CellInputLike = (
458
+ | {
459
+ previousOutput: OutPointLike;
460
+ }
461
+ | { outPoint: OutPointLike }
462
+ ) & {
455
463
  since?: SinceLike | NumLike | null;
456
464
  cellOutput?: CellOutputLike | null;
457
465
  outputData?: HexLike | null;
@@ -465,10 +473,7 @@ export type CellInputLike = {
465
473
  since: Since,
466
474
  previousOutput: OutPoint,
467
475
  })
468
- .mapIn((encodable: CellInputLike) => ({
469
- ...encodable,
470
- since: encodable.since ?? 0,
471
- })),
476
+ .mapIn((encodable: CellInputLike) => CellInput.from(encodable)),
472
477
  )
473
478
  export class CellInput extends mol.Entity.Base<CellInputLike, CellInput>() {
474
479
  /**
@@ -509,7 +514,11 @@ export class CellInput extends mol.Entity.Base<CellInputLike, CellInput>() {
509
514
  }
510
515
 
511
516
  return new CellInput(
512
- OutPoint.from(cellInput.previousOutput),
517
+ OutPoint.from(
518
+ "previousOutput" in cellInput
519
+ ? cellInput.previousOutput
520
+ : cellInput.outPoint,
521
+ ),
513
522
  Since.from(cellInput.since ?? 0).toNum(),
514
523
  apply(CellOutput.from, cellInput.cellOutput),
515
524
  apply(hexFrom, cellInput.outputData),
@@ -539,6 +548,14 @@ export class CellInput extends mol.Entity.Base<CellInputLike, CellInput>() {
539
548
  this.cellOutput = cell.cellOutput;
540
549
  this.outputData = cell.outputData;
541
550
  }
551
+
552
+ clone(): CellInput {
553
+ const cloned = super.clone();
554
+ cloned.cellOutput = this.cellOutput;
555
+ cloned.outputData = this.outputData;
556
+
557
+ return cloned;
558
+ }
542
559
  }
543
560
  export const CellInputVec = mol.vector(CellInput);
544
561
 
@@ -891,6 +908,11 @@ export class Transaction extends mol.Entity.Base<
891
908
  });
892
909
  }
893
910
 
911
+ /**
912
+ * @deprecated
913
+ * Use ccc.stringify instead.
914
+ * stringify the tx to JSON string.
915
+ */
894
916
  stringify(): string {
895
917
  return JSON.stringify(this, (_, value) => {
896
918
  if (typeof value === "bigint") {
@@ -1230,6 +1252,58 @@ export class Transaction extends mol.Entity.Base<
1230
1252
  this.outputsData[index] = hexFrom(witness);
1231
1253
  }
1232
1254
 
1255
+ /**
1256
+ * get input
1257
+ *
1258
+ * @param index - The cell input index
1259
+ *
1260
+ * @example
1261
+ * ```typescript
1262
+ * await tx.getInput(0);
1263
+ * ```
1264
+ */
1265
+ getInput(index: NumLike): CellInput | undefined {
1266
+ return this.inputs[Number(numFrom(index))];
1267
+ }
1268
+ /**
1269
+ * add input
1270
+ *
1271
+ * @param inputLike - The cell input.
1272
+ *
1273
+ * @example
1274
+ * ```typescript
1275
+ * await tx.addInput({ });
1276
+ * ```
1277
+ */
1278
+ addInput(inputLike: CellInputLike): number {
1279
+ return this.inputs.push(CellInput.from(inputLike));
1280
+ }
1281
+
1282
+ /**
1283
+ * get output
1284
+ *
1285
+ * @param index - The cell output index
1286
+ *
1287
+ * @example
1288
+ * ```typescript
1289
+ * await tx.getOutput(0);
1290
+ * ```
1291
+ */
1292
+ getOutput(index: NumLike):
1293
+ | {
1294
+ cellOutput: CellOutput;
1295
+ outputData: Hex;
1296
+ }
1297
+ | undefined {
1298
+ const i = Number(numFrom(index));
1299
+ if (i >= this.outputs.length) {
1300
+ return;
1301
+ }
1302
+ return {
1303
+ cellOutput: this.outputs[i],
1304
+ outputData: this.outputsData[i] ?? "0x",
1305
+ };
1306
+ }
1233
1307
  /**
1234
1308
  * Add output
1235
1309
  *
@@ -1245,7 +1319,7 @@ export class Transaction extends mol.Entity.Base<
1245
1319
  outputLike: Omit<CellOutputLike, "capacity"> &
1246
1320
  Partial<Pick<CellOutputLike, "capacity">>,
1247
1321
  outputData: HexLike = "0x",
1248
- ): void {
1322
+ ): number {
1249
1323
  const output = CellOutput.from({
1250
1324
  ...outputLike,
1251
1325
  capacity: outputLike.capacity ?? 0,
@@ -1255,8 +1329,10 @@ export class Transaction extends mol.Entity.Base<
1255
1329
  output.occupiedSize + bytesFrom(outputData).length,
1256
1330
  );
1257
1331
  }
1258
- const i = this.outputs.push(output) - 1;
1259
- this.setOutputDataAt(i, outputData);
1332
+ const len = this.outputs.push(output);
1333
+ this.setOutputDataAt(len - 1, outputData);
1334
+
1335
+ return len;
1260
1336
  }
1261
1337
 
1262
1338
  /**
@@ -1415,15 +1491,7 @@ export class Transaction extends mol.Entity.Base<
1415
1491
  acc = next;
1416
1492
  }
1417
1493
 
1418
- this.inputs.push(
1419
- ...collectedCells.map(({ outPoint, outputData, cellOutput }) =>
1420
- CellInput.from({
1421
- previousOutput: outPoint,
1422
- outputData,
1423
- cellOutput,
1424
- }),
1425
- ),
1426
- );
1494
+ collectedCells.forEach((cell) => this.addInput(cell));
1427
1495
  if (fulfilled) {
1428
1496
  return {
1429
1497
  addedCount: collectedCells.length,
@@ -1543,13 +1611,7 @@ export class Transaction extends mol.Entity.Base<
1543
1611
  continue;
1544
1612
  }
1545
1613
 
1546
- this.inputs.push(
1547
- CellInput.from({
1548
- previousOutput: cell.outPoint,
1549
- outputData: cell.outputData,
1550
- cellOutput: cell.cellOutput,
1551
- }),
1552
- );
1614
+ this.addInput(cell);
1553
1615
  return 1;
1554
1616
  }
1555
1617
 
@@ -1567,9 +1629,21 @@ export class Transaction extends mol.Entity.Base<
1567
1629
  return this.completeInputsAddOne(from, filter);
1568
1630
  }
1569
1631
 
1632
+ async fee(client: Client): Promise<Num> {
1633
+ return (await this.getInputsCapacity(client)) - this.getOutputsCapacity();
1634
+ }
1635
+
1636
+ async feeRate(client: Client): Promise<Num> {
1637
+ return (
1638
+ ((await this.fee(client)) * numFrom(1000)) /
1639
+ numFrom(this.toBytes().length + 4)
1640
+ );
1641
+ }
1642
+
1570
1643
  estimateFee(feeRate: NumLike): Num {
1571
1644
  const txSize = this.toBytes().length + 4;
1572
- return (numFrom(txSize) * numFrom(feeRate) + numFrom(1000)) / numFrom(1000);
1645
+ // + 999 then / 1000 to ceil the calculated fee
1646
+ return (numFrom(txSize) * numFrom(feeRate) + numFrom(999)) / numFrom(1000);
1573
1647
  }
1574
1648
 
1575
1649
  async completeFee(
@@ -1577,8 +1651,11 @@ export class Transaction extends mol.Entity.Base<
1577
1651
  change: (tx: Transaction, capacity: Num) => Promise<NumLike> | NumLike,
1578
1652
  expectedFeeRate?: NumLike,
1579
1653
  filter?: ClientCollectableSearchKeyFilterLike,
1654
+ options?: { feeRateBlockRange?: NumLike; maxFeeRate?: NumLike },
1580
1655
  ): Promise<[number, boolean]> {
1581
- const feeRate = expectedFeeRate ?? (await from.client.getFeeRate());
1656
+ const feeRate =
1657
+ expectedFeeRate ??
1658
+ (await from.client.getFeeRate(options?.feeRateBlockRange, options));
1582
1659
 
1583
1660
  // Complete all inputs extra infos for cache
1584
1661
  await this.getInputsCapacity(from.client);
@@ -1609,27 +1686,21 @@ export class Transaction extends mol.Entity.Base<
1609
1686
  // The initial fee is calculated based on prepared transaction
1610
1687
  leastFee = tx.estimateFee(feeRate);
1611
1688
  }
1612
- const extraCapacity =
1613
- (await tx.getInputsCapacity(from.client)) - tx.getOutputsCapacity();
1689
+ const fee = await tx.fee(from.client);
1614
1690
  // The extra capacity paid the fee without a change
1615
- if (extraCapacity === leastFee) {
1691
+ if (fee === leastFee) {
1616
1692
  this.copy(tx);
1617
1693
  return [collected, false];
1618
1694
  }
1619
1695
 
1620
- const needed = numFrom(
1621
- await Promise.resolve(change(tx, extraCapacity - leastFee)),
1622
- );
1696
+ const needed = numFrom(await Promise.resolve(change(tx, fee - leastFee)));
1623
1697
  // No enough extra capacity to create new cells for change
1624
1698
  if (needed > Zero) {
1625
1699
  leastExtraCapacity = needed;
1626
1700
  continue;
1627
1701
  }
1628
1702
 
1629
- if (
1630
- (await tx.getInputsCapacity(from.client)) - tx.getOutputsCapacity() !==
1631
- leastFee
1632
- ) {
1703
+ if ((await tx.fee(from.client)) !== leastFee) {
1633
1704
  throw new Error(
1634
1705
  "The change function doesn't use all available capacity",
1635
1706
  );
@@ -6,10 +6,31 @@ import {
6
6
  TransactionLike,
7
7
  } from "../../ckb/index.js";
8
8
  import { HexLike } from "../../hex/index.js";
9
+ import { NumLike } from "../../num/index.js";
9
10
  import { ClientCollectableSearchKeyLike } from "../clientTypes.advanced.js";
11
+ import {
12
+ ClientBlock,
13
+ ClientBlockHeader,
14
+ ClientBlockHeaderLike,
15
+ ClientBlockLike,
16
+ ClientTransactionResponse,
17
+ ClientTransactionResponseLike,
18
+ } from "../clientTypes.js";
10
19
 
20
+ /**
21
+ * @public
22
+ * The ClientCache class is mainly designed for chained transactions.
23
+ * Consumed & Created cells are "marked" so they can be correctly handled when composing transactions.
24
+ * It also act as cache for rpc requests to reduce cost, but this is optional.
25
+ */
11
26
  export abstract class ClientCache {
12
- abstract markUsable(...cellLikes: (CellLike | CellLike[])[]): Promise<void>;
27
+ abstract markUsableNoCache(
28
+ ...cellLikes: (CellLike | CellLike[])[]
29
+ ): Promise<void>;
30
+ async markUsable(...cellLikes: (CellLike | CellLike[])[]): Promise<void> {
31
+ await this.recordCells(...cellLikes);
32
+ return this.markUsableNoCache(...cellLikes);
33
+ }
13
34
  abstract markUnusable(
14
35
  ...outPointLike: (OutPointLike | OutPointLike[])[]
15
36
  ): Promise<void>;
@@ -17,7 +38,12 @@ export abstract class ClientCache {
17
38
  ...transactionLike: (TransactionLike | TransactionLike[])[]
18
39
  ): Promise<void> {
19
40
  await Promise.all([
20
- this.recordTransactions(...transactionLike),
41
+ this.recordTransactionResponses(
42
+ transactionLike.flat().map((transaction) => ({
43
+ transaction: transaction,
44
+ status: "sent",
45
+ })),
46
+ ),
21
47
  ...transactionLike.flat().map((transactionLike) => {
22
48
  const tx = Transaction.from(transactionLike);
23
49
  const txHash = tx.hash();
@@ -42,34 +68,121 @@ export abstract class ClientCache {
42
68
  abstract findCells(
43
69
  filter: ClientCollectableSearchKeyLike,
44
70
  ): AsyncGenerator<Cell>;
71
+ abstract isUnusable(outPointLike: OutPointLike): Promise<boolean>;
72
+
73
+ // ======
74
+ // Following methods are for requests caching and optional.
75
+ // ======
76
+
77
+ /**
78
+ * Record known cells
79
+ * Implement this method to enable cells query caching
80
+ * @param _cells
81
+ */
82
+ async recordCells(..._cells: (CellLike | CellLike[])[]): Promise<void> {}
45
83
  /**
46
84
  * Get a known cell by out point
85
+ * Implement this method to enable cells query caching
47
86
  * @param _outPoint
48
87
  */
49
- abstract getCell(_outPoint: OutPointLike): Promise<Cell | undefined>;
50
- abstract isUnusable(outPointLike: OutPointLike): Promise<boolean>;
88
+ async getCell(_outPoint: OutPointLike): Promise<Cell | undefined> {
89
+ return;
90
+ }
51
91
 
52
92
  /**
53
- * Record known transactions
93
+ * Record known transaction responses.
54
94
  * Implement this method to enable transactions query caching
55
95
  * @param _transactions
56
96
  */
57
- async recordTransactions(
58
- ..._transactions: (TransactionLike | TransactionLike[])[]
97
+ async recordTransactionResponses(
98
+ ..._transactions: (
99
+ | ClientTransactionResponseLike
100
+ | ClientTransactionResponseLike[]
101
+ )[]
59
102
  ): Promise<void> {}
60
103
  /**
61
- * Get a known transaction by hash
104
+ * Get a known transaction response by hash
62
105
  * Implement this method to enable transactions query caching
63
106
  * @param _txHash
64
107
  */
65
- async getTransaction(_txHash: HexLike): Promise<Transaction | undefined> {
108
+ async getTransactionResponse(
109
+ _txHash: HexLike,
110
+ ): Promise<ClientTransactionResponse | undefined> {
111
+ return;
112
+ }
113
+ /**
114
+ * Record known transactions.
115
+ * @param transactions
116
+ */
117
+ async recordTransactions(
118
+ ...transactions: (TransactionLike | TransactionLike[])[]
119
+ ): Promise<void> {
120
+ return this.recordTransactionResponses(
121
+ transactions.flat().map((transaction) => ({
122
+ transaction,
123
+ status: "unknown",
124
+ })),
125
+ );
126
+ }
127
+ /**
128
+ * Get a known transaction by hash
129
+ * @param txHash
130
+ */
131
+ async getTransaction(txHash: HexLike): Promise<Transaction | undefined> {
132
+ return (await this.getTransactionResponse(txHash))?.transaction;
133
+ }
134
+
135
+ /**
136
+ * Record known block headers.
137
+ * Implement this method to enable block headers query caching
138
+ * @param _headers
139
+ */
140
+ async recordHeaders(
141
+ ..._headers: (ClientBlockHeaderLike | ClientBlockHeaderLike[])[]
142
+ ): Promise<void> {}
143
+ /**
144
+ * Get a known block header by hash
145
+ * Implement this method to enable block headers query caching
146
+ * @param _hash
147
+ */
148
+ async getHeaderByHash(
149
+ _hash: HexLike,
150
+ ): Promise<ClientBlockHeader | undefined> {
151
+ return;
152
+ }
153
+ /**
154
+ * Get a known block header by number
155
+ * Implement this method to enable block headers query caching
156
+ * @param _number
157
+ */
158
+ async getHeaderByNumber(
159
+ _number: NumLike,
160
+ ): Promise<ClientBlockHeader | undefined> {
66
161
  return;
67
162
  }
68
163
 
69
164
  /**
70
- * Record known cells
71
- * Implement this method to enable cells query caching
72
- * @param _cells
165
+ * Record known blocks.
166
+ * Implement this method to enable blocks query caching
167
+ * @param _blocks
73
168
  */
74
- async recordCells(..._cells: (CellLike | CellLike[])[]): Promise<void> {}
169
+ async recordBlocks(
170
+ ..._blocks: (ClientBlockLike | ClientBlockLike[])[]
171
+ ): Promise<void> {}
172
+ /**
173
+ * Get a known block header by hash
174
+ * Implement this method to enable block headers query caching
175
+ * @param _hash
176
+ */
177
+ async getBlockByHash(_hash: HexLike): Promise<ClientBlock | undefined> {
178
+ return;
179
+ }
180
+ /**
181
+ * Get a known block header by number
182
+ * Implement this method to enable block headers query caching
183
+ * @param _number
184
+ */
185
+ async getBlockByNumber(_number: NumLike): Promise<ClientBlock | undefined> {
186
+ return;
187
+ }
75
188
  }
@@ -149,7 +149,12 @@ export class MapLru<K, V> extends Map<K, V> {
149
149
  super();
150
150
  }
151
151
 
152
- access(key: K) {
152
+ get(key: K) {
153
+ const val = super.get(key);
154
+ if (val === undefined) {
155
+ return;
156
+ }
157
+
153
158
  const index = this.lru.indexOf(key);
154
159
  if (index !== -1) {
155
160
  this.lru.splice(index, 1);
@@ -159,15 +164,27 @@ export class MapLru<K, V> extends Map<K, V> {
159
164
  this.delete(this.lru[0]);
160
165
  this.lru.shift();
161
166
  }
162
- }
163
167
 
164
- get(key: K) {
165
- this.access(key);
166
- return super.get(key);
168
+ return val;
167
169
  }
168
170
 
169
171
  set(key: K, value: V) {
170
- this.access(key);
171
- return super.set(key, value);
172
+ this.get(key);
173
+
174
+ super.set(key, value);
175
+ return this;
176
+ }
177
+
178
+ delete(key: K): boolean {
179
+ if (!super.delete(key)) {
180
+ return false;
181
+ }
182
+
183
+ const index = this.lru.indexOf(key);
184
+ if (index !== -1) {
185
+ this.lru.splice(index, 1);
186
+ }
187
+
188
+ return true;
172
189
  }
173
190
  }
@@ -1,13 +1,15 @@
1
- import {
2
- Cell,
3
- CellLike,
4
- OutPoint,
5
- OutPointLike,
6
- Transaction,
7
- TransactionLike,
8
- } from "../../ckb/index.js";
1
+ import { Cell, CellLike, OutPoint, OutPointLike } from "../../ckb/index.js";
9
2
  import { hexFrom, HexLike } from "../../hex/index.js";
3
+ import { Num, numFrom, NumLike } from "../../num/index.js";
10
4
  import { ClientCollectableSearchKeyLike } from "../clientTypes.advanced.js";
5
+ import {
6
+ ClientBlock,
7
+ ClientBlockHeader,
8
+ ClientBlockHeaderLike,
9
+ ClientBlockLike,
10
+ ClientTransactionResponse,
11
+ ClientTransactionResponseLike,
12
+ } from "../clientTypes.js";
11
13
  import { ClientCache } from "./cache.js";
12
14
  import { CellRecord, filterCell, MapLru } from "./memory.advanced.js";
13
15
 
@@ -18,21 +20,44 @@ export class ClientCacheMemory extends ClientCache {
18
20
  private readonly cells: MapLru<string, CellRecord>;
19
21
 
20
22
  /**
21
- * TX Hash => Transaction
23
+ * TX Hash => Transaction Response
24
+ */
25
+ private readonly knownTransactions: MapLru<string, ClientTransactionResponse>;
26
+
27
+ /**
28
+ * Block Number => Block Hash
29
+ */
30
+ private readonly knownBlockHashes: MapLru<Num, string>;
31
+
32
+ /**
33
+ * Block Hash => Block Header / Full Block
22
34
  */
23
- private readonly knownTransactions: MapLru<string, Transaction>;
35
+ private readonly knownBlocks: MapLru<
36
+ string,
37
+ Pick<ClientBlock, "header"> | ClientBlock
38
+ >;
24
39
 
25
40
  constructor(
26
41
  private readonly maxCells = 512,
27
42
  private readonly maxTxs = 256,
43
+ private readonly maxBlocks = 128,
28
44
  ) {
29
45
  super();
30
46
 
31
47
  this.cells = new MapLru<string, CellRecord>(this.maxCells);
32
- this.knownTransactions = new MapLru<string, Transaction>(this.maxTxs);
48
+ this.knownTransactions = new MapLru<string, ClientTransactionResponse>(
49
+ this.maxTxs,
50
+ );
51
+ this.knownBlockHashes = new MapLru<Num, string>(this.maxBlocks);
52
+ this.knownBlocks = new MapLru<
53
+ string,
54
+ Pick<ClientBlock, "header"> | ClientBlock
55
+ >(this.maxBlocks);
33
56
  }
34
57
 
35
- async markUsable(...cellLikes: (CellLike | CellLike[])[]): Promise<void> {
58
+ async markUsableNoCache(
59
+ ...cellLikes: (CellLike | CellLike[])[]
60
+ ): Promise<void> {
36
61
  cellLikes.flat().forEach((cellLike) => {
37
62
  const cell = Cell.from(cellLike).clone();
38
63
  const outPointStr = hexFrom(cell.outPoint.toBytes());
@@ -73,10 +98,28 @@ export class ClientCacheMemory extends ClientCache {
73
98
  continue;
74
99
  }
75
100
 
76
- this.cells.access(key);
101
+ this.cells.get(key);
77
102
  yield cell.clone();
78
103
  }
79
104
  }
105
+
106
+ async isUnusable(outPointLike: OutPointLike): Promise<boolean> {
107
+ const outPoint = OutPoint.from(outPointLike);
108
+
109
+ return !(this.cells.get(hexFrom(outPoint.toBytes()))?.[0] ?? true);
110
+ }
111
+
112
+ async recordCells(...cells: (CellLike | CellLike[])[]): Promise<void> {
113
+ cells.flat().map((cellLike) => {
114
+ const cell = Cell.from(cellLike);
115
+ const outPointStr = hexFrom(cell.outPoint.toBytes());
116
+
117
+ if (this.cells.get(outPointStr)) {
118
+ return;
119
+ }
120
+ this.cells.set(outPointStr, [undefined, cell]);
121
+ });
122
+ }
80
123
  async getCell(outPointLike: OutPointLike): Promise<Cell | undefined> {
81
124
  const outPoint = OutPoint.from(outPointLike);
82
125
 
@@ -86,34 +129,91 @@ export class ClientCacheMemory extends ClientCache {
86
129
  }
87
130
  }
88
131
 
89
- async isUnusable(outPointLike: OutPointLike): Promise<boolean> {
90
- const outPoint = OutPoint.from(outPointLike);
91
-
92
- return !(this.cells.get(hexFrom(outPoint.toBytes()))?.[0] ?? true);
93
- }
94
-
95
- async recordTransactions(
96
- ...transactions: (TransactionLike | TransactionLike[])[]
132
+ async recordTransactionResponses(
133
+ ...transactions: (
134
+ | ClientTransactionResponseLike
135
+ | ClientTransactionResponseLike[]
136
+ )[]
97
137
  ): Promise<void> {
98
138
  transactions.flat().map((txLike) => {
99
- const tx = Transaction.from(txLike);
100
- this.knownTransactions.set(tx.hash(), tx);
139
+ const tx = ClientTransactionResponse.from(txLike);
140
+ this.knownTransactions.set(tx.transaction.hash(), tx);
101
141
  });
102
142
  }
103
- async getTransaction(txHashLike: HexLike): Promise<Transaction | undefined> {
143
+ async getTransactionResponse(
144
+ txHashLike: HexLike,
145
+ ): Promise<ClientTransactionResponse | undefined> {
104
146
  const txHash = hexFrom(txHashLike);
105
147
  return this.knownTransactions.get(txHash)?.clone();
106
148
  }
107
149
 
108
- async recordCells(...cells: (CellLike | CellLike[])[]): Promise<void> {
109
- cells.flat().map((cellLike) => {
110
- const cell = Cell.from(cellLike);
111
- const outPointStr = hexFrom(cell.outPoint.toBytes());
150
+ async recordHeaders(
151
+ ...headers: (ClientBlockHeaderLike | ClientBlockHeaderLike[])[]
152
+ ): Promise<void> {
153
+ headers.flat().map((headerLike) => {
154
+ const header = ClientBlockHeader.from(headerLike);
112
155
 
113
- if (this.cells.get(outPointStr)) {
156
+ this.knownBlockHashes.set(header.number, header.hash);
157
+
158
+ const existed = this.knownBlocks.get(header.hash);
159
+ if (existed) {
114
160
  return;
115
161
  }
116
- this.cells.set(outPointStr, [undefined, cell]);
162
+ this.knownBlocks.set(header.hash, { header });
117
163
  });
118
164
  }
165
+ async getHeaderByHash(
166
+ hashLike: HexLike,
167
+ ): Promise<ClientBlockHeader | undefined> {
168
+ const hash = hexFrom(hashLike);
169
+ const block = this.knownBlocks.get(hash);
170
+ if (block) {
171
+ this.knownBlockHashes.get(block.header.number); // For LRU
172
+ }
173
+ return block?.header;
174
+ }
175
+ async getHeaderByNumber(
176
+ numberLike: NumLike,
177
+ ): Promise<ClientBlockHeader | undefined> {
178
+ const number = numFrom(numberLike);
179
+
180
+ const hash = this.knownBlockHashes.get(number);
181
+ if (!hash) {
182
+ return;
183
+ }
184
+ return this.getHeaderByHash(hash);
185
+ }
186
+
187
+ async recordBlocks(
188
+ ...blocks: (ClientBlockLike | ClientBlockLike[])[]
189
+ ): Promise<void> {
190
+ blocks.flat().map((blockLike) => {
191
+ const block = ClientBlock.from(blockLike);
192
+
193
+ this.knownBlockHashes.set(block.header.number, block.header.hash);
194
+ this.knownBlocks.set(block.header.hash, block);
195
+ });
196
+ }
197
+ async getBlockByHash(hashLike: HexLike): Promise<ClientBlock | undefined> {
198
+ const hash = hexFrom(hashLike);
199
+ const block = this.knownBlocks.get(hash);
200
+ if (block) {
201
+ this.knownBlockHashes.get(block.header.number); // For LRU
202
+ if ("transactions" in block) {
203
+ return block;
204
+ }
205
+ }
206
+ return;
207
+ }
208
+ async getBlockByNumber(
209
+ numberLike: NumLike,
210
+ ): Promise<ClientBlock | undefined> {
211
+ const number = numFrom(numberLike);
212
+
213
+ const hash = this.knownBlockHashes.get(number);
214
+ if (!hash) {
215
+ return;
216
+ }
217
+ return this.getBlockByHash(hash);
218
+ }
119
219
  }