@ckb-ccc/core 1.13.0 → 1.15.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 (59) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/address.advanced-BHxWrbFc.mjs +2 -0
  3. package/dist/address.advanced-BHxWrbFc.mjs.map +1 -0
  4. package/dist/advanced.d.mts +1 -1
  5. package/dist/advanced.mjs +1 -1
  6. package/{dist.commonjs/advancedBarrel-DRDT4GBS.d.ts → dist/advancedBarrel-Dwyn3WMm.d.mts} +437 -14
  7. package/dist/advancedBarrel-Dwyn3WMm.d.mts.map +1 -0
  8. package/dist/advancedBarrel.d.mts +1 -1
  9. package/dist/advancedBarrel.mjs +1 -1
  10. package/dist/barrel-DO-IW8Ui.mjs +7 -0
  11. package/dist/barrel-DO-IW8Ui.mjs.map +1 -0
  12. package/dist/barrel.d.mts +2 -2
  13. package/dist/barrel.mjs +1 -1
  14. package/dist/index.d.mts +2 -2
  15. package/dist/index.mjs +1 -1
  16. package/dist.commonjs/address.advanced-UCU9Hn8t.js +2 -0
  17. package/dist.commonjs/address.advanced-UCU9Hn8t.js.map +1 -0
  18. package/dist.commonjs/advanced.d.ts +1 -1
  19. package/dist.commonjs/advanced.js +1 -1
  20. package/{dist/advancedBarrel-BD7EAPVd.d.mts → dist.commonjs/advancedBarrel-D97MfkUZ.d.ts} +438 -13
  21. package/dist.commonjs/advancedBarrel-D97MfkUZ.d.ts.map +1 -0
  22. package/dist.commonjs/advancedBarrel.d.ts +1 -1
  23. package/dist.commonjs/advancedBarrel.js +1 -1
  24. package/dist.commonjs/barrel-D5PRJCB6.js +21 -0
  25. package/dist.commonjs/barrel-D5PRJCB6.js.map +1 -0
  26. package/dist.commonjs/barrel.d.ts +2 -2
  27. package/dist.commonjs/barrel.js +1 -1
  28. package/dist.commonjs/index.d.ts +2 -2
  29. package/dist.commonjs/index.js +1 -1
  30. package/package.json +23 -12
  31. package/src/address/address.advanced.ts +3 -3
  32. package/src/ckb/transaction.ts +20 -1
  33. package/src/client/clientPublicMainnet.advanced.ts +17 -0
  34. package/src/client/clientPublicTestnet.advanced.ts +17 -0
  35. package/src/client/knownScript.ts +1 -0
  36. package/src/codec/entity.ts +12 -8
  37. package/src/hasher/hasherCkb.ts +7 -1
  38. package/src/hex/index.ts +82 -2
  39. package/src/molecule/codec.ts +15 -15
  40. package/src/num/index.ts +17 -4
  41. package/src/signer/ckb/index.ts +3 -1
  42. package/src/signer/ckb/secp256k1Signing.ts +94 -0
  43. package/src/signer/ckb/signerCkbPrivateKey.ts +6 -11
  44. package/src/signer/ckb/signerCkbPublicKey.ts +8 -3
  45. package/src/signer/ckb/signerMultisigCkbPrivateKey.ts +129 -0
  46. package/src/signer/ckb/signerMultisigCkbReadonly.ts +713 -0
  47. package/src/signer/signer/index.ts +100 -1
  48. package/tsdown.config.mts +12 -3
  49. package/dist/address.advanced-CWGrVD-M.mjs +0 -2
  50. package/dist/address.advanced-CWGrVD-M.mjs.map +0 -1
  51. package/dist/advancedBarrel-BD7EAPVd.d.mts.map +0 -1
  52. package/dist/barrel-DchA_RPN.mjs +0 -7
  53. package/dist/barrel-DchA_RPN.mjs.map +0 -1
  54. package/dist.commonjs/address.advanced-COp7SNhZ.js +0 -2
  55. package/dist.commonjs/address.advanced-COp7SNhZ.js.map +0 -1
  56. package/dist.commonjs/advancedBarrel-DRDT4GBS.d.ts.map +0 -1
  57. package/dist.commonjs/barrel-CXC2Uy5L.js +0 -7
  58. package/dist.commonjs/barrel-CXC2Uy5L.js.map +0 -1
  59. package/src/signer/ckb/verifyCkbSecp256k1.ts +0 -31
package/src/num/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Bytes, BytesLike, bytesConcat, bytesFrom } from "../bytes/index.js";
2
+ import { Zero } from "../fixedPoint/index.js";
2
3
  import { Hex, HexLike, hexFrom } from "../hex/index.js";
