@ottochain/sdk 1.2.0 → 1.3.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 (103) hide show
  1. package/LICENSE +190 -0
  2. package/dist/cjs/index.js +34 -15
  3. package/dist/cjs/ottochain/index.js +20 -1
  4. package/dist/cjs/ottochain/metagraph-client.js +7 -8
  5. package/dist/cjs/ottochain/snapshot.js +3 -3
  6. package/dist/cjs/{metakit → ottochain}/transaction.js +4 -5
  7. package/dist/cjs/verify.js +17 -0
  8. package/dist/esm/apps/contracts/index.js +28 -10
  9. package/dist/esm/apps/corporate/index.js +79 -24
  10. package/dist/esm/apps/governance/index.js +85 -36
  11. package/dist/esm/apps/identity/constants.js +27 -22
  12. package/dist/esm/apps/identity/index.js +35 -7
  13. package/dist/esm/apps/index.js +32 -6
  14. package/dist/esm/apps/markets/index.js +27 -6
  15. package/dist/esm/apps/oracles/index.js +27 -7
  16. package/dist/esm/errors.js +19 -9
  17. package/dist/esm/generated/google/protobuf/struct.js +39 -33
  18. package/dist/esm/generated/google/protobuf/timestamp.js +9 -6
  19. package/dist/esm/generated/index.js +134 -10
  20. package/dist/esm/generated/ottochain/apps/contracts/v1/contract.js +54 -48
  21. package/dist/esm/generated/ottochain/apps/corporate/v1/corporate.js +357 -335
  22. package/dist/esm/generated/ottochain/apps/governance/v1/governance.js +299 -284
  23. package/dist/esm/generated/ottochain/apps/identity/v1/agent.js +47 -38
  24. package/dist/esm/generated/ottochain/apps/identity/v1/attestation.js +50 -44
  25. package/dist/esm/generated/ottochain/apps/markets/v1/market.js +86 -77
  26. package/dist/esm/generated/ottochain/apps/oracles/v1/oracle.js +72 -66
  27. package/dist/esm/generated/ottochain/v1/common.js +4 -1
  28. package/dist/esm/generated/ottochain/v1/fiber.js +96 -90
  29. package/dist/esm/generated/ottochain/v1/messages.js +82 -79
  30. package/dist/esm/generated/ottochain/v1/records.js +140 -137
  31. package/dist/esm/index.js +86 -20
  32. package/dist/esm/{metakit → ottochain}/drop-nulls.js +5 -1
  33. package/dist/esm/ottochain/index.js +56 -3
  34. package/dist/esm/ottochain/metagraph-client.js +16 -13
  35. package/dist/esm/{metakit → ottochain}/normalize.js +11 -4
  36. package/dist/esm/ottochain/snapshot.js +20 -10
  37. package/dist/esm/{metakit → ottochain}/transaction.js +25 -14
  38. package/dist/esm/ottochain/types.js +2 -1
  39. package/dist/esm/types.js +7 -2
  40. package/dist/esm/validation.js +76 -65
  41. package/dist/esm/verify.js +17 -0
  42. package/dist/types/index.d.ts +14 -7
  43. package/dist/types/ottochain/index.d.ts +4 -0
  44. package/dist/types/ottochain/metagraph-client.d.ts +1 -1
  45. package/dist/types/{metakit → ottochain}/transaction.d.ts +1 -1
  46. package/dist/types/validation.d.ts +8 -8
  47. package/dist/types/verify.d.ts +9 -0
  48. package/package.json +5 -3
  49. package/dist/cjs/metakit/binary.js +0 -58
  50. package/dist/cjs/metakit/canonicalize.js +0 -40
  51. package/dist/cjs/metakit/codec.js +0 -45
  52. package/dist/cjs/metakit/currency-transaction.js +0 -319
  53. package/dist/cjs/metakit/currency-types.js +0 -13
  54. package/dist/cjs/metakit/hash.js +0 -84
  55. package/dist/cjs/metakit/index.js +0 -86
  56. package/dist/cjs/metakit/network/client.js +0 -78
  57. package/dist/cjs/metakit/network/currency-l1-client.js +0 -101
  58. package/dist/cjs/metakit/network/data-l1-client.js +0 -76
  59. package/dist/cjs/metakit/network/index.js +0 -16
  60. package/dist/cjs/metakit/network/types.js +0 -20
  61. package/dist/cjs/metakit/sign.js +0 -120
  62. package/dist/cjs/metakit/signed-object.js +0 -100
  63. package/dist/cjs/metakit/types.js +0 -14
  64. package/dist/cjs/metakit/verify.js +0 -217
  65. package/dist/cjs/metakit/wallet.js +0 -127
  66. package/dist/esm/metakit/binary.js +0 -53
  67. package/dist/esm/metakit/canonicalize.js +0 -33
  68. package/dist/esm/metakit/codec.js +0 -38
  69. package/dist/esm/metakit/currency-transaction.js +0 -306
  70. package/dist/esm/metakit/currency-types.js +0 -10
  71. package/dist/esm/metakit/hash.js +0 -77
  72. package/dist/esm/metakit/index.js +0 -33
  73. package/dist/esm/metakit/network/client.js +0 -74
  74. package/dist/esm/metakit/network/currency-l1-client.js +0 -97
  75. package/dist/esm/metakit/network/data-l1-client.js +0 -72
  76. package/dist/esm/metakit/network/index.js +0 -9
  77. package/dist/esm/metakit/network/types.js +0 -16
  78. package/dist/esm/metakit/sign.js +0 -114
  79. package/dist/esm/metakit/signed-object.js +0 -94
  80. package/dist/esm/metakit/types.js +0 -11
  81. package/dist/esm/metakit/verify.js +0 -210
  82. package/dist/esm/metakit/wallet.js +0 -117
  83. package/dist/types/metakit/binary.d.ts +0 -38
  84. package/dist/types/metakit/canonicalize.d.ts +0 -26
  85. package/dist/types/metakit/codec.d.ts +0 -16
  86. package/dist/types/metakit/currency-transaction.d.ts +0 -157
  87. package/dist/types/metakit/currency-types.d.ts +0 -55
  88. package/dist/types/metakit/hash.d.ts +0 -50
  89. package/dist/types/metakit/index.d.ts +0 -26
  90. package/dist/types/metakit/network/client.d.ts +0 -23
  91. package/dist/types/metakit/network/currency-l1-client.d.ts +0 -71
  92. package/dist/types/metakit/network/data-l1-client.d.ts +0 -57
  93. package/dist/types/metakit/network/index.d.ts +0 -10
  94. package/dist/types/metakit/network/types.d.ts +0 -74
  95. package/dist/types/metakit/sign.d.ts +0 -65
  96. package/dist/types/metakit/signed-object.d.ts +0 -66
  97. package/dist/types/metakit/types.d.ts +0 -67
  98. package/dist/types/metakit/verify.d.ts +0 -55
  99. package/dist/types/metakit/wallet.d.ts +0 -70
  100. /package/dist/cjs/{metakit → ottochain}/drop-nulls.js +0 -0
  101. /package/dist/cjs/{metakit → ottochain}/normalize.js +0 -0
  102. /package/dist/types/{metakit → ottochain}/drop-nulls.d.ts +0 -0
  103. /package/dist/types/{metakit → ottochain}/normalize.d.ts +0 -0
