@btc-vision/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.gitattributes +2 -0
  2. package/.github/dependabot.yml +9 -0
  3. package/.github/workflows/ci.yml +48 -0
  4. package/.prettierrc.json +12 -0
  5. package/CONTRIBUTING.md +56 -0
  6. package/LICENSE +190 -0
  7. package/NOTICE +17 -0
  8. package/README.md +509 -0
  9. package/SECURITY.md +35 -0
  10. package/build/commands/AcceptCommand.d.ts +7 -0
  11. package/build/commands/AcceptCommand.js +110 -0
  12. package/build/commands/BaseCommand.d.ts +12 -0
  13. package/build/commands/BaseCommand.js +27 -0
  14. package/build/commands/CompileCommand.d.ts +7 -0
  15. package/build/commands/CompileCommand.js +138 -0
  16. package/build/commands/ConfigCommand.d.ts +17 -0
  17. package/build/commands/ConfigCommand.js +124 -0
  18. package/build/commands/DeprecateCommand.d.ts +7 -0
  19. package/build/commands/DeprecateCommand.js +112 -0
  20. package/build/commands/InfoCommand.d.ts +10 -0
  21. package/build/commands/InfoCommand.js +223 -0
  22. package/build/commands/InitCommand.d.ts +16 -0
  23. package/build/commands/InitCommand.js +336 -0
  24. package/build/commands/InstallCommand.d.ts +7 -0
  25. package/build/commands/InstallCommand.js +130 -0
  26. package/build/commands/KeygenCommand.d.ts +13 -0
  27. package/build/commands/KeygenCommand.js +133 -0
  28. package/build/commands/ListCommand.d.ts +7 -0
  29. package/build/commands/ListCommand.js +117 -0
  30. package/build/commands/LoginCommand.d.ts +9 -0
  31. package/build/commands/LoginCommand.js +139 -0
  32. package/build/commands/LogoutCommand.d.ts +7 -0
  33. package/build/commands/LogoutCommand.js +57 -0
  34. package/build/commands/PublishCommand.d.ts +7 -0
  35. package/build/commands/PublishCommand.js +163 -0
  36. package/build/commands/SearchCommand.d.ts +7 -0
  37. package/build/commands/SearchCommand.js +97 -0
  38. package/build/commands/SignCommand.d.ts +7 -0
  39. package/build/commands/SignCommand.js +80 -0
  40. package/build/commands/TransferCommand.d.ts +8 -0
  41. package/build/commands/TransferCommand.js +179 -0
  42. package/build/commands/UndeprecateCommand.d.ts +7 -0
  43. package/build/commands/UndeprecateCommand.js +95 -0
  44. package/build/commands/UpdateCommand.d.ts +7 -0
  45. package/build/commands/UpdateCommand.js +130 -0
  46. package/build/commands/VerifyCommand.d.ts +7 -0
  47. package/build/commands/VerifyCommand.js +167 -0
  48. package/build/commands/WhoamiCommand.d.ts +7 -0
  49. package/build/commands/WhoamiCommand.js +84 -0
  50. package/build/index.d.ts +2 -0
  51. package/build/index.js +64 -0
  52. package/build/lib/PackageRegistry.abi.d.ts +2 -0
  53. package/build/lib/PackageRegistry.abi.js +356 -0
  54. package/build/lib/binary.d.ts +16 -0
  55. package/build/lib/binary.js +165 -0
  56. package/build/lib/config.d.ts +11 -0
  57. package/build/lib/config.js +160 -0
  58. package/build/lib/credentials.d.ts +10 -0
  59. package/build/lib/credentials.js +89 -0
  60. package/build/lib/ipfs.d.ts +16 -0
  61. package/build/lib/ipfs.js +209 -0
  62. package/build/lib/manifest.d.ts +14 -0
  63. package/build/lib/manifest.js +88 -0
  64. package/build/lib/provider.d.ts +9 -0
  65. package/build/lib/provider.js +48 -0
  66. package/build/lib/registry.d.ts +58 -0
  67. package/build/lib/registry.js +197 -0
  68. package/build/lib/wallet.d.ts +32 -0
  69. package/build/lib/wallet.js +114 -0
  70. package/build/types/PackageRegistry.d.ts +177 -0
  71. package/build/types/PackageRegistry.js +1 -0
  72. package/build/types/index.d.ts +30 -0
  73. package/build/types/index.js +52 -0
  74. package/eslint.config.js +41 -0
  75. package/gulpfile.js +41 -0
  76. package/package.json +83 -0
  77. package/src/commands/AcceptCommand.ts +151 -0
  78. package/src/commands/BaseCommand.ts +59 -0
  79. package/src/commands/CompileCommand.ts +196 -0
  80. package/src/commands/ConfigCommand.ts +144 -0
  81. package/src/commands/DeprecateCommand.ts +156 -0
  82. package/src/commands/InfoCommand.ts +293 -0
  83. package/src/commands/InitCommand.ts +465 -0
  84. package/src/commands/InstallCommand.ts +179 -0
  85. package/src/commands/KeygenCommand.ts +157 -0
  86. package/src/commands/ListCommand.ts +169 -0
  87. package/src/commands/LoginCommand.ts +197 -0
  88. package/src/commands/LogoutCommand.ts +76 -0
  89. package/src/commands/PublishCommand.ts +230 -0
  90. package/src/commands/SearchCommand.ts +141 -0
  91. package/src/commands/SignCommand.ts +122 -0
  92. package/src/commands/TransferCommand.ts +235 -0
  93. package/src/commands/UndeprecateCommand.ts +134 -0
  94. package/src/commands/UpdateCommand.ts +200 -0
  95. package/src/commands/VerifyCommand.ts +228 -0
  96. package/src/commands/WhoamiCommand.ts +113 -0
  97. package/src/index.ts +86 -0
  98. package/src/lib/PackageRegistry.abi.json +765 -0
  99. package/src/lib/PackageRegistry.abi.ts +365 -0
  100. package/src/lib/binary.ts +336 -0
  101. package/src/lib/config.ts +265 -0
  102. package/src/lib/credentials.ts +176 -0
  103. package/src/lib/ipfs.ts +369 -0
  104. package/src/lib/manifest.ts +172 -0
  105. package/src/lib/provider.ts +121 -0
  106. package/src/lib/registry.ts +464 -0
  107. package/src/lib/wallet.ts +271 -0
  108. package/src/types/PackageRegistry.ts +344 -0
  109. package/src/types/index.ts +145 -0
  110. package/tsconfig.json +25 -0