3
4
 
4
5
  /**
@@ -90,19 +91,31 @@ export function numFrom(val: NumLike): Num {
90
91
  }
91
92
 
92
93
  /**
93
- * Converts a NumLike value to a hexadecimal string.
94
+ * Converts a {@link NumLike} value into its hexadecimal string representation, prefixed with `0x`.
95
+ *
96
+ * @remarks
97
+ * This function returns the direct hexadecimal representation of the number, which may have an odd number of digits.
98
+ * For a full-byte representation (an even number of hex digits), consider using {@link numToBytes}, {@link numLeToBytes}, or {@link numBeToBytes} and then converting the resulting byte array to a hex string.
99
+ *
94
100
  * @public
95
101
  *
96
102
  * @param val - The value to convert, which can be a string, number, bigint, or HexLike.
97
- * @returns A Hex string representing the numeric value.
103
+ * @returns A Hex string representing the number.
104
+ *
105
+ * @throws {Error} If the normalized numeric value is negative.
98
106
  *
99
107
  * @example
100
108
  * ```typescript
101
- * const hex = numToHex(12345); // Outputs "0x3039"
109
+ * const hex = numToHex(4660); // "0x1234"
110
+ * const oddLengthHex = numToHex(10); // "0xa"
102
111
  * ```
103
112
  */
104
113
  export function numToHex(val: NumLike): Hex {
105
- return `0x${numFrom(val).toString(16)}`;
114
+ const v = numFrom(val);
115
+ if (v < Zero) {
116
+ throw new Error("value must be non-negative");
117
+ }
118
+ return `0x${v.toString(16)}`;
106
119
  }
107
120
 
