@btc-vision/cli 1.0.13 → 1.1.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.
- package/README.md +1087 -191
- package/build/commands/CompileCommand.js +5 -5
- package/build/commands/DomainCommand.js +3 -2
- package/build/commands/KeygenCommand.js +7 -6
- package/build/commands/LoginCommand.js +10 -0
- package/build/commands/PublishCommand.js +2 -2
- package/build/commands/SignCommand.js +3 -3
- package/build/commands/VerifyCommand.js +2 -2
- package/build/commands/WhoamiCommand.js +2 -1
- package/build/lib/binary.d.ts +10 -9
- package/build/lib/binary.js +50 -34
- package/build/lib/credentials.js +25 -3
- package/build/lib/ipfs.js +93 -22
- package/build/lib/manifest.js +3 -1
- package/build/lib/provider.js +5 -1
- package/build/lib/registry.js +19 -2
- package/build/lib/transaction.js +8 -3
- package/build/lib/wallet.d.ts +10 -10
- package/build/lib/wallet.js +6 -7
- package/package.json +19 -19
|
@@ -4,7 +4,7 @@ import * as esbuild from 'esbuild';
|
|
|
4
4
|
import bytenode from 'bytenode';
|
|
5
5
|
import { BaseCommand } from './BaseCommand.js';
|
|
6
6
|
import { getManifestPath, loadManifest } from '../lib/manifest.js';
|
|
7
|
-
import { buildOpnetBinary, formatFileSize } from '../lib/binary.js';
|
|
7
|
+
import { buildOpnetBinary, formatFileSize, toHex } from '../lib/binary.js';
|
|
8
8
|
import { CLIWallet } from '../lib/wallet.js';
|
|
9
9
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
10
10
|
export class CompileCommand extends BaseCommand {
|
|
@@ -63,7 +63,7 @@ export class CompileCommand extends BaseCommand {
|
|
|
63
63
|
});
|
|
64
64
|
const bytecode = fs.readFileSync(bytecodePath);
|
|
65
65
|
this.logger.success(`V8 bytecode generated (${formatFileSize(bytecode.length)})`);
|
|
66
|
-
let proto =
|
|
66
|
+
let proto = new Uint8Array(0);
|
|
67
67
|
const protoPath = path.join(projectDir, 'plugin.proto');
|
|
68
68
|
if (fs.existsSync(protoPath)) {
|
|
69
69
|
proto = fs.readFileSync(protoPath);
|
|
@@ -90,7 +90,7 @@ export class CompileCommand extends BaseCommand {
|
|
|
90
90
|
else {
|
|
91
91
|
this.logger.warn('Skipping signing (--no-sign)');
|
|
92
92
|
mldsaLevel = 44;
|
|
93
|
-
publicKey =
|
|
93
|
+
publicKey = new Uint8Array(1312);
|
|
94
94
|
}
|
|
95
95
|
this.logger.info('Assembling .opnet binary...');
|
|
96
96
|
const { binary, checksum } = buildOpnetBinary({
|
|
@@ -102,7 +102,7 @@ export class CompileCommand extends BaseCommand {
|
|
|
102
102
|
signFn,
|
|
103
103
|
});
|
|
104
104
|
if (options.sign) {
|
|
105
|
-
this.logger.success(`Plugin signed (checksum: sha256:${checksum
|
|
105
|
+
this.logger.success(`Plugin signed (checksum: sha256:${toHex(checksum).substring(0, 16)}...)`);
|
|
106
106
|
}
|
|
107
107
|
this.logger.success(`Binary assembled (${formatFileSize(binary.length)})`);
|
|
108
108
|
const outputPath = options.output ||
|
|
@@ -119,7 +119,7 @@ export class CompileCommand extends BaseCommand {
|
|
|
119
119
|
this.logger.log(`Plugin: ${manifest.name}@${manifest.version}`);
|
|
120
120
|
this.logger.log(`Type: ${manifest.pluginType}`);
|
|
121
121
|
this.logger.log(`MLDSA Level: ${mldsaLevel}`);
|
|
122
|
-
this.logger.log(`Checksum: sha256:${checksum
|
|
122
|
+
this.logger.log(`Checksum: sha256:${toHex(checksum)}`);
|
|
123
123
|
this.logger.log(`Signed: ${options.sign ? 'Yes' : 'No'}`);
|
|
124
124
|
this.logger.log('');
|
|
125
125
|
if (!options.sign) {
|
|
@@ -2,6 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { confirm } from '@inquirer/prompts';
|
|
3
3
|
import { Logger } from '@btc-vision/logger';
|
|
4
4
|
import { CLIWallet } from '../lib/wallet.js';
|
|
5
|
+
import { toHex } from '../lib/binary.js';
|
|
5
6
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
6
7
|
import { getContenthash, getContenthashTypeName, getDomain, getDomainPrice, getResolverContract, getTreasuryAddress, parseDomainName, validateDomainName, } from '../lib/resolver.js';
|
|
7
8
|
import { DEFAULT_DOMAIN_PRICE_SATS, PREMIUM_TIER_0_PRICE_SATS, PREMIUM_TIER_1_PRICE_SATS, PREMIUM_TIER_2_PRICE_SATS, PREMIUM_TIER_3_PRICE_SATS, PREMIUM_TIER_4_PRICE_SATS, PREMIUM_TIER_5_PRICE_SATS, PREMIUM_TIER_6_PRICE_SATS, } from '../types/BtcResolver.js';
|
|
@@ -126,7 +127,7 @@ async function registerDomain(domain, options) {
|
|
|
126
127
|
const contract = getResolverContract(network, sender);
|
|
127
128
|
const extraUtxo = {
|
|
128
129
|
address: treasuryAddr,
|
|
129
|
-
value:
|
|
130
|
+
value: price,
|
|
130
131
|
};
|
|
131
132
|
const outSimulation = [
|
|
132
133
|
{
|
|
@@ -237,7 +238,7 @@ async function domainInfo(domain, options) {
|
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
else {
|
|
240
|
-
const hashHex =
|
|
241
|
+
const hashHex = toHex(new Uint8Array(contenthash.hashData));
|
|
241
242
|
logger.log(`Value: ${hashHex}`);
|
|
242
243
|
}
|
|
243
244
|
}
|
|
@@ -2,6 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import { BaseCommand } from './BaseCommand.js';
|
|
4
4
|
import { computePublicKeyHash, generateMLDSAKeypair, generateMnemonic } from '../lib/wallet.js';
|
|
5
|
+
import { toHex } from '../lib/binary.js';
|
|
5
6
|
import { isValidMldsaLevel } from '../lib/credentials.js';
|
|
6
7
|
export class KeygenCommand extends BaseCommand {
|
|
7
8
|
constructor() {
|
|
@@ -68,10 +69,10 @@ export class KeygenCommand extends BaseCommand {
|
|
|
68
69
|
if (options.output) {
|
|
69
70
|
const privateKeyPath = `${options.output}.private.key`;
|
|
70
71
|
const publicKeyPath = `${options.output}.public.key`;
|
|
71
|
-
fs.writeFileSync(privateKeyPath, keypair.privateKey
|
|
72
|
+
fs.writeFileSync(privateKeyPath, toHex(keypair.privateKey) + '\n', {
|
|
72
73
|
mode: 0o600,
|
|
73
74
|
});
|
|
74
|
-
fs.writeFileSync(publicKeyPath, keypair.publicKey
|
|
75
|
+
fs.writeFileSync(publicKeyPath, toHex(keypair.publicKey) + '\n', {
|
|
75
76
|
mode: 0o644,
|
|
76
77
|
});
|
|
77
78
|
this.logger.success('Keys generated successfully!');
|
|
@@ -85,8 +86,8 @@ export class KeygenCommand extends BaseCommand {
|
|
|
85
86
|
else if (options.json) {
|
|
86
87
|
const output = {
|
|
87
88
|
level: levelNum,
|
|
88
|
-
privateKey: keypair.privateKey
|
|
89
|
-
publicKey: keypair.publicKey
|
|
89
|
+
privateKey: toHex(keypair.privateKey),
|
|
90
|
+
publicKey: toHex(keypair.publicKey),
|
|
90
91
|
publicKeyHash,
|
|
91
92
|
privateKeySize: keypair.privateKey.length,
|
|
92
93
|
publicKeySize: keypair.publicKey.length,
|
|
@@ -100,10 +101,10 @@ export class KeygenCommand extends BaseCommand {
|
|
|
100
101
|
this.logger.log(`Private Key Size: ${keypair.privateKey.length} bytes`);
|
|
101
102
|
this.logger.log('');
|
|
102
103
|
this.logger.log('Public Key (hex):');
|
|
103
|
-
this.logger.log(keypair.publicKey
|
|
104
|
+
this.logger.log(toHex(keypair.publicKey));
|
|
104
105
|
this.logger.log('');
|
|
105
106
|
this.logger.log('Private Key (hex):');
|
|
106
|
-
this.logger.log(keypair.privateKey
|
|
107
|
+
this.logger.log(toHex(keypair.privateKey));
|
|
107
108
|
this.logger.log('');
|
|
108
109
|
this.logger.warn('IMPORTANT: Store the private key securely!');
|
|
109
110
|
this.logger.warn('Use --output <prefix> to save to files.');
|
|
@@ -17,6 +17,16 @@ export class LoginCommand extends BaseCommand {
|
|
|
17
17
|
}
|
|
18
18
|
async execute(options) {
|
|
19
19
|
try {
|
|
20
|
+
if (options.mnemonic) {
|
|
21
|
+
this.logger.warn('WARNING: Mnemonic passed via CLI argument. It may be visible in shell history and process list.');
|
|
22
|
+
this.logger.warn('Consider using interactive mode instead: opnet login');
|
|
23
|
+
this.logger.log('');
|
|
24
|
+
}
|
|
25
|
+
if (options.wif || options.mldsa) {
|
|
26
|
+
this.logger.warn('WARNING: Private key passed via CLI argument. It may be visible in shell history and process list.');
|
|
27
|
+
this.logger.warn('Consider using interactive mode or environment variables instead.');
|
|
28
|
+
this.logger.log('');
|
|
29
|
+
}
|
|
20
30
|
const credentials = await this.buildCredentials(options);
|
|
21
31
|
this.logger.info('Deriving wallet...');
|
|
22
32
|
const wallet = CLIWallet.fromCredentials(credentials);
|
|
@@ -135,7 +135,7 @@ export class PublishCommand extends BaseCommand {
|
|
|
135
135
|
const treasuryAddress = await contract.getTreasuryAddress();
|
|
136
136
|
const extraUtxo = {
|
|
137
137
|
address: treasuryAddress.properties.treasuryAddress,
|
|
138
|
-
value:
|
|
138
|
+
value: 10000n,
|
|
139
139
|
};
|
|
140
140
|
let txParams = buildTransactionParams(wallet, network, DEFAULT_MAX_SAT_TO_SPEND, DEFAULT_FEE_RATE, extraUtxo);
|
|
141
141
|
if (isNewPackage) {
|
|
@@ -184,7 +184,7 @@ export class PublishCommand extends BaseCommand {
|
|
|
184
184
|
}
|
|
185
185
|
txParams = buildTransactionParams(wallet, network, DEFAULT_MAX_SAT_TO_SPEND, DEFAULT_FEE_RATE);
|
|
186
186
|
this.logger.info('Publishing version...');
|
|
187
|
-
const publishResult = await contract.publishVersion(meta.name, meta.version, pinResult.cid,
|
|
187
|
+
const publishResult = await contract.publishVersion(meta.name, meta.version, pinResult.cid, parsed.checksum, parsed.signature, mldsaLevelToRegistry(mldsaLevel), meta.opnetVersion, pluginTypeToRegistry(meta.pluginType), permissionsHash, dependencies);
|
|
188
188
|
if (publishResult.revert) {
|
|
189
189
|
this.logger.fail('Version publishing would fail');
|
|
190
190
|
this.logger.error(`Reason: ${publishResult.revert}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as crypto from 'crypto';
|
|
3
3
|
import { BaseCommand } from './BaseCommand.js';
|
|
4
|
-
import { buildOpnetBinary, formatFileSize, parseOpnetBinary } from '../lib/binary.js';
|
|
4
|
+
import { buildOpnetBinary, formatFileSize, parseOpnetBinary, toHex } from '../lib/binary.js';
|
|
5
5
|
import { CLIWallet } from '../lib/wallet.js';
|
|
6
6
|
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
7
7
|
export class SignCommand extends BaseCommand {
|
|
@@ -55,10 +55,10 @@ export class SignCommand extends BaseCommand {
|
|
|
55
55
|
publicKey: wallet.mldsaPublicKey,
|
|
56
56
|
metadata: parsed.metadata,
|
|
57
57
|
bytecode: parsed.bytecode,
|
|
58
|
-
proto: parsed.proto
|
|
58
|
+
proto: parsed.proto,
|
|
59
59
|
signFn,
|
|
60
60
|
});
|
|
61
|
-
this.logger.success(`Signed (checksum: sha256:${checksum
|
|
61
|
+
this.logger.success(`Signed (checksum: sha256:${toHex(checksum).substring(0, 16)}...)`);
|
|
62
62
|
this.logger.success(`Binary rebuilt (${formatFileSize(newBinary.length)})`);
|
|
63
63
|
const outputPath = options.output || file;
|
|
64
64
|
fs.writeFileSync(outputPath, newBinary);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as crypto from 'crypto';
|
|
3
3
|
import { BaseCommand } from './BaseCommand.js';
|
|
4
|
-
import { formatFileSize, parseOpnetBinary, verifyChecksum } from '../lib/binary.js';
|
|
4
|
+
import { formatFileSize, parseOpnetBinary, toHex, verifyChecksum } from '../lib/binary.js';
|
|
5
5
|
import { CLIWallet } from '../lib/wallet.js';
|
|
6
6
|
export class VerifyCommand extends BaseCommand {
|
|
7
7
|
constructor() {
|
|
@@ -136,7 +136,7 @@ export class VerifyCommand extends BaseCommand {
|
|
|
136
136
|
this.logger.log(` Proto: ${formatFileSize(parsed.proto?.length ?? 0)}`);
|
|
137
137
|
this.logger.log('');
|
|
138
138
|
this.logger.log('Checksums:');
|
|
139
|
-
this.logger.log(` Stored: ${parsed.checksum
|
|
139
|
+
this.logger.log(` Stored: ${toHex(parsed.checksum)}`);
|
|
140
140
|
this.logger.log('');
|
|
141
141
|
this.logger.log('Author:');
|
|
142
142
|
this.logger.log(` Name: ${parsed.metadata.author.name}`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseCommand } from './BaseCommand.js';
|
|
2
2
|
import { getCredentialSource, hasCredentials, loadCredentials, maskSensitive, } from '../lib/credentials.js';
|
|
3
3
|
import { CLIWallet } from '../lib/wallet.js';
|
|
4
|
+
import { toHex } from '../lib/binary.js';
|
|
4
5
|
export class WhoamiCommand extends BaseCommand {
|
|
5
6
|
constructor() {
|
|
6
7
|
super('whoami', 'Display current wallet identity and configuration');
|
|
@@ -37,7 +38,7 @@ export class WhoamiCommand extends BaseCommand {
|
|
|
37
38
|
if (options.publicKey) {
|
|
38
39
|
this.logger.log('');
|
|
39
40
|
this.logger.log('MLDSA Public Key:');
|
|
40
|
-
this.logger.log(wallet.mldsaPublicKey
|
|
41
|
+
this.logger.log(toHex(wallet.mldsaPublicKey));
|
|
41
42
|
}
|
|
42
43
|
if (options.verbose) {
|
|
43
44
|
this.logger.log('');
|
package/build/lib/binary.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { IParsedPluginFile, IPluginMetadata } from '@btc-vision/plugin-sdk';
|
|
2
2
|
import { CLIMldsaLevel } from '../types/index.js';
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
3
|
+
export declare function toHex(data: Uint8Array): string;
|
|
4
|
+
export declare function parseOpnetBinary(data: Uint8Array): IParsedPluginFile;
|
|
5
|
+
export declare function computeChecksum(metadata: Uint8Array, bytecode: Uint8Array, proto: Uint8Array): Uint8Array;
|
|
5
6
|
export declare function verifyChecksum(parsed: IParsedPluginFile): boolean;
|
|
6
7
|
export declare function buildOpnetBinary(options: {
|
|
7
8
|
mldsaLevel: CLIMldsaLevel;
|
|
8
|
-
publicKey:
|
|
9
|
+
publicKey: Uint8Array;
|
|
9
10
|
metadata: IPluginMetadata;
|
|
10
|
-
bytecode:
|
|
11
|
-
proto?:
|
|
12
|
-
signFn?: (checksum:
|
|
11
|
+
bytecode: Uint8Array;
|
|
12
|
+
proto?: Uint8Array;
|
|
13
|
+
signFn?: (checksum: Uint8Array) => Uint8Array;
|
|
13
14
|
}): {
|
|
14
|
-
binary:
|
|
15
|
-
checksum:
|
|
15
|
+
binary: Uint8Array;
|
|
16
|
+
checksum: Uint8Array;
|
|
16
17
|
};
|
|
17
|
-
export declare function extractMetadata(data:
|
|
18
|
+
export declare function extractMetadata(data: Uint8Array): IPluginMetadata | null;
|
|
18
19
|
export declare function getParsedMldsaLevel(parsed: IParsedPluginFile): CLIMldsaLevel;
|
|
19
20
|
export declare function formatFileSize(bytes: number): string;
|
package/build/lib/binary.js
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
2
|
import { MLDSA_PUBLIC_KEY_SIZES, MLDSA_SIGNATURE_SIZES, PLUGIN_FORMAT_VERSION, PLUGIN_MAGIC_BYTES, } from '@btc-vision/plugin-sdk';
|
|
3
3
|
import { cliLevelToMLDSALevel, mldsaLevelToCLI } from '../types/index.js';
|
|
4
|
+
const HEX_CHARS = '0123456789abcdef';
|
|
5
|
+
export function toHex(data) {
|
|
6
|
+
let hex = '';
|
|
7
|
+
for (let i = 0; i < data.length; i++) {
|
|
8
|
+
hex += HEX_CHARS[data[i] >> 4] + HEX_CHARS[data[i] & 0xf];
|
|
9
|
+
}
|
|
10
|
+
return hex;
|
|
11
|
+
}
|
|
4
12
|
export function parseOpnetBinary(data) {
|
|
13
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5
14
|
let offset = 0;
|
|
6
15
|
if (data.length < 8 + 4 + 1) {
|
|
7
16
|
throw new Error('Binary too small: missing header');
|
|
8
17
|
}
|
|
9
18
|
const magic = data.subarray(offset, offset + 8);
|
|
10
19
|
offset += 8;
|
|
11
|
-
if (!
|
|
12
|
-
throw new Error(`Invalid magic bytes: expected ${PLUGIN_MAGIC_BYTES
|
|
20
|
+
if (!PLUGIN_MAGIC_BYTES.every((b, i) => magic[i] === b)) {
|
|
21
|
+
throw new Error(`Invalid magic bytes: expected ${toHex(PLUGIN_MAGIC_BYTES)}, got ${toHex(magic)}`);
|
|
13
22
|
}
|
|
14
|
-
const formatVersion =
|
|
23
|
+
const formatVersion = view.getUint32(offset, true);
|
|
15
24
|
offset += 4;
|
|
16
25
|
if (formatVersion !== PLUGIN_FORMAT_VERSION) {
|
|
17
26
|
throw new Error(`Unsupported format version: ${formatVersion} (expected ${PLUGIN_FORMAT_VERSION})`);
|
|
18
27
|
}
|
|
19
|
-
const mldsaLevelValue =
|
|
28
|
+
const mldsaLevelValue = view.getUint8(offset);
|
|
20
29
|
offset += 1;
|
|
21
30
|
if (mldsaLevelValue > 2) {
|
|
22
31
|
throw new Error(`Invalid MLDSA level: ${mldsaLevelValue} (must be 0, 1, or 2)`);
|
|
@@ -28,44 +37,45 @@ export function parseOpnetBinary(data) {
|
|
|
28
37
|
if (data.length < minSize) {
|
|
29
38
|
throw new Error(`Binary truncated: expected at least ${minSize} bytes, got ${data.length}`);
|
|
30
39
|
}
|
|
31
|
-
const publicKey =
|
|
40
|
+
const publicKey = data.slice(offset, offset + publicKeySize);
|
|
32
41
|
offset += publicKeySize;
|
|
33
|
-
const signature =
|
|
42
|
+
const signature = data.slice(offset, offset + signatureSize);
|
|
34
43
|
offset += signatureSize;
|
|
35
|
-
const metadataLength =
|
|
44
|
+
const metadataLength = view.getUint32(offset, true);
|
|
36
45
|
offset += 4;
|
|
37
46
|
if (offset + metadataLength > data.length) {
|
|
38
47
|
throw new Error(`Metadata section truncated at offset ${offset}`);
|
|
39
48
|
}
|
|
40
49
|
const metadataBytes = data.subarray(offset, offset + metadataLength);
|
|
41
50
|
offset += metadataLength;
|
|
51
|
+
const decoder = new TextDecoder();
|
|
42
52
|
let rawMetadata;
|
|
43
53
|
let metadata;
|
|
44
54
|
try {
|
|
45
|
-
rawMetadata =
|
|
55
|
+
rawMetadata = decoder.decode(metadataBytes);
|
|
46
56
|
metadata = JSON.parse(rawMetadata);
|
|
47
57
|
}
|
|
48
58
|
catch {
|
|
49
59
|
throw new Error('Malformed JSON metadata');
|
|
50
60
|
}
|
|
51
|
-
const bytecodeLength =
|
|
61
|
+
const bytecodeLength = view.getUint32(offset, true);
|
|
52
62
|
offset += 4;
|
|
53
63
|
if (offset + bytecodeLength > data.length) {
|
|
54
64
|
throw new Error(`Bytecode section truncated at offset ${offset}`);
|
|
55
65
|
}
|
|
56
|
-
const bytecode =
|
|
66
|
+
const bytecode = data.slice(offset, offset + bytecodeLength);
|
|
57
67
|
offset += bytecodeLength;
|
|
58
|
-
const protoLength =
|
|
68
|
+
const protoLength = view.getUint32(offset, true);
|
|
59
69
|
offset += 4;
|
|
60
70
|
if (offset + protoLength > data.length) {
|
|
61
71
|
throw new Error(`Proto section truncated at offset ${offset}`);
|
|
62
72
|
}
|
|
63
|
-
const proto = protoLength > 0 ?
|
|
73
|
+
const proto = protoLength > 0 ? data.slice(offset, offset + protoLength) : undefined;
|
|
64
74
|
offset += protoLength;
|
|
65
75
|
if (offset + 32 > data.length) {
|
|
66
76
|
throw new Error('Checksum truncated');
|
|
67
77
|
}
|
|
68
|
-
const checksum =
|
|
78
|
+
const checksum = data.slice(offset, offset + 32);
|
|
69
79
|
return {
|
|
70
80
|
formatVersion,
|
|
71
81
|
mldsaLevel,
|
|
@@ -86,26 +96,31 @@ export function computeChecksum(metadata, bytecode, proto) {
|
|
|
86
96
|
return hash.digest();
|
|
87
97
|
}
|
|
88
98
|
export function verifyChecksum(parsed) {
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
99
|
+
const encoder = new TextEncoder();
|
|
100
|
+
const metadataBytes = encoder.encode(parsed.rawMetadata);
|
|
101
|
+
const computed = computeChecksum(metadataBytes, parsed.bytecode, parsed.proto ?? new Uint8Array(0));
|
|
102
|
+
if (computed.length !== parsed.checksum.length) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return crypto.timingSafeEqual(computed, parsed.checksum);
|
|
92
106
|
}
|
|
93
107
|
export function buildOpnetBinary(options) {
|
|
94
|
-
const { mldsaLevel, publicKey, metadata, bytecode, proto =
|
|
108
|
+
const { mldsaLevel, publicKey, metadata, bytecode, proto = new Uint8Array(0), signFn, } = options;
|
|
95
109
|
const sdkLevel = cliLevelToMLDSALevel(mldsaLevel);
|
|
96
110
|
const expectedPkSize = MLDSA_PUBLIC_KEY_SIZES[sdkLevel];
|
|
97
111
|
const expectedSigSize = MLDSA_SIGNATURE_SIZES[sdkLevel];
|
|
98
112
|
if (publicKey.length !== expectedPkSize) {
|
|
99
113
|
throw new Error(`Public key size mismatch: expected ${expectedPkSize}, got ${publicKey.length}`);
|
|
100
114
|
}
|
|
115
|
+
const encoder = new TextEncoder();
|
|
101
116
|
const tempMetadata = { ...metadata, checksum: '' };
|
|
102
|
-
const tempMetadataBytes =
|
|
117
|
+
const tempMetadataBytes = encoder.encode(JSON.stringify(tempMetadata));
|
|
103
118
|
const checksum = computeChecksum(tempMetadataBytes, bytecode, proto);
|
|
104
|
-
const checksumHex = `sha256:${checksum
|
|
119
|
+
const checksumHex = `sha256:${toHex(checksum)}`;
|
|
105
120
|
const finalMetadata = { ...metadata, checksum: checksumHex };
|
|
106
|
-
const metadataBytes =
|
|
121
|
+
const metadataBytes = encoder.encode(JSON.stringify(finalMetadata));
|
|
107
122
|
const finalChecksum = computeChecksum(metadataBytes, bytecode, proto);
|
|
108
|
-
const signature = signFn ? signFn(finalChecksum) :
|
|
123
|
+
const signature = signFn ? signFn(finalChecksum) : new Uint8Array(expectedSigSize);
|
|
109
124
|
if (signature.length !== expectedSigSize) {
|
|
110
125
|
throw new Error(`Signature size mismatch: expected ${expectedSigSize}, got ${signature.length}`);
|
|
111
126
|
}
|
|
@@ -121,31 +136,32 @@ export function buildOpnetBinary(options) {
|
|
|
121
136
|
4 +
|
|
122
137
|
proto.length +
|
|
123
138
|
32;
|
|
124
|
-
const buffer =
|
|
139
|
+
const buffer = new Uint8Array(totalSize);
|
|
140
|
+
const view = new DataView(buffer.buffer);
|
|
125
141
|
let offset = 0;
|
|
126
|
-
|
|
142
|
+
buffer.set(PLUGIN_MAGIC_BYTES, offset);
|
|
127
143
|
offset += 8;
|
|
128
|
-
|
|
144
|
+
view.setUint32(offset, PLUGIN_FORMAT_VERSION, true);
|
|
129
145
|
offset += 4;
|
|
130
|
-
|
|
146
|
+
view.setUint8(offset, sdkLevel);
|
|
131
147
|
offset += 1;
|
|
132
|
-
|
|
148
|
+
buffer.set(publicKey, offset);
|
|
133
149
|
offset += publicKey.length;
|
|
134
|
-
|
|
150
|
+
buffer.set(signature, offset);
|
|
135
151
|
offset += signature.length;
|
|
136
|
-
|
|
152
|
+
view.setUint32(offset, metadataBytes.length, true);
|
|
137
153
|
offset += 4;
|
|
138
|
-
|
|
154
|
+
buffer.set(metadataBytes, offset);
|
|
139
155
|
offset += metadataBytes.length;
|
|
140
|
-
|
|
156
|
+
view.setUint32(offset, bytecode.length, true);
|
|
141
157
|
offset += 4;
|
|
142
|
-
|
|
158
|
+
buffer.set(bytecode, offset);
|
|
143
159
|
offset += bytecode.length;
|
|
144
|
-
|
|
160
|
+
view.setUint32(offset, proto.length, true);
|
|
145
161
|
offset += 4;
|
|
146
|
-
|
|
162
|
+
buffer.set(proto, offset);
|
|
147
163
|
offset += proto.length;
|
|
148
|
-
|
|
164
|
+
buffer.set(finalChecksum, offset);
|
|
149
165
|
return { binary: buffer, checksum: finalChecksum };
|
|
150
166
|
}
|
|
151
167
|
export function extractMetadata(data) {
|
package/build/lib/credentials.js
CHANGED
|
@@ -3,11 +3,22 @@ import * as path from 'path';
|
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import { ensureConfigDir } from './config.js';
|
|
5
5
|
const CREDENTIALS_FILE = path.join(os.homedir(), '.opnet', 'credentials.json');
|
|
6
|
+
const VALID_MLDSA_LEVELS = [44, 65, 87];
|
|
7
|
+
function parseEnvMldsaLevel() {
|
|
8
|
+
const raw = process.env.OPNET_MLDSA_LEVEL;
|
|
9
|
+
if (!raw)
|
|
10
|
+
return 44;
|
|
11
|
+
const parsed = parseInt(raw, 10);
|
|
12
|
+
if (!VALID_MLDSA_LEVELS.includes(parsed)) {
|
|
13
|
+
throw new Error(`Invalid OPNET_MLDSA_LEVEL="${raw}". Must be one of: ${VALID_MLDSA_LEVELS.join(', ')}`);
|
|
14
|
+
}
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
6
17
|
export function loadCredentials() {
|
|
7
18
|
if (process.env.OPNET_MNEMONIC) {
|
|
8
19
|
return {
|
|
9
20
|
mnemonic: process.env.OPNET_MNEMONIC,
|
|
10
|
-
mldsaLevel:
|
|
21
|
+
mldsaLevel: parseEnvMldsaLevel(),
|
|
11
22
|
network: process.env.OPNET_NETWORK || 'mainnet',
|
|
12
23
|
};
|
|
13
24
|
}
|
|
@@ -15,18 +26,29 @@ export function loadCredentials() {
|
|
|
15
26
|
return {
|
|
16
27
|
wif: process.env.OPNET_PRIVATE_KEY,
|
|
17
28
|
mldsaPrivateKey: process.env.OPNET_MLDSA_KEY,
|
|
18
|
-
mldsaLevel:
|
|
29
|
+
mldsaLevel: parseEnvMldsaLevel(),
|
|
19
30
|
network: process.env.OPNET_NETWORK || 'mainnet',
|
|
20
31
|
};
|
|
21
32
|
}
|
|
22
33
|
if (!fs.existsSync(CREDENTIALS_FILE)) {
|
|
23
34
|
return null;
|
|
24
35
|
}
|
|
36
|
+
try {
|
|
37
|
+
const stat = fs.statSync(CREDENTIALS_FILE);
|
|
38
|
+
const mode = stat.mode & 0o777;
|
|
39
|
+
if (mode !== 0o600 && mode !== 0o400) {
|
|
40
|
+
process.stderr.write(`WARNING: Credentials file ${CREDENTIALS_FILE} has permissions ${mode.toString(8)}. Expected 600 or 400.\n` +
|
|
41
|
+
`Run: chmod 600 ${CREDENTIALS_FILE}\n`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
}
|
|
25
46
|
try {
|
|
26
47
|
const content = fs.readFileSync(CREDENTIALS_FILE, 'utf-8');
|
|
27
48
|
return JSON.parse(content);
|
|
28
49
|
}
|
|
29
|
-
catch {
|
|
50
|
+
catch (error) {
|
|
51
|
+
process.stderr.write(`WARNING: Failed to parse credentials file: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
30
52
|
return null;
|
|
31
53
|
}
|
|
32
54
|
}
|