@@ -1,101 +0,0 @@
1
- "use strict";
2
- /**
3
- * Currency L1 client for submitting and querying transactions
4
- *
5
- * @packageDocumentation
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.CurrencyL1Client = void 0;
9
- const client_js_1 = require("./client.js");
10
- const types_js_1 = require("./types.js");
11
- /**
12
- * Client for interacting with Currency L1 nodes
13
- *
14
- * @example
15
- * ```typescript
16
- * const client = new CurrencyL1Client({ l1Url: 'http://localhost:9010' });
17
- *
18
- * // Get last reference for an address
19
- * const lastRef = await client.getLastReference('DAG...');
20
- *
21
- * // Submit a transaction
22
- * const result = await client.postTransaction(signedTx);
23
- *
24
- * // Check transaction status
25
- * const pending = await client.getPendingTransaction(result.hash);
26
- * ```
27
- */
28
- class CurrencyL1Client {
29
- /**
30
- * Create a new CurrencyL1Client
31
- *
32
- * @param config - Network configuration with l1Url
33
- * @throws Error if l1Url is not provided
34
- */
35
- constructor(config) {
36
- if (!config.l1Url) {
37
- throw new Error('l1Url is required for CurrencyL1Client');
38
- }
39
- this.client = new client_js_1.HttpClient(config.l1Url, config.timeout);
40
- }
41
- /**
42
- * Get the last accepted transaction reference for an address
43
- *
44
- * This is needed to create a new transaction that chains from
45
- * the address's most recent transaction.
46
- *
47
- * @param address - DAG address to query
48
- * @param options - Request options
49
- * @returns Transaction reference with hash and ordinal
50
- */
51
- async getLastReference(address, options) {
52
- return this.client.get(`/transactions/last-reference/${address}`, options);
53
- }
54
- /**
55
- * Submit a signed currency transaction to the L1 network
56
- *
57
- * @param transaction - Signed currency transaction
58
- * @param options - Request options
59
- * @returns Response containing the transaction hash
60
- */
61
- async postTransaction(transaction, options) {
62
- return this.client.post('/transactions', transaction, options);
63
- }
64
- /**
65
- * Get a pending transaction by hash
66
- *
67
- * Use this to poll for transaction status after submission.
68
- * Returns null if the transaction is not found (already confirmed or invalid).
69
- *
70
- * @param hash - Transaction hash
71
- * @param options - Request options
72
- * @returns Pending transaction details or null if not found
73
- */
74
- async getPendingTransaction(hash, options) {
75
- try {
76
- return await this.client.get(`/transactions/${hash}`, options);
77
- }
78
- catch (error) {
79
- if (error instanceof types_js_1.NetworkError && error.statusCode === 404) {
80
- return null;
81
- }
82
- throw error;
83
- }
84
- }
85
- /**
86
- * Check the health/availability of the L1 node
87
- *
88
- * @param options - Request options
89
- * @returns True if the node is healthy
90
- */
91
- async checkHealth(options) {
92
- try {
93
- await this.client.get('/cluster/info', options);
94
- return true;
95
- }
96
- catch {
97
- return false;
98
- }
99
- }
100
- }
101
- exports.CurrencyL1Client = CurrencyL1Client;
@@ -1,76 +0,0 @@
1
- "use strict";
2
- /**
3
- * Data L1 client for submitting data transactions to metagraphs
4
- *
5
- * @packageDocumentation
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.DataL1Client = void 0;
9
- const client_js_1 = require("./client.js");
10
- /**
11
- * Client for interacting with Data L1 nodes (metagraphs)
12
- *
13
- * @example
14
- * ```typescript
15
- * const client = new DataL1Client({ dataL1Url: 'http://localhost:8080' });
16
- *
17
- * // Estimate fee for data submission
18
- * const feeInfo = await client.estimateFee(signedData);
19
- *
20
- * // Submit data
21
- * const result = await client.postData(signedData);
22
- * ```
23
- */
24
- class DataL1Client {
25
- /**
26
- * Create a new DataL1Client
27
- *
28
- * @param config - Network configuration with dataL1Url
29
- * @throws Error if dataL1Url is not provided
30
- */
31
- constructor(config) {
32
- if (!config.dataL1Url) {
33
- throw new Error('dataL1Url is required for DataL1Client');
34
- }
35
- this.client = new client_js_1.HttpClient(config.dataL1Url, config.timeout);
36
- }
37
- /**
38
- * Estimate the fee for submitting data
39
- *
40
- * Some metagraphs charge fees for data submissions.
41
- * Call this before postData to know the required fee.
42
- *
43
- * @param data - Signed data object to estimate fee for
44
- * @param options - Request options
45
- * @returns Fee estimate with amount and destination address
46
- */
47
- async estimateFee(data, options) {
48
- return this.client.post('/data/estimate-fee', data, options);
49
- }
50
- /**
51
- * Submit signed data to the Data L1 node
52
- *
53
- * @param data - Signed data object to submit
54
- * @param options - Request options
55
- * @returns Response containing the data hash
56
- */
57
- async postData(data, options) {
58
- return this.client.post('/data', data, options);
59
- }
60
- /**
61
- * Check the health/availability of the Data L1 node
62
- *
63
- * @param options - Request options
64
- * @returns True if the node is healthy
65
- */
66
- async checkHealth(options) {
67
- try {
68
- await this.client.get('/cluster/info', options);
69
- return true;
70
- }
71
- catch {
72
- return false;
73
- }
74
- }
75
- }
76
- exports.DataL1Client = DataL1Client;
@@ -1,16 +0,0 @@
1
- "use strict";
2
- /**
3
- * Network operations for L1 node interactions
4
- *
5
- * @packageDocumentation
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.NetworkError = exports.HttpClient = exports.DataL1Client = exports.CurrencyL1Client = void 0;
9
- var currency_l1_client_js_1 = require("./currency-l1-client.js");
10
- Object.defineProperty(exports, "CurrencyL1Client", { enumerable: true, get: function () { return currency_l1_client_js_1.CurrencyL1Client; } });
11
- var data_l1_client_js_1 = require("./data-l1-client.js");
12
- Object.defineProperty(exports, "DataL1Client", { enumerable: true, get: function () { return data_l1_client_js_1.DataL1Client; } });
13
- var client_js_1 = require("./client.js");
14
- Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return client_js_1.HttpClient; } });
15
- var types_js_1 = require("./types.js");
16
- Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return types_js_1.NetworkError; } });
@@ -1,20 +0,0 @@
1
- "use strict";
2
- /**
3
- * Network types for L1 client operations
4
- *
5
- * @packageDocumentation
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.NetworkError = void 0;
9
- /**
10
- * Network error with status code and response details
11
- */
12
- class NetworkError extends Error {
13
- constructor(message, statusCode, responseBody) {
14
- super(message);
15
- this.name = 'NetworkError';
16
- this.statusCode = statusCode;
17
- this.responseBody = responseBody;
18
- }
19
- }
20
- exports.NetworkError = NetworkError;
@@ -1,120 +0,0 @@
1
- "use strict";
2
- /**
3
- * Signing Functions
4
- *
5
- * ECDSA signing using secp256k1 curve via dag4js.
6
- * Implements the Constellation signature protocol.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.signHash = exports.signDataUpdate = exports.sign = void 0;
10
- const dag4_1 = require("@stardust-collective/dag4");
11
- const js_sha256_1 = require("js-sha256");
12
- const canonicalize_js_1 = require("./canonicalize.js");
13
- /**
14
- * Sign data using the regular Constellation protocol (non-DataUpdate)
15
- *
16
- * Protocol:
17
- * 1. Canonicalize JSON (RFC 8785)
18
- * 2. SHA-256 hash the canonical JSON string
19
- * 3. Sign using dag4.keyStore.sign
20
- *
21
- * @param data - Any JSON-serializable object
22
- * @param privateKey - Private key in hex format
23
- * @returns SignatureProof with public key ID and signature
24
- *
25
- * @example
26
- * ```typescript
27
- * const proof = await sign({ action: 'test' }, privateKeyHex);
28
- * console.log(proof.id); // public key (128 chars)
29
- * console.log(proof.signature); // DER signature
30
- * ```
31
- */
32
- async function sign(data, privateKey) {
33
- // Step 1: Canonicalize JSON (RFC 8785)
34
- const canonicalJson = (0, canonicalize_js_1.canonicalize)(data);
35
- // Step 2-3: UTF-8 encode and SHA-256 hash (sha256 handles UTF-8 encoding internally)
36
- // Returns 64-character hex string
37
- const hashHex = (0, js_sha256_1.sha256)(canonicalJson);
38
- // Step 4-6: dag4.keyStore.sign internally:
39
- // 4. Treats hashHex as UTF-8 bytes
40
- // 5. SHA-512 hashes those bytes, truncates to 32 bytes
41
- // 6. Signs with ECDSA secp256k1
42
- const signature = await dag4_1.dag4.keyStore.sign(privateKey, hashHex);
43
- // Get public key ID (without 04 prefix)
44
- const publicKey = dag4_1.dag4.keyStore.getPublicKeyFromPrivate(privateKey, false);
45
- const id = normalizePublicKeyId(publicKey);
46
- return { id, signature };
47
- }
48
- exports.sign = sign;
49
- /**
50
- * Sign data as a DataUpdate (with Constellation prefix)
51
- *
52
- * Protocol:
53
- * 1. Canonicalize JSON (RFC 8785)
54
- * 2. Base64 encode the canonical JSON
55
- * 3. Sign using dag4.keyStore.dataSign (adds Constellation prefix internally)
56
- *
57
- * @param data - Any JSON-serializable object
58
- * @param privateKey - Private key in hex format
59
- * @returns SignatureProof
60
- */
61
- async function signDataUpdate(data, privateKey) {
62
- // Step 1: Canonicalize JSON
63
- const canonicalJson = (0, canonicalize_js_1.canonicalize)(data);
64
- // Step 2: Base64 encode for dataSign
65
- const base64String = Buffer.from(canonicalJson, 'utf-8').toString('base64');
66
- // Step 3: Sign using dag4's dataSign (handles Constellation prefix internally)
67
- const signature = await dag4_1.dag4.keyStore.dataSign(privateKey, base64String);
68
- // Get public key ID
69
- const publicKey = dag4_1.dag4.keyStore.getPublicKeyFromPrivate(privateKey, false);
70
- const id = normalizePublicKeyId(publicKey);
71
- return { id, signature };
72
- }
73
- exports.signDataUpdate = signDataUpdate;
74
- /**
75
- * Sign a pre-computed SHA-256 hash
76
- *
77
- * This is the low-level signing function. Use `sign()` or `signDataUpdate()`
78
- * for most use cases.
79
- *
80
- * Protocol (performed by dag4.keyStore.sign):
81
- * 1. Treat hashHex as UTF-8 bytes (64 ASCII characters = 64 bytes)
82
- * 2. SHA-512 hash those bytes (produces 64 bytes)
83
- * 3. Truncate to first 32 bytes (for secp256k1 curve order)
84
- * 4. Sign with ECDSA secp256k1
85
- * 5. Return DER-encoded signature
86
- *
87
- * @param hashHex - SHA-256 hash as 64-character hex string
88
- * @param privateKey - Private key in hex format (64 characters)
89
- * @returns DER-encoded signature in hex format
90
- *
91
- * @example
92
- * ```typescript
93
- * // Compute your own hash
94
- * const hashHex = sha256(myData);
95
- * const signature = await signHash(hashHex, privateKey);
96
- * ```
97
- */
98
- async function signHash(hashHex, privateKey) {
99
- // dag4.keyStore.sign performs:
100
- // 1. SHA-512 of hashHex (treating 64 hex chars as UTF-8 bytes)
101
- // 2. Truncation to 32 bytes (handled internally by crypto library)
102
- // 3. ECDSA signing with secp256k1
103
- return dag4_1.dag4.keyStore.sign(privateKey, hashHex);
104
- }
105
- exports.signHash = signHash;
106
- /**
107
- * Normalize public key to ID format (without 04 prefix, 128 chars)
108
- */
109
- function normalizePublicKeyId(publicKey) {
110
- // If 130 chars (with 04 prefix), remove prefix
111
- if (publicKey.length === 130 && publicKey.startsWith('04')) {
112
- return publicKey.substring(2);
113
- }
114
- // If 128 chars (without prefix), return as-is
115
- if (publicKey.length === 128) {
116
- return publicKey;
117
- }
118
- // Otherwise return as-is and let validation catch issues
119
- return publicKey;
120
- }
@@ -1,100 +0,0 @@
1
- "use strict";
2
- /**
3
- * High-Level Signed Object API
4
- *
5
- * Convenience functions for creating and managing signed objects.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.batchSign = exports.addSignature = exports.createSignedObject = void 0;
9
- const sign_js_1 = require("./sign.js");
10
- /**
11
- * Create a signed object with a single signature
12
- *
13
- * @param value - Any JSON-serializable object
14
- * @param privateKey - Private key in hex format
15
- * @param options - Signing options
16
- * @returns Signed object ready for submission
17
- *
18
- * @example
19
- * ```typescript
20
- * // Sign a regular data object
21
- * const signed = await createSignedObject(myData, privateKey);
22
- *
23
- * // Sign as DataUpdate for L1 submission
24
- * const signedUpdate = await createSignedObject(myData, privateKey, { isDataUpdate: true });
25
- * ```
26
- */
27
- async function createSignedObject(value, privateKey, options = {}) {
28
- const { isDataUpdate = false } = options;
29
- const proof = isDataUpdate
30
- ? await (0, sign_js_1.signDataUpdate)(value, privateKey)
31
- : await (0, sign_js_1.sign)(value, privateKey);
32
- return {
33
- value,
34
- proofs: [proof],
35
- };
36
- }
37
- exports.createSignedObject = createSignedObject;
38
- /**
39
- * Add an additional signature to an existing signed object
40
- *
41
- * This allows building multi-signature objects where multiple parties
42
- * need to sign the same data.
43
- *
44
- * @param signed - Existing signed object
45
- * @param privateKey - Private key in hex format
46
- * @param options - Signing options (must match original signing)
47
- * @returns New signed object with additional proof
48
- *
49
- * @example
50
- * ```typescript
51
- * // First party signs
52
- * let signed = await createSignedObject(data, party1Key);
53
- *
54
- * // Second party adds signature
55
- * signed = await addSignature(signed, party2Key);
56
- *
57
- * // Now has 2 proofs
58
- * console.log(signed.proofs.length); // 2
59
- * ```
60
- */
61
- async function addSignature(signed, privateKey, options = {}) {
62
- const { isDataUpdate = false } = options;
63
- const newProof = isDataUpdate
64
- ? await (0, sign_js_1.signDataUpdate)(signed.value, privateKey)
65
- : await (0, sign_js_1.sign)(signed.value, privateKey);
66
- return {
67
- value: signed.value,
68
- proofs: [...signed.proofs, newProof],
69
- };
70
- }
71
- exports.addSignature = addSignature;
72
- /**
73
- * Create a signed object with multiple signatures at once
74
- *
75
- * Useful when you have access to multiple private keys and want
76
- * to create a multi-sig object in one operation.
77
- *
78
- * @param value - Any JSON-serializable object
79
- * @param privateKeys - Array of private keys in hex format
80
- * @param options - Signing options
81
- * @returns Signed object with multiple proofs
82
- *
83
- * @example
84
- * ```typescript
85
- * const signed = await batchSign(data, [key1, key2, key3]);
86
- * console.log(signed.proofs.length); // 3
87
- * ```
88
- */
89
- async function batchSign(value, privateKeys, options = {}) {
90
- if (privateKeys.length === 0) {
91
- throw new Error('At least one private key is required');
92
- }
93
- const { isDataUpdate = false } = options;
94
- const proofs = await Promise.all(privateKeys.map((key) => (isDataUpdate ? (0, sign_js_1.signDataUpdate)(value, key) : (0, sign_js_1.sign)(value, key))));
95
- return {
96
- value,
97
- proofs,
98
- };
99
- }
100
- exports.batchSign = batchSign;
@@ -1,14 +0,0 @@
1
- "use strict";
2
- /**
3
- * Core type definitions for the Ottochain SDK
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.CONSTELLATION_PREFIX = exports.ALGORITHM = void 0;
7
- /**
8
- * Supported signature algorithm
9
- */
10
- exports.ALGORITHM = 'SECP256K1_RFC8785_V1';
11
- /**
12
- * Constellation prefix for DataUpdate signing
13
- */
14
- exports.CONSTELLATION_PREFIX = '\x19Constellation Signed Data:\n';
@@ -1,217 +0,0 @@
1
- "use strict";
2
- /**
3
- * Signature Verification
4
- *
5
- * Verify ECDSA signatures using secp256k1 curve via dag4js.
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.normalizeSignatureToLowS = exports.verifySignature = exports.verifyHash = exports.verify = void 0;
9
- const dag4_1 = require("@stardust-collective/dag4");
10
- const js_sha256_1 = require("js-sha256");
11
- const binary_js_1 = require("./binary.js");
12
- // secp256k1 curve order (n) for signature normalization
13
- const SECP256K1_N = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141');
14
- const SECP256K1_HALF_N = SECP256K1_N / 2n;
15
- /**
16
- * Verify a signed object
17
- *
18
- * @param signed - Signed object with value and proofs
19
- * @param isDataUpdate - Whether the value was signed as a DataUpdate
20
- * @returns VerificationResult with valid/invalid proof lists
21
- *
22
- * @example
23
- * ```typescript
24
- * const result = await verify(signedObject);
25
- * if (result.isValid) {
26
- * console.log('All signatures valid');
27
- * }
28
- * ```
29
- */
30
- async function verify(signed, isDataUpdate = false) {
31
- // Compute the hash that should have been signed
32
- const bytes = (0, binary_js_1.toBytes)(signed.value, isDataUpdate);
33
- const hashHex = js_sha256_1.sha256.hex(bytes);
34
- const validProofs = [];
35
- const invalidProofs = [];
36
- for (const proof of signed.proofs) {
37
- try {
38
- const isValid = await verifyHash(hashHex, proof.signature, proof.id);
39
- if (isValid) {
40
- validProofs.push(proof);
41
- }
42
- else {
43
- invalidProofs.push(proof);
44
- }
45
- }
46
- catch {
47
- // Verification error = invalid
48
- invalidProofs.push(proof);
49
- }
50
- }
51
- return {
52
- isValid: invalidProofs.length === 0 && validProofs.length > 0,
53
- validProofs,
54
- invalidProofs,
55
- };
56
- }
57
- exports.verify = verify;
58
- /**
59
- * Verify a signature against a SHA-256 hash
60
- *
61
- * Protocol:
62
- * 1. Treat hash hex as UTF-8 bytes (NOT hex decode)
63
- * 2. SHA-512 hash
64
- * 3. Truncate to 32 bytes (handled internally by dag4)
65
- * 4. Verify ECDSA signature
66
- *
67
- * @param hashHex - SHA-256 hash as 64-character hex string
68
- * @param signature - DER-encoded signature in hex format
69
- * @param publicKeyId - Public key in hex (with or without 04 prefix)
70
- * @returns true if signature is valid
71
- */
72
- async function verifyHash(hashHex, signature, publicKeyId) {
73
- try {
74
- // Normalize public key (add 04 prefix if needed)
75
- const fullPublicKey = normalizePublicKey(publicKeyId);
76
- // Normalize signature to low-S form for BIP 62/146 compatibility
77
- // Some signing implementations produce high-S signatures which are
78
- // mathematically valid but rejected by strict implementations
79
- const normalizedSignature = normalizeSignatureToLowS(signature);
80
- // Use dag4's verify which handles:
81
- // 1. SHA-512 of hashHex (treating as UTF-8)
82
- // 2. Internal truncation to 32 bytes
83
- // 3. ECDSA verification
84
- return dag4_1.dag4.keyStore.verify(fullPublicKey, hashHex, normalizedSignature);
85
- }
86
- catch {
87
- return false;
88
- }
89
- }
90
- exports.verifyHash = verifyHash;
91
- /**
92
- * Verify a single signature proof against data
93
- *
94
- * @param data - The original data that was signed
95
- * @param proof - The signature proof to verify
96
- * @param isDataUpdate - Whether data was signed as DataUpdate
97
- * @returns true if signature is valid
98
- */
99
- async function verifySignature(data, proof, isDataUpdate = false) {
100
- const bytes = (0, binary_js_1.toBytes)(data, isDataUpdate);
101
- const hashHex = js_sha256_1.sha256.hex(bytes);
102
- return verifyHash(hashHex, proof.signature, proof.id);
103
- }
104
- exports.verifySignature = verifySignature;
105
- /**
106
- * Normalize public key to full format (with 04 prefix)
107
- */
108
- function normalizePublicKey(publicKey) {
109
- // If 128 chars (without 04 prefix), add prefix
110
- if (publicKey.length === 128) {
111
- return '04' + publicKey;
112
- }
113
- // If 130 chars (with 04 prefix), return as-is
114
- if (publicKey.length === 130 && publicKey.startsWith('04')) {
115
- return publicKey;
116
- }
117
- // Otherwise return as-is
118
- return publicKey;
119
- }
120
- /**
121
- * Normalize a DER-encoded signature to use low-S value.
122
- *
123
- * BIP 62/146 requires S values to be in the lower half of the curve order.
124
- * Some signing implementations produce high-S signatures which are mathematically
125
- * valid but rejected by strict verifiers. This normalizes high-S to low-S by
126
- * computing S' = N - S where N is the curve order.
127
- */
128
- function normalizeSignatureToLowS(signatureHex) {
129
- const bytes = hexToBytes(signatureHex);
130
- // Parse DER signature: 0x30 <total_len> 0x02 <r_len> <r> 0x02 <s_len> <s>
131
- if (bytes[0] !== 0x30) {
132
- return signatureHex; // Not a valid DER signature
133
- }
134
- let offset = 2; // Skip 0x30 and total length
135
- // Parse R
136
- if (bytes[offset] !== 0x02) {
137
- return signatureHex;
138
- }
139
- const rLen = bytes[offset + 1];
140
- const rStart = offset + 2;
141
- const rEnd = rStart + rLen;
142
- offset = rEnd;
143
- // Parse S
144
- if (bytes[offset] !== 0x02) {
145
- return signatureHex;
146
- }
147
- const sLen = bytes[offset + 1];
148
- const sStart = offset + 2;
149
- const sEnd = sStart + sLen;
150
- // Extract S value
151
- const sBytes = bytes.slice(sStart, sEnd);
152
- const s = bytesToBigInt(sBytes);
153
- // Check if S is high (> N/2)
154
- if (s <= SECP256K1_HALF_N) {
155
- return signatureHex; // Already low-S
156
- }
157
- // Compute low-S: S' = N - S
158
- const lowS = SECP256K1_N - s;
159
- const lowSBytes = bigIntToBytes(lowS);
160
- // Ensure proper DER encoding (no leading zeros unless needed for sign bit)
161
- const normalizedSBytes = normalizeDerInteger(lowSBytes);
162
- // Build new signature
163
- const rBytes = bytes.slice(rStart, rEnd);
164
- const normalizedRBytes = normalizeDerInteger(rBytes);
165
- const newSigContent = new Uint8Array([
166
- 0x02,
167
- normalizedRBytes.length,
168
- ...normalizedRBytes,
169
- 0x02,
170
- normalizedSBytes.length,
171
- ...normalizedSBytes,
172
- ]);
173
- const newSig = new Uint8Array([0x30, newSigContent.length, ...newSigContent]);
174
- return bytesToHex(newSig);
175
- }
176
- exports.normalizeSignatureToLowS = normalizeSignatureToLowS;
177
- /**
178
- * Normalize a byte array for DER integer encoding
179
- */
180
- function normalizeDerInteger(bytes) {
181
- // Remove leading zeros, but keep one if the high bit is set
182
- let start = 0;
183
- while (start < bytes.length - 1 && bytes[start] === 0 && (bytes[start + 1] & 0x80) === 0) {
184
- start++;
185
- }
186
- // Add leading zero if high bit is set (to indicate positive number)
187
- if (bytes[start] & 0x80) {
188
- const result = new Uint8Array(bytes.length - start + 1);
189
- result[0] = 0;
190
- result.set(bytes.slice(start), 1);
191
- return result;
192
- }
193
- return bytes.slice(start);
194
- }
195
- function hexToBytes(hex) {
196
- const bytes = new Uint8Array(hex.length / 2);
197
- for (let i = 0; i < hex.length; i += 2) {
198
- bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
199
- }
200
- return bytes;
201
- }
202
- function bytesToHex(bytes) {
203
- return Array.from(bytes)
204
- .map((b) => b.toString(16).padStart(2, '0'))
205
- .join('');
206
- }
207
- function bytesToBigInt(bytes) {
208
- let result = 0n;
209
- for (const byte of bytes) {
210
- result = (result << 8n) | BigInt(byte);
211
- }
212
- return result;
213
- }
214
- function bigIntToBytes(n) {
215
- const hex = n.toString(16).padStart(64, '0'); // 32 bytes = 64 hex chars
216
- return hexToBytes(hex);
217
- }