108
121
  /**
@@ -1,5 +1,7 @@
1
+ export * from "./secp256k1Signing.js";
1
2
  export * from "./signerCkbPrivateKey.js";
2
3
  export * from "./signerCkbPublicKey.js";
3
4
  export * from "./signerCkbScriptReadonly.js";
4
- export * from "./verifyCkbSecp256k1.js";
5
+ export * from "./signerMultisigCkbPrivateKey.js";
6
+ export * from "./signerMultisigCkbReadonly.js";
5
7
  export * from "./verifyJoyId.js";
@@ -0,0 +1,94 @@
1
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
2
+ import { bytesConcat, bytesFrom, BytesLike } from "../../bytes/index.js";
3
+ import { hashCkb } from "../../hasher/index.js";
4
+ import { Hex, hexFrom } from "../../hex/index.js";
5
+
6
+ export const SECP256K1_SIGNATURE_LENGTH = 65;
7
+
8
+ /**
9
+ * Sign a message using Secp256k1.
10
+ *
11
+ * @param message - The message to sign.
12
+ * @param privateKey - The private key.
13
+ * @returns The signature.
14
+ * @public
15
+ */
16
+ export function signMessageSecp256k1(
17
+ message: BytesLike,
18
+ privateKey: BytesLike,
19
+ ): Hex {
20
+ const signature = secp256k1.sign(bytesFrom(message), bytesFrom(privateKey), {
21
+ format: "recovered",
22
+ prehash: false,
23
+ });
24
+ return hexFrom(bytesConcat(signature.slice(1), signature.slice(0, 1)));
25
+ }
26
+
27
+ /**
28
+ * Verify a message using Secp256k1.
29
+ *
30
+ * @param message - The message to verify.
31
+ * @param signature - The signature.
32
+ * @param publicKey - The public key.
33
+ * @returns True if the signature is valid, false otherwise.
34
+ * @public
35
+ */
36
+ export function verifyMessageSecp256k1(
37
+ message: BytesLike,
38
+ signature: BytesLike,
39
+ publicKey: BytesLike,
40
+ ): boolean {
41
+ const signatureBytes = bytesFrom(signature);
42
+ return secp256k1.verify(
43
+ bytesConcat(signatureBytes.slice(64), signatureBytes.slice(0, 64)),
44
+ bytesFrom(message),
45
+ bytesFrom(publicKey),
46
+ { format: "recovered", prehash: false },
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Recover the public key from a Secp256k1 signature.
52
+ *
53
+ * @param message - The message.
54
+ * @param signature - The signature.
55
+ * @returns The recovered public key.
56
+ * @public
57
+ */
58
+ export function recoverMessageSecp256k1(
59
+ message: BytesLike,
60
+ signature: BytesLike,
61
+ ): Hex {
62
+ const signatureBytes = bytesFrom(signature);
63
+ return hexFrom(
64
+ secp256k1.recoverPublicKey(
65
+ bytesConcat(signatureBytes.slice(64), signatureBytes.slice(0, 64)),
66
+ bytesFrom(message),
67
+ { prehash: false },
68
+ ),
69
+ );
70
+ }
71
+
72
+ /**
73
+ * @public
74
+ */
75
+ export function messageHashCkbSecp256k1(message: string | BytesLike): Hex {
76
+ const msg = typeof message === "string" ? message : hexFrom(message);
77
+ const buffer = bytesFrom(`Nervos Message:${msg}`, "utf8");
78
+ return hashCkb(buffer);
79
+ }
80
+
81
+ /**
82
+ * @public
83
+ */
84
+ export function verifyMessageCkbSecp256k1(
85
+ message: string | BytesLike,
86
+ signature: string,
87
+ publicKey: string,
88
+ ): boolean {
89
+ return verifyMessageSecp256k1(
90
+ messageHashCkbSecp256k1(message),
91
+ signature,
92
+ publicKey,
93
+ );
94
+ }
@@ -1,10 +1,13 @@
1
1
  import { secp256k1 } from "@noble/curves/secp256k1.js";
2
- import { bytesConcat, bytesFrom, BytesLike } from "../../bytes/index.js";
2
+ import { bytesFrom, BytesLike } from "../../bytes/index.js";
3
3
  import { Transaction, TransactionLike, WitnessArgs } from "../../ckb/index.js";
4
4
  import { Client } from "../../client/index.js";
5
5
  import { Hex, hexFrom, HexLike } from "../../hex/index.js";
6
+ import {
7
+ messageHashCkbSecp256k1,
8
+ signMessageSecp256k1,
9
+ } from "./secp256k1Signing.js";
6
10
  import { SignerCkbPublicKey } from "./signerCkbPublicKey.js";
7
- import { messageHashCkbSecp256k1 } from "./verifyCkbSecp256k1.js";
8
11
 
9
12
  /**
10
13
  * @public
@@ -23,15 +26,7 @@ export class SignerCkbPrivateKey extends SignerCkbPublicKey {
23
26
  }
24
27
 
25
28
  async _signMessage(message: HexLike): Promise<Hex> {
26
- const signature = secp256k1.sign(
27
- bytesFrom(message),
28
- bytesFrom(this.privateKey),
29
- {
30
- format: "recovered",
31
- prehash: false,
32
- },
33
- );
34
- return hexFrom(bytesConcat(signature.slice(1), signature.slice(0, 1)));
29
+ return signMessageSecp256k1(message, this.privateKey);
35
30
  }
36
31
 
37
32
  async signMessageRaw(message: string | BytesLike): Promise<Hex> {
@@ -2,9 +2,10 @@ import { Address } from "../../address/index.js";
2
2
  import { bytesFrom } from "../../bytes/index.js";
3
3
  import { Script, Transaction, TransactionLike } from "../../ckb/index.js";
4
4
  import { CellDepInfo, Client, KnownScript } from "../../client/index.js";
5
- import { hashCkb } from "../../hasher/index.js";
5
+ import { hashCkbShort } from "../../hasher/index.js";
6
6
  import { Hex, HexLike, hexFrom } from "../../hex/index.js";
7
7
  import { Signer, SignerSignType, SignerType } from "../signer/index.js";
8
+ import { SECP256K1_SIGNATURE_LENGTH } from "./secp256k1Signing.js";
8
9
 
9
10
  /**
10
11
  * @public
@@ -47,7 +48,7 @@ export class SignerCkbPublicKey extends Signer {
47
48
  return Address.fromKnownScript(
48
49
  this.client,
49
50
  KnownScript.Secp256k1Blake160,
50
- bytesFrom(hashCkb(this.publicKey)).slice(0, 20),
51
+ hashCkbShort(this.publicKey),
51
52
  );
52
53
  }
53
54
 
@@ -140,7 +141,11 @@ export class SignerCkbPublicKey extends Signer {
140
141
 
141
142
  await Promise.all(
142
143
  (await this.getRelatedScripts(tx)).map(async ({ script, cellDeps }) => {
143
- await tx.prepareSighashAllWitness(script, 65, this.client);
144
+ await tx.prepareSighashAllWitness(
145
+ script,
146
+ SECP256K1_SIGNATURE_LENGTH,
147
+ this.client,
148
+ );
144
149
  await tx.addCellDepInfos(this.client, cellDeps);
145
150
  }),
146
151
  );
@@ -0,0 +1,129 @@
1
+ import { SinceLike, Transaction, TransactionLike } from "../../ckb/index.js";
2
+ import { Client, KnownScript, ScriptInfoLike } from "../../client/index.js";
3
+ import { hashCkbShort } from "../../hasher/index.js";
4
+ import { Hex, hexFrom, HexLike } from "../../hex/index.js";
5
+ import { signMessageSecp256k1 } from "./secp256k1Signing.js";
6
+ import { SignerCkbPrivateKey } from "./signerCkbPrivateKey.js";
7
+ import {
8
+ MultisigCkbWitnessLike,
9
+ SignerMultisigCkbReadonly,
10
+ } from "./signerMultisigCkbReadonly.js";
11
+
12
+ /**
13
+ * A class extending Signer that provides access to a CKB multisig script and supports signing operations.
14
+ * @public
15
+ */
16
+ export class SignerMultisigCkbPrivateKey extends SignerMultisigCkbReadonly {
17
+ private readonly privateKey: Hex;
18
+ private readonly signer: SignerCkbPrivateKey;
19
+
20
+ /**
21
+ * Creates an instance of SignerMultisigCkbPrivateKey.
22
+ *
23
+ * @param client - The client instance.
24
+ * @param privateKey - The private key.
25
+ * @param multisigInfo - The multisig information.
26
+ * @param options - The options.
27
+ */
28
+ constructor(
29
+ client: Client,
30
+ privateKey: HexLike,
31
+ multisigInfo: MultisigCkbWitnessLike,
32
+ options?: {
33
+ since?: SinceLike | null;
34
+ scriptInfos?: (KnownScript | ScriptInfoLike)[] | null;
35
+ } | null,
36
+ ) {
37
+ super(client, multisigInfo, options);
38
+
39
+ this.privateKey = hexFrom(privateKey);
40
+ this.signer = new SignerCkbPrivateKey(client, this.privateKey);
41
+ }
42
+
43
+ /**
44
+ * Sign a transaction only (without preparing).
45
+ *
46
+ * @param txLike - The transaction to sign.
47
+ * @returns The signed transaction.
48
+ */
49
+ async signOnlyTransaction(txLike: TransactionLike): Promise<Transaction> {
50
+ let tx = Transaction.from(txLike);
51
+
52
+ const thisPubkeyHash = hashCkbShort(this.signer.publicKey);
53
+
54
+ const index = this.multisigInfo.publicKeyHashes.indexOf(thisPubkeyHash);
55
+ if (index === -1) {
56
+ return tx;
57
+ }
58
+ const isSelfRequired = index < this.multisigInfo.mustMatch;
59
+
60
+ for (const { script } of await this.scriptInfos) {
61
+ const info = await this.getSignInfo(tx, script);
62
+ if (!info) {
63
+ continue;
64
+ }
65
+
66
+ // === Find a position for the signature ===
67
+ tx = await this.prepareWitnessArgsAt(
68
+ tx,
69
+ info.position,
70
+ async (witness) => {
71
+ // We re-evaluate the signatures to filter invalid / excessive signatures
72
+ const signatures: Hex[] = [];
73
+ let requiredCount = 0;
74
+ let isSignNeeded = true;
75
+
76
+ for (const {
77
+ pubkeyHash,
78
+ signature,
79
+ isRequired,
80
+ } of witness.generatePublicKeyHashesFromSignatures(info.message)) {
81
+ if (pubkeyHash === thisPubkeyHash) {
82
+ if (!isSignNeeded) {
83
+ // Has signed and added to the signatures list already. We will not add it again.
84
+ continue;
85
+ }
86
+ isSignNeeded = false;
87
+ }
88
+
89
+ if (isRequired) {
90
+ requiredCount += 1;
91
+ } else if (
92
+ signatures.length - requiredCount >=
93
+ this.multisigInfo.flexibleThreshold
94
+ ) {
95
+ // Too many flexible signatures
96
+ continue;
97
+ }
98
+
99
+ signatures.push(signature);
100
+ if (signatures.length >= this.multisigInfo.threshold) {
101
+ // We have got enough signatures
102
+ isSignNeeded = false;
103
+ break;
104
+ }
105
+ }
106
+
107
+ if (
108
+ isSignNeeded &&
109
+ (isSelfRequired ||
110
+ signatures.length - requiredCount <
111
+ this.multisigInfo.flexibleThreshold)
112
+ ) {
113
+ // Add the signature from this signer only when
114
+ // 1. The signature is needed
115
+ // 2. It's required or...
116
+ // 3. We haven't got enough flexible signatures
117
+ signatures.push(
118
+ signMessageSecp256k1(info.message, this.privateKey),
119
+ );
120
+ }
121
+ witness.signatures = signatures;
122
+ return witness;
123
+ },
124
+ );
125
+ }
126
+
127
+ return tx;
128
+ }
129
+ }