@@ -0,0 +1,365 @@
1
+ /**
2
+ * PackageRegistry Contract ABI
3
+ *
4
+ * Auto-generated from PackageRegistry.abi.json
5
+ */
6
+
7
+ import { BitcoinAbiTypes, BitcoinInterfaceAbi } from 'opnet';
8
+ import { ABIDataTypes } from '@btc-vision/transaction';
9
+
10
+ export const PACKAGE_REGISTRY_ABI: BitcoinInterfaceAbi = [
11
+ // Functions
12
+ {
13
+ name: 'setTreasuryAddress',
14
+ type: BitcoinAbiTypes.Function,
15
+ inputs: [{ name: 'treasuryAddress', type: ABIDataTypes.STRING }],
16
+ outputs: [],
17
+ },
18
+ {
19
+ name: 'setScopePrice',
20
+ type: BitcoinAbiTypes.Function,
21
+ inputs: [{ name: 'priceSats', type: ABIDataTypes.UINT64 }],
22
+ outputs: [],
23
+ },
24
+ {
25
+ name: 'setPackagePrice',
26
+ type: BitcoinAbiTypes.Function,
27
+ inputs: [{ name: 'priceSats', type: ABIDataTypes.UINT64 }],
28
+ outputs: [],
29
+ },
30
+ {
31
+ name: 'registerScope',
32
+ type: BitcoinAbiTypes.Function,
33
+ inputs: [{ name: 'scopeName', type: ABIDataTypes.STRING }],
34
+ outputs: [],
35
+ },
36
+ {
37
+ name: 'initiateScopeTransfer',
38
+ type: BitcoinAbiTypes.Function,
39
+ inputs: [
40
+ { name: 'scopeName', type: ABIDataTypes.STRING },
41
+ { name: 'newOwner', type: ABIDataTypes.ADDRESS },
42
+ ],
43
+ outputs: [],
44
+ },
45
+ {
46
+ name: 'acceptScopeTransfer',
47
+ type: BitcoinAbiTypes.Function,
48
+ inputs: [{ name: 'scopeName', type: ABIDataTypes.STRING }],
49
+ outputs: [],
50
+ },
51
+ {
52
+ name: 'cancelScopeTransfer',
53
+ type: BitcoinAbiTypes.Function,
54
+ inputs: [{ name: 'scopeName', type: ABIDataTypes.STRING }],
55
+ outputs: [],
56
+ },
57
+ {
58
+ name: 'registerPackage',
59
+ type: BitcoinAbiTypes.Function,
60
+ inputs: [{ name: 'packageName', type: ABIDataTypes.STRING }],
61
+ outputs: [],
62
+ },
63
+ {
64
+ name: 'publishVersion',
65
+ type: BitcoinAbiTypes.Function,
66
+ inputs: [
67
+ { name: 'packageName', type: ABIDataTypes.STRING },
68
+ { name: 'version', type: ABIDataTypes.STRING },
69
+ { name: 'ipfsCid', type: ABIDataTypes.STRING },
70
+ { name: 'checksum', type: ABIDataTypes.BYTES32 },
71
+ { name: 'signature', type: ABIDataTypes.BYTES },
72
+ { name: 'mldsaLevel', type: ABIDataTypes.UINT8 },
73
+ { name: 'opnetVersionRange', type: ABIDataTypes.STRING },
74
+ { name: 'pluginType', type: ABIDataTypes.UINT8 },
75
+ { name: 'permissionsHash', type: ABIDataTypes.BYTES32 },
76
+ { name: 'dependencies', type: ABIDataTypes.BYTES },
77
+ ],
78
+ outputs: [],
79
+ },
80
+ {
81
+ name: 'deprecateVersion',
82
+ type: BitcoinAbiTypes.Function,
83
+ inputs: [
84
+ { name: 'packageName', type: ABIDataTypes.STRING },
85
+ { name: 'version', type: ABIDataTypes.STRING },
86
+ { name: 'reason', type: ABIDataTypes.STRING },
87
+ ],
88
+ outputs: [],
89
+ },
90
+ {
91
+ name: 'undeprecateVersion',
92
+ type: BitcoinAbiTypes.Function,
93
+ inputs: [
94
+ { name: 'packageName', type: ABIDataTypes.STRING },
95
+ { name: 'version', type: ABIDataTypes.STRING },
96
+ ],
97
+ outputs: [],
98
+ },
99
+ {
100
+ name: 'initiateTransfer',
101
+ type: BitcoinAbiTypes.Function,
102
+ inputs: [
103
+ { name: 'packageName', type: ABIDataTypes.STRING },
104
+ { name: 'newOwner', type: ABIDataTypes.ADDRESS },
105
+ ],
106
+ outputs: [],
107
+ },
108
+ {
109
+ name: 'acceptTransfer',
110
+ type: BitcoinAbiTypes.Function,
111
+ inputs: [{ name: 'packageName', type: ABIDataTypes.STRING }],
112
+ outputs: [],
113
+ },
114
+ {
115
+ name: 'cancelTransfer',
116
+ type: BitcoinAbiTypes.Function,
117
+ inputs: [{ name: 'packageName', type: ABIDataTypes.STRING }],
118
+ outputs: [],
119
+ },
120
+ {
121
+ name: 'getScope',
122
+ type: BitcoinAbiTypes.Function,
123
+ inputs: [{ name: 'scopeName', type: ABIDataTypes.STRING }],
124
+ outputs: [
125
+ { name: 'exists', type: ABIDataTypes.BOOL },
126
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
127
+ { name: 'createdAt', type: ABIDataTypes.UINT64 },
128
+ ],
129
+ },
130
+ {
131
+ name: 'getScopeOwner',
132
+ type: BitcoinAbiTypes.Function,
133
+ inputs: [{ name: 'scopeName', type: ABIDataTypes.STRING }],
134
+ outputs: [{ name: 'owner', type: ABIDataTypes.ADDRESS }],
135
+ },
136
+ {
137
+ name: 'getPackage',
138
+ type: BitcoinAbiTypes.Function,
139
+ inputs: [{ name: 'packageName', type: ABIDataTypes.STRING }],
140
+ outputs: [
141
+ { name: 'exists', type: ABIDataTypes.BOOL },
142
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
143
+ { name: 'createdAt', type: ABIDataTypes.UINT64 },
144
+ { name: 'versionCount', type: ABIDataTypes.UINT256 },
145
+ { name: 'latestVersion', type: ABIDataTypes.STRING },
146
+ ],
147
+ },
148
+ {
149
+ name: 'getOwner',
150
+ type: BitcoinAbiTypes.Function,
151
+ inputs: [{ name: 'packageName', type: ABIDataTypes.STRING }],
152
+ outputs: [{ name: 'owner', type: ABIDataTypes.ADDRESS }],
153
+ },
154
+ {
155
+ name: 'getVersion',
156
+ type: BitcoinAbiTypes.Function,
157
+ inputs: [
158
+ { name: 'packageName', type: ABIDataTypes.STRING },
159
+ { name: 'version', type: ABIDataTypes.STRING },
160
+ ],
161
+ outputs: [
162
+ { name: 'exists', type: ABIDataTypes.BOOL },
163
+ { name: 'ipfsCid', type: ABIDataTypes.STRING },
164
+ { name: 'checksum', type: ABIDataTypes.BYTES32 },
165
+ { name: 'sigHash', type: ABIDataTypes.BYTES32 },
166
+ { name: 'mldsaLevel', type: ABIDataTypes.UINT8 },
167
+ { name: 'opnetVersionRange', type: ABIDataTypes.STRING },
168
+ { name: 'pluginType', type: ABIDataTypes.UINT8 },
169
+ { name: 'permissionsHash', type: ABIDataTypes.BYTES32 },
170
+ { name: 'depsHash', type: ABIDataTypes.BYTES32 },
171
+ { name: 'publisher', type: ABIDataTypes.ADDRESS },
172
+ { name: 'publishedAt', type: ABIDataTypes.UINT64 },
173
+ { name: 'deprecated', type: ABIDataTypes.BOOL },
174
+ ],
175
+ },
176
+ {
177
+ name: 'isDeprecated',
178
+ type: BitcoinAbiTypes.Function,
179
+ inputs: [
180
+ { name: 'packageName', type: ABIDataTypes.STRING },
181
+ { name: 'version', type: ABIDataTypes.STRING },
182
+ ],
183
+ outputs: [{ name: 'deprecated', type: ABIDataTypes.BOOL }],
184
+ },
185
+ {
186
+ name: 'isImmutable',
187
+ type: BitcoinAbiTypes.Function,
188
+ inputs: [
189
+ { name: 'packageName', type: ABIDataTypes.STRING },
190
+ { name: 'version', type: ABIDataTypes.STRING },
191
+ ],
192
+ outputs: [{ name: 'immutable', type: ABIDataTypes.BOOL }],
193
+ },
194
+ {
195
+ name: 'getPendingTransfer',
196
+ type: BitcoinAbiTypes.Function,
197
+ inputs: [{ name: 'packageName', type: ABIDataTypes.STRING }],
198
+ outputs: [
199
+ { name: 'pendingOwner', type: ABIDataTypes.ADDRESS },
200
+ { name: 'initiatedAt', type: ABIDataTypes.UINT64 },
201
+ ],
202
+ },
203
+ {
204
+ name: 'getPendingScopeTransfer',
205
+ type: BitcoinAbiTypes.Function,
206
+ inputs: [{ name: 'scopeName', type: ABIDataTypes.STRING }],
207
+ outputs: [
208
+ { name: 'pendingOwner', type: ABIDataTypes.ADDRESS },
209
+ { name: 'initiatedAt', type: ABIDataTypes.UINT64 },
210
+ ],
211
+ },
212
+ {
213
+ name: 'getTreasuryAddress',
214
+ type: BitcoinAbiTypes.Function,
215
+ inputs: [],
216
+ outputs: [{ name: 'treasuryAddress', type: ABIDataTypes.STRING }],
217
+ },
218
+ {
219
+ name: 'getScopePrice',
220
+ type: BitcoinAbiTypes.Function,
221
+ inputs: [],
222
+ outputs: [{ name: 'priceSats', type: ABIDataTypes.UINT64 }],
223
+ },
224
+ {
225
+ name: 'getPackagePrice',
226
+ type: BitcoinAbiTypes.Function,
227
+ inputs: [],
228
+ outputs: [{ name: 'priceSats', type: ABIDataTypes.UINT64 }],
229
+ },
230
+ // Events
231
+ {
232
+ name: 'TreasuryAddressChanged',
233
+ type: BitcoinAbiTypes.Event,
234
+ values: [
235
+ { name: 'previousAddressHash', type: ABIDataTypes.UINT256 },
236
+ { name: 'newAddressHash', type: ABIDataTypes.UINT256 },
237
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
238
+ ],
239
+ },
240
+ {
241
+ name: 'ScopePriceChanged',
242
+ type: BitcoinAbiTypes.Event,
243
+ values: [
244
+ { name: 'oldPrice', type: ABIDataTypes.UINT64 },
245
+ { name: 'newPrice', type: ABIDataTypes.UINT64 },
246
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
247
+ ],
248
+ },
249
+ {
250
+ name: 'PackagePriceChanged',
251
+ type: BitcoinAbiTypes.Event,
252
+ values: [
253
+ { name: 'oldPrice', type: ABIDataTypes.UINT64 },
254
+ { name: 'newPrice', type: ABIDataTypes.UINT64 },
255
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
256
+ ],
257
+ },
258
+ {
259
+ name: 'ScopeRegistered',
260
+ type: BitcoinAbiTypes.Event,
261
+ values: [
262
+ { name: 'scopeHash', type: ABIDataTypes.UINT256 },
263
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
264
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
265
+ ],
266
+ },
267
+ {
268
+ name: 'ScopeTransferInitiated',
269
+ type: BitcoinAbiTypes.Event,
270
+ values: [
271
+ { name: 'scopeHash', type: ABIDataTypes.UINT256 },
272
+ { name: 'currentOwner', type: ABIDataTypes.ADDRESS },
273
+ { name: 'newOwner', type: ABIDataTypes.ADDRESS },
274
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
275
+ ],
276
+ },
277
+ {
278
+ name: 'ScopeTransferCompleted',
279
+ type: BitcoinAbiTypes.Event,
280
+ values: [
281
+ { name: 'scopeHash', type: ABIDataTypes.UINT256 },
282
+ { name: 'previousOwner', type: ABIDataTypes.ADDRESS },
283
+ { name: 'newOwner', type: ABIDataTypes.ADDRESS },
284
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
285
+ ],
286
+ },
287
+ {
288
+ name: 'ScopeTransferCancelled',
289
+ type: BitcoinAbiTypes.Event,
290
+ values: [
291
+ { name: 'scopeHash', type: ABIDataTypes.UINT256 },
292
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
293
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
294
+ ],
295
+ },
296
+ {
297
+ name: 'PackageRegistered',
298
+ type: BitcoinAbiTypes.Event,
299
+ values: [
300
+ { name: 'packageHash', type: ABIDataTypes.UINT256 },
301
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
302
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
303
+ ],
304
+ },
305
+ {
306
+ name: 'VersionPublished',
307
+ type: BitcoinAbiTypes.Event,
308
+ values: [
309
+ { name: 'packageHash', type: ABIDataTypes.UINT256 },
310
+ { name: 'versionHash', type: ABIDataTypes.UINT256 },
311
+ { name: 'publisher', type: ABIDataTypes.ADDRESS },
312
+ { name: 'checksum', type: ABIDataTypes.UINT256 },
313
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
314
+ { name: 'mldsaLevel', type: ABIDataTypes.UINT8 },
315
+ { name: 'pluginType', type: ABIDataTypes.UINT8 },
316
+ ],
317
+ },
318
+ {
319
+ name: 'VersionDeprecated',
320
+ type: BitcoinAbiTypes.Event,
321
+ values: [
322
+ { name: 'packageHash', type: ABIDataTypes.UINT256 },
323
+ { name: 'versionHash', type: ABIDataTypes.UINT256 },
324
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
325
+ ],
326
+ },
327
+ {
328
+ name: 'VersionUndeprecated',
329
+ type: BitcoinAbiTypes.Event,
330
+ values: [
331
+ { name: 'packageHash', type: ABIDataTypes.UINT256 },
332
+ { name: 'versionHash', type: ABIDataTypes.UINT256 },
333
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
334
+ ],
335
+ },
336
+ {
337
+ name: 'PackageTransferInitiated',
338
+ type: BitcoinAbiTypes.Event,
339
+ values: [
340
+ { name: 'packageHash', type: ABIDataTypes.UINT256 },
341
+ { name: 'currentOwner', type: ABIDataTypes.ADDRESS },
342
+ { name: 'newOwner', type: ABIDataTypes.ADDRESS },
343
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
344
+ ],
345
+ },
346
+ {
347
+ name: 'PackageTransferCompleted',
348
+ type: BitcoinAbiTypes.Event,
349
+ values: [
350
+ { name: 'packageHash', type: ABIDataTypes.UINT256 },
351
+ { name: 'previousOwner', type: ABIDataTypes.ADDRESS },
352
+ { name: 'newOwner', type: ABIDataTypes.ADDRESS },
353
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
354
+ ],
355
+ },
356
+ {
357
+ name: 'PackageTransferCancelled',
358
+ type: BitcoinAbiTypes.Event,
359
+ values: [
360
+ { name: 'packageHash', type: ABIDataTypes.UINT256 },
361
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
362
+ { name: 'timestamp', type: ABIDataTypes.UINT64 },
363
+ ],
364
+ },
365
+ ];
@@ -0,0 +1,336 @@
1
+ /**
2
+ * .opnet Binary Format Parser/Writer
3
+ *
4
+ * Implements the OIP-0003 binary format for compiled plugins.
5
+ *
6
+ * @module lib/binary
7
+ */
8
+
9
+ import * as crypto from 'crypto';
10
+ import {
11
+ IParsedPluginFile,
12
+ IPluginMetadata,
13
+ MLDSA_PUBLIC_KEY_SIZES,
14
+ MLDSA_SIGNATURE_SIZES,
15
+ MLDSALevel,
16
+ PLUGIN_FORMAT_VERSION,
17
+ PLUGIN_MAGIC_BYTES,
18
+ } from '@btc-vision/plugin-sdk';
19
+
20
+ import { cliLevelToMLDSALevel, CLIMldsaLevel, mldsaLevelToCLI } from '../types/index.js';
21
+
22
+ /**
23
+ * Parse a .opnet binary file
24
+ *
25
+ * @param data - The binary file contents
26
+ * @returns Parsed binary structure
27
+ * @throws Error if the binary is malformed
28
+ */
29
+ export function parseOpnetBinary(data: Buffer): IParsedPluginFile {
30
+ let offset = 0;
31
+
32
+ // Check minimum size
33
+ if (data.length < 8 + 4 + 1) {
34
+ throw new Error('Binary too small: missing header');
35
+ }
36
+
37
+ // Magic bytes (8 bytes)
38
+ const magic = data.subarray(offset, offset + 8);
39
+ offset += 8;
40
+
41
+ if (!magic.equals(PLUGIN_MAGIC_BYTES)) {
42
+ throw new Error(
43
+ `Invalid magic bytes: expected ${PLUGIN_MAGIC_BYTES.toString('hex')}, got ${magic.toString('hex')}`,
44
+ );
45
+ }
46
+
47
+ // Format version (uint32 LE)
48
+ const formatVersion = data.readUInt32LE(offset);
49
+ offset += 4;
50
+
51
+ if (formatVersion !== PLUGIN_FORMAT_VERSION) {
52
+ throw new Error(
53
+ `Unsupported format version: ${formatVersion} (expected ${PLUGIN_FORMAT_VERSION})`,
54
+ );
55
+ }
56
+
57
+ // MLDSA level (uint8)
58
+ const mldsaLevelValue = data.readUInt8(offset);
59
+ offset += 1;
60
+
61
+ if (mldsaLevelValue > 2) {
62
+ throw new Error(`Invalid MLDSA level: ${mldsaLevelValue} (must be 0, 1, or 2)`);
63
+ }
64
+
65
+ const mldsaLevel = mldsaLevelValue as MLDSALevel;
66
+
67
+ // Get sizes based on level
68
+ const publicKeySize = MLDSA_PUBLIC_KEY_SIZES[mldsaLevel];
69
+ const signatureSize = MLDSA_SIGNATURE_SIZES[mldsaLevel];
70
+
71
+ // Check we have enough data
72
+ const minSize = offset + publicKeySize + signatureSize + 4 + 4 + 4 + 32;
73
+ if (data.length < minSize) {
74
+ throw new Error(`Binary truncated: expected at least ${minSize} bytes, got ${data.length}`);
75
+ }
76
+
77
+ // Public key
78
+ const publicKey = Buffer.from(data.subarray(offset, offset + publicKeySize));
79
+ offset += publicKeySize;
80
+
81
+ // Signature
82
+ const signature = Buffer.from(data.subarray(offset, offset + signatureSize));
83
+ offset += signatureSize;
84
+
85
+ // Metadata length (uint32 LE)
86
+ const metadataLength = data.readUInt32LE(offset);
87
+ offset += 4;
88
+
89
+ if (offset + metadataLength > data.length) {
90
+ throw new Error(`Metadata section truncated at offset ${offset}`);
91
+ }
92
+
93
+ // Metadata
94
+ const metadataBytes = data.subarray(offset, offset + metadataLength);
95
+ offset += metadataLength;
96
+
97
+ let rawMetadata: string;
98
+ let metadata: IPluginMetadata;
99
+ try {
100
+ rawMetadata = metadataBytes.toString('utf-8');
101
+ metadata = JSON.parse(rawMetadata) as IPluginMetadata;
102
+ } catch {
103
+ throw new Error('Malformed JSON metadata');
104
+ }
105
+
106
+ // Bytecode length (uint32 LE)
107
+ const bytecodeLength = data.readUInt32LE(offset);
108
+ offset += 4;
109
+
110
+ if (offset + bytecodeLength > data.length) {
111
+ throw new Error(`Bytecode section truncated at offset ${offset}`);
112
+ }
113
+
114
+ // Bytecode
115
+ const bytecode = Buffer.from(data.subarray(offset, offset + bytecodeLength));
116
+ offset += bytecodeLength;
117
+
118
+ // Proto length (uint32 LE)
119
+ const protoLength = data.readUInt32LE(offset);
120
+ offset += 4;
121
+
122
+ if (offset + protoLength > data.length) {
123
+ throw new Error(`Proto section truncated at offset ${offset}`);
124
+ }
125
+
126
+ // Proto
127
+ const proto =
128
+ protoLength > 0 ? Buffer.from(data.subarray(offset, offset + protoLength)) : undefined;
129
+ offset += protoLength;
130
+
131
+ // Checksum (32 bytes)
132
+ if (offset + 32 > data.length) {
133
+ throw new Error('Checksum truncated');
134
+ }
135
+
136
+ const checksum = Buffer.from(data.subarray(offset, offset + 32));
137
+
138
+ return {
139
+ formatVersion,
140
+ mldsaLevel,
141
+ publicKey,
142
+ signature,
143
+ metadata,
144
+ rawMetadata,
145
+ bytecode,
146
+ proto,
147
+ checksum,
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Compute the checksum for plugin data
153
+ *
154
+ * @param metadata - JSON metadata bytes
155
+ * @param bytecode - V8 bytecode bytes
156
+ * @param proto - Proto bytes (can be empty)
157
+ * @returns SHA-256 hash
158
+ */
159
+ export function computeChecksum(metadata: Buffer, bytecode: Buffer, proto: Buffer): Buffer {
160
+ const hash = crypto.createHash('sha256');
161
+ hash.update(metadata);
162
+ hash.update(bytecode);
163
+ hash.update(proto);
164
+ return hash.digest();
165
+ }
166
+
167
+ /**
168
+ * Verify the checksum of parsed binary
169
+ *
170
+ * @param parsed - The parsed binary structure
171
+ * @returns True if checksum matches
172
+ */
173
+ export function verifyChecksum(parsed: IParsedPluginFile): boolean {
174
+ const metadataBytes = Buffer.from(parsed.rawMetadata, 'utf-8');
175
+ const computed = computeChecksum(
176
+ metadataBytes,
177
+ parsed.bytecode,
178
+ parsed.proto ?? Buffer.alloc(0),
179
+ );
180
+ return computed.equals(parsed.checksum);
181
+ }
182
+
183
+ /**
184
+ * Build a .opnet binary file
185
+ *
186
+ * @param options - Build options
187
+ * @returns The assembled binary
188
+ */
189
+ export function buildOpnetBinary(options: {
190
+ mldsaLevel: CLIMldsaLevel;
191
+ publicKey: Buffer;
192
+ signature: Buffer;
193
+ metadata: IPluginMetadata;
194
+ bytecode: Buffer;
195
+ proto?: Buffer;
196
+ }): Buffer {
197
+ const {
198
+ mldsaLevel,
199
+ publicKey,
200
+ signature,
201
+ metadata,
202
+ bytecode,
203
+ proto = Buffer.alloc(0),
204
+ } = options;
205
+
206
+ const sdkLevel = cliLevelToMLDSALevel(mldsaLevel);
207
+
208
+ // Validate sizes
209
+ const expectedPkSize = MLDSA_PUBLIC_KEY_SIZES[sdkLevel];
210
+ const expectedSigSize = MLDSA_SIGNATURE_SIZES[sdkLevel];
211
+
212
+ if (publicKey.length !== expectedPkSize) {
213
+ throw new Error(
214
+ `Public key size mismatch: expected ${expectedPkSize}, got ${publicKey.length}`,
215
+ );
216
+ }
217
+
218
+ if (signature.length !== expectedSigSize) {
219
+ throw new Error(
220
+ `Signature size mismatch: expected ${expectedSigSize}, got ${signature.length}`,
221
+ );
222
+ }
223
+
224
+ // Serialize metadata
225
+ const metadataStr = JSON.stringify(metadata);
226
+ const metadataBytes = Buffer.from(metadataStr, 'utf-8');
227
+
228
+ // Compute checksum
229
+ const checksum = computeChecksum(metadataBytes, bytecode, proto);
230
+
231
+ // Calculate total size
232
+ const totalSize =
233
+ 8 + // magic
234
+ 4 + // version
235
+ 1 + // mldsa level
236
+ publicKey.length +
237
+ signature.length +
238
+ 4 +
239
+ metadataBytes.length + // metadata
240
+ 4 +
241
+ bytecode.length + // bytecode
242
+ 4 +
243
+ proto.length + // proto
244
+ 32; // checksum
245
+
246
+ // Build buffer
247
+ const buffer = Buffer.alloc(totalSize);
248
+ let offset = 0;
249
+
250
+ // Magic bytes
251
+ PLUGIN_MAGIC_BYTES.copy(buffer, offset);
252
+ offset += 8;
253
+
254
+ // Format version
255
+ buffer.writeUInt32LE(PLUGIN_FORMAT_VERSION, offset);
256
+ offset += 4;
257
+
258
+ // MLDSA level (enum value 0, 1, or 2)
259
+ buffer.writeUInt8(sdkLevel, offset);
260
+ offset += 1;
261
+
262
+ // Public key
263
+ publicKey.copy(buffer, offset);
264
+ offset += publicKey.length;
265
+
266
+ // Signature
267
+ signature.copy(buffer, offset);
268
+ offset += signature.length;
269
+
270
+ // Metadata length
271
+ buffer.writeUInt32LE(metadataBytes.length, offset);
272
+ offset += 4;
273
+
274
+ // Metadata
275
+ metadataBytes.copy(buffer, offset);
276
+ offset += metadataBytes.length;
277
+
278
+ // Bytecode length
279
+ buffer.writeUInt32LE(bytecode.length, offset);
280
+ offset += 4;
281
+
282
+ // Bytecode
283
+ bytecode.copy(buffer, offset);
284
+ offset += bytecode.length;
285
+
286
+ // Proto length
287
+ buffer.writeUInt32LE(proto.length, offset);
288
+ offset += 4;
289
+
290
+ // Proto
291
+ proto.copy(buffer, offset);
292
+ offset += proto.length;
293
+
294
+ // Checksum
295
+ checksum.copy(buffer, offset);
296
+
297
+ return buffer;
298
+ }
299
+
300
+ /**
301
+ * Extract just the metadata from a .opnet file without full validation
302
+ *
303
+ * @param data - The binary file contents
304
+ * @returns The parsed metadata or null if invalid
305
+ */
306
+ export function extractMetadata(data: Buffer): IPluginMetadata | null {
307
+ try {
308
+ const parsed = parseOpnetBinary(data);
309
+ return parsed.metadata;
310
+ } catch {
311
+ return null;
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Get the CLI MLDSA level from parsed binary
317
+ */
318
+ export function getParsedMldsaLevel(parsed: IParsedPluginFile): CLIMldsaLevel {
319
+ return mldsaLevelToCLI(parsed.mldsaLevel);
320
+ }
321
+
322
+ /**
323
+ * Get file size formatted as human-readable string
324
+ *
325
+ * @param bytes - File size in bytes
326
+ * @returns Formatted string (e.g., "1.5 MB")
327
+ */
328
+ export function formatFileSize(bytes: number): string {
329
+ if (bytes < 1024) {
330
+ return `${bytes} B`;
331
+ } else if (bytes < 1024 * 1024) {
332
+ return `${(bytes / 1024).toFixed(1)} KB`;
333
+ } else {
334
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
335
+ }
336
+ }