@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.
- package/.gitattributes +2 -0
- package/.github/dependabot.yml +9 -0
- package/.github/workflows/ci.yml +48 -0
- package/.prettierrc.json +12 -0
- package/CONTRIBUTING.md +56 -0
- package/LICENSE +190 -0
- package/NOTICE +17 -0
- package/README.md +509 -0
- package/SECURITY.md +35 -0
- package/build/commands/AcceptCommand.d.ts +7 -0
- package/build/commands/AcceptCommand.js +110 -0
- package/build/commands/BaseCommand.d.ts +12 -0
- package/build/commands/BaseCommand.js +27 -0
- package/build/commands/CompileCommand.d.ts +7 -0
- package/build/commands/CompileCommand.js +138 -0
- package/build/commands/ConfigCommand.d.ts +17 -0
- package/build/commands/ConfigCommand.js +124 -0
- package/build/commands/DeprecateCommand.d.ts +7 -0
- package/build/commands/DeprecateCommand.js +112 -0
- package/build/commands/InfoCommand.d.ts +10 -0
- package/build/commands/InfoCommand.js +223 -0
- package/build/commands/InitCommand.d.ts +16 -0
- package/build/commands/InitCommand.js +336 -0
- package/build/commands/InstallCommand.d.ts +7 -0
- package/build/commands/InstallCommand.js +130 -0
- package/build/commands/KeygenCommand.d.ts +13 -0
- package/build/commands/KeygenCommand.js +133 -0
- package/build/commands/ListCommand.d.ts +7 -0
- package/build/commands/ListCommand.js +117 -0
- package/build/commands/LoginCommand.d.ts +9 -0
- package/build/commands/LoginCommand.js +139 -0
- package/build/commands/LogoutCommand.d.ts +7 -0
- package/build/commands/LogoutCommand.js +57 -0
- package/build/commands/PublishCommand.d.ts +7 -0
- package/build/commands/PublishCommand.js +163 -0
- package/build/commands/SearchCommand.d.ts +7 -0
- package/build/commands/SearchCommand.js +97 -0
- package/build/commands/SignCommand.d.ts +7 -0
- package/build/commands/SignCommand.js +80 -0
- package/build/commands/TransferCommand.d.ts +8 -0
- package/build/commands/TransferCommand.js +179 -0
- package/build/commands/UndeprecateCommand.d.ts +7 -0
- package/build/commands/UndeprecateCommand.js +95 -0
- package/build/commands/UpdateCommand.d.ts +7 -0
- package/build/commands/UpdateCommand.js +130 -0
- package/build/commands/VerifyCommand.d.ts +7 -0
- package/build/commands/VerifyCommand.js +167 -0
- package/build/commands/WhoamiCommand.d.ts +7 -0
- package/build/commands/WhoamiCommand.js +84 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +64 -0
- package/build/lib/PackageRegistry.abi.d.ts +2 -0
- package/build/lib/PackageRegistry.abi.js +356 -0
- package/build/lib/binary.d.ts +16 -0
- package/build/lib/binary.js +165 -0
- package/build/lib/config.d.ts +11 -0
- package/build/lib/config.js +160 -0
- package/build/lib/credentials.d.ts +10 -0
- package/build/lib/credentials.js +89 -0
- package/build/lib/ipfs.d.ts +16 -0
- package/build/lib/ipfs.js +209 -0
- package/build/lib/manifest.d.ts +14 -0
- package/build/lib/manifest.js +88 -0
- package/build/lib/provider.d.ts +9 -0
- package/build/lib/provider.js +48 -0
- package/build/lib/registry.d.ts +58 -0
- package/build/lib/registry.js +197 -0
- package/build/lib/wallet.d.ts +32 -0
- package/build/lib/wallet.js +114 -0
- package/build/types/PackageRegistry.d.ts +177 -0
- package/build/types/PackageRegistry.js +1 -0
- package/build/types/index.d.ts +30 -0
- package/build/types/index.js +52 -0
- package/eslint.config.js +41 -0
- package/gulpfile.js +41 -0
- package/package.json +83 -0
- package/src/commands/AcceptCommand.ts +151 -0
- package/src/commands/BaseCommand.ts +59 -0
- package/src/commands/CompileCommand.ts +196 -0
- package/src/commands/ConfigCommand.ts +144 -0
- package/src/commands/DeprecateCommand.ts +156 -0
- package/src/commands/InfoCommand.ts +293 -0
- package/src/commands/InitCommand.ts +465 -0
- package/src/commands/InstallCommand.ts +179 -0
- package/src/commands/KeygenCommand.ts +157 -0
- package/src/commands/ListCommand.ts +169 -0
- package/src/commands/LoginCommand.ts +197 -0
- package/src/commands/LogoutCommand.ts +76 -0
- package/src/commands/PublishCommand.ts +230 -0
- package/src/commands/SearchCommand.ts +141 -0
- package/src/commands/SignCommand.ts +122 -0
- package/src/commands/TransferCommand.ts +235 -0
- package/src/commands/UndeprecateCommand.ts +134 -0
- package/src/commands/UpdateCommand.ts +200 -0
- package/src/commands/VerifyCommand.ts +228 -0
- package/src/commands/WhoamiCommand.ts +113 -0
- package/src/index.ts +86 -0
- package/src/lib/PackageRegistry.abi.json +765 -0
- package/src/lib/PackageRegistry.abi.ts +365 -0
- package/src/lib/binary.ts +336 -0
- package/src/lib/config.ts +265 -0
- package/src/lib/credentials.ts +176 -0
- package/src/lib/ipfs.ts +369 -0
- package/src/lib/manifest.ts +172 -0
- package/src/lib/provider.ts +121 -0
- package/src/lib/registry.ts +464 -0
- package/src/lib/wallet.ts +271 -0
- package/src/types/PackageRegistry.ts +344 -0
- package/src/types/index.ts +145 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { validateManifest as sdkValidateManifest, PLUGIN_NAME_REGEX, PLUGIN_MANIFEST_FILENAME, DEFAULT_PERMISSIONS, DEFAULT_RESOURCES, DEFAULT_LIFECYCLE, } from '@btc-vision/plugin-sdk';
|
|
4
|
+
const SCOPED_NAME_PATTERN = /^@[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/;
|
|
5
|
+
export function validatePluginName(name) {
|
|
6
|
+
const errors = [];
|
|
7
|
+
if (!name) {
|
|
8
|
+
errors.push('Name is required');
|
|
9
|
+
return errors;
|
|
10
|
+
}
|
|
11
|
+
if (name.startsWith('@')) {
|
|
12
|
+
if (!SCOPED_NAME_PATTERN.test(name)) {
|
|
13
|
+
errors.push('Scoped name must be @scope/name where scope and name are lowercase alphanumeric with hyphens, starting with a letter');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
if (!PLUGIN_NAME_REGEX.test(name)) {
|
|
18
|
+
errors.push('Name must be lowercase alphanumeric with hyphens, starting with a letter');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (name.length > 100) {
|
|
22
|
+
errors.push('Name must be 100 characters or less');
|
|
23
|
+
}
|
|
24
|
+
return errors;
|
|
25
|
+
}
|
|
26
|
+
export function validateManifest(manifest) {
|
|
27
|
+
return sdkValidateManifest(manifest);
|
|
28
|
+
}
|
|
29
|
+
export function validatePermissions(permissions) {
|
|
30
|
+
const errors = [];
|
|
31
|
+
if (permissions.database?.enabled) {
|
|
32
|
+
if (!permissions.database.collections ||
|
|
33
|
+
!Array.isArray(permissions.database.collections)) {
|
|
34
|
+
errors.push('database.collections must be an array when database is enabled');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return errors;
|
|
38
|
+
}
|
|
39
|
+
export function loadManifest(manifestPath) {
|
|
40
|
+
const resolvedPath = path.resolve(manifestPath);
|
|
41
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
42
|
+
throw new Error(`Manifest not found: ${resolvedPath}`);
|
|
43
|
+
}
|
|
44
|
+
let manifest;
|
|
45
|
+
try {
|
|
46
|
+
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
47
|
+
manifest = JSON.parse(content);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
throw new Error(`Failed to parse manifest: ${e instanceof Error ? e.message : String(e)}`);
|
|
51
|
+
}
|
|
52
|
+
const result = validateManifest(manifest);
|
|
53
|
+
if (!result.valid) {
|
|
54
|
+
const errorList = result.errors
|
|
55
|
+
.map((e) => ` - ${e.path}: ${e.message}`)
|
|
56
|
+
.join('\n');
|
|
57
|
+
throw new Error(`Invalid manifest:\n${errorList}`);
|
|
58
|
+
}
|
|
59
|
+
return manifest;
|
|
60
|
+
}
|
|
61
|
+
export function saveManifest(manifestPath, manifest) {
|
|
62
|
+
const content = JSON.stringify(manifest, null, 4);
|
|
63
|
+
fs.writeFileSync(manifestPath, content, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
export function createManifest(options) {
|
|
66
|
+
return {
|
|
67
|
+
name: options.name,
|
|
68
|
+
version: '1.0.0',
|
|
69
|
+
opnetVersion: '^1.0.0',
|
|
70
|
+
main: 'dist/index.jsc',
|
|
71
|
+
target: 'bytenode',
|
|
72
|
+
type: 'plugin',
|
|
73
|
+
checksum: '',
|
|
74
|
+
author: {
|
|
75
|
+
name: options.author,
|
|
76
|
+
email: options.email,
|
|
77
|
+
},
|
|
78
|
+
description: options.description,
|
|
79
|
+
pluginType: options.pluginType,
|
|
80
|
+
permissions: DEFAULT_PERMISSIONS,
|
|
81
|
+
resources: DEFAULT_RESOURCES,
|
|
82
|
+
lifecycle: DEFAULT_LIFECYCLE,
|
|
83
|
+
dependencies: {},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export function getManifestPath(dir = process.cwd()) {
|
|
87
|
+
return path.join(dir, PLUGIN_MANIFEST_FILENAME);
|
|
88
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { JSONRpcProvider } from 'opnet';
|
|
2
|
+
import { NetworkName } from '../types/index.js';
|
|
3
|
+
export declare function getProvider(network?: NetworkName): JSONRpcProvider;
|
|
4
|
+
export declare function clearProviderCache(): void;
|
|
5
|
+
export declare function getRegistryContractAddress(network?: NetworkName): string;
|
|
6
|
+
export declare function checkConnection(network?: NetworkName): Promise<boolean>;
|
|
7
|
+
export declare function getBlockNumber(network?: NetworkName): Promise<bigint>;
|
|
8
|
+
export declare function getBalance(address: string, network?: NetworkName): Promise<bigint>;
|
|
9
|
+
export declare function broadcastTransaction(rawTx: string, network?: NetworkName, isPsbt?: boolean): Promise<string>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { JSONRpcProvider } from 'opnet';
|
|
2
|
+
import { getRpcUrl, getRegistryAddress, loadConfig } from './config.js';
|
|
3
|
+
import { getNetwork } from './wallet.js';
|
|
4
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
5
|
+
const providerCache = new Map();
|
|
6
|
+
export function getProvider(network) {
|
|
7
|
+
const config = loadConfig();
|
|
8
|
+
const targetNetwork = network || config.defaultNetwork;
|
|
9
|
+
const rpcUrl = getRpcUrl(targetNetwork);
|
|
10
|
+
const cacheKey = `${targetNetwork}:${rpcUrl}`;
|
|
11
|
+
const cached = providerCache.get(cacheKey);
|
|
12
|
+
if (cached) {
|
|
13
|
+
return cached;
|
|
14
|
+
}
|
|
15
|
+
const bitcoinNetwork = getNetwork(targetNetwork);
|
|
16
|
+
const provider = new JSONRpcProvider(rpcUrl, bitcoinNetwork, DEFAULT_TIMEOUT);
|
|
17
|
+
providerCache.set(cacheKey, provider);
|
|
18
|
+
return provider;
|
|
19
|
+
}
|
|
20
|
+
export function clearProviderCache() {
|
|
21
|
+
providerCache.clear();
|
|
22
|
+
}
|
|
23
|
+
export function getRegistryContractAddress(network) {
|
|
24
|
+
return getRegistryAddress(network);
|
|
25
|
+
}
|
|
26
|
+
export async function checkConnection(network) {
|
|
27
|
+
try {
|
|
28
|
+
const provider = getProvider(network);
|
|
29
|
+
await provider.getBlockNumber();
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function getBlockNumber(network) {
|
|
37
|
+
const provider = getProvider(network);
|
|
38
|
+
return provider.getBlockNumber();
|
|
39
|
+
}
|
|
40
|
+
export async function getBalance(address, network) {
|
|
41
|
+
const provider = getProvider(network);
|
|
42
|
+
return provider.getBalance(address);
|
|
43
|
+
}
|
|
44
|
+
export async function broadcastTransaction(rawTx, network, isPsbt = false) {
|
|
45
|
+
const provider = getProvider(network);
|
|
46
|
+
const result = await provider.sendRawTransaction(rawTx, isPsbt);
|
|
47
|
+
return result.result ?? '';
|
|
48
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Address } from '@btc-vision/transaction';
|
|
2
|
+
import { NetworkName, CLIMldsaLevel, RegistryPluginType } from '../types/index.js';
|
|
3
|
+
import { IPluginPermissions } from '@btc-vision/plugin-sdk';
|
|
4
|
+
import { IPackageRegistry } from '../types/PackageRegistry.js';
|
|
5
|
+
export interface ScopeInfo {
|
|
6
|
+
exists: boolean;
|
|
7
|
+
owner: Address;
|
|
8
|
+
createdAt: bigint;
|
|
9
|
+
}
|
|
10
|
+
export interface PackageInfo {
|
|
11
|
+
exists: boolean;
|
|
12
|
+
owner: Address;
|
|
13
|
+
createdAt: bigint;
|
|
14
|
+
versionCount: bigint;
|
|
15
|
+
latestVersion: string;
|
|
16
|
+
}
|
|
17
|
+
export interface VersionInfo {
|
|
18
|
+
exists: boolean;
|
|
19
|
+
ipfsCid: string;
|
|
20
|
+
checksum: Uint8Array;
|
|
21
|
+
sigHash: Uint8Array;
|
|
22
|
+
mldsaLevel: number;
|
|
23
|
+
opnetVersionRange: string;
|
|
24
|
+
pluginType: number;
|
|
25
|
+
permissionsHash: Uint8Array;
|
|
26
|
+
depsHash: Uint8Array;
|
|
27
|
+
publisher: Address;
|
|
28
|
+
publishedAt: bigint;
|
|
29
|
+
deprecated: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface PendingTransferInfo {
|
|
32
|
+
pendingOwner: Address;
|
|
33
|
+
initiatedAt: bigint;
|
|
34
|
+
}
|
|
35
|
+
export declare function getRegistryContract(network?: NetworkName): IPackageRegistry;
|
|
36
|
+
export declare function clearRegistryCache(): void;
|
|
37
|
+
export declare function getScope(scopeName: string, network?: NetworkName): Promise<ScopeInfo | null>;
|
|
38
|
+
export declare function getScopeOwner(scopeName: string, network?: NetworkName): Promise<Address | null>;
|
|
39
|
+
export declare function getPackage(packageName: string, network?: NetworkName): Promise<PackageInfo | null>;
|
|
40
|
+
export declare function getPackageOwner(packageName: string, network?: NetworkName): Promise<Address | null>;
|
|
41
|
+
export declare function getVersion(packageName: string, version: string, network?: NetworkName): Promise<VersionInfo | null>;
|
|
42
|
+
export declare function isVersionDeprecated(packageName: string, version: string, network?: NetworkName): Promise<boolean>;
|
|
43
|
+
export declare function isVersionImmutable(packageName: string, version: string, network?: NetworkName): Promise<boolean>;
|
|
44
|
+
export declare function getPendingTransfer(packageName: string, network?: NetworkName): Promise<PendingTransferInfo | null>;
|
|
45
|
+
export declare function getPendingScopeTransfer(scopeName: string, network?: NetworkName): Promise<PendingTransferInfo | null>;
|
|
46
|
+
export declare function getTreasuryAddress(network?: NetworkName): Promise<string>;
|
|
47
|
+
export declare function getScopePrice(network?: NetworkName): Promise<bigint>;
|
|
48
|
+
export declare function getPackagePrice(network?: NetworkName): Promise<bigint>;
|
|
49
|
+
export declare function parsePackageName(fullName: string): {
|
|
50
|
+
scope: string | null;
|
|
51
|
+
name: string;
|
|
52
|
+
};
|
|
53
|
+
export declare function computePermissionsHash(permissions: IPluginPermissions | undefined): Uint8Array;
|
|
54
|
+
export declare function encodeDependencies(dependencies: Record<string, string>): Uint8Array;
|
|
55
|
+
export declare function registryToMldsaLevel(registryLevel: number): CLIMldsaLevel;
|
|
56
|
+
export declare function mldsaLevelToRegistry(mldsaLevel: CLIMldsaLevel): number;
|
|
57
|
+
export declare function registryToPluginType(registryType: number): RegistryPluginType;
|
|
58
|
+
export declare function pluginTypeToRegistry(pluginType: RegistryPluginType): number;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { getContract } from 'opnet';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
import { getProvider, getRegistryContractAddress } from './provider.js';
|
|
4
|
+
import { getNetwork } from './wallet.js';
|
|
5
|
+
import { loadConfig } from './config.js';
|
|
6
|
+
import { PACKAGE_REGISTRY_ABI } from './PackageRegistry.abi.js';
|
|
7
|
+
const registryCache = new Map();
|
|
8
|
+
export function getRegistryContract(network) {
|
|
9
|
+
const config = loadConfig();
|
|
10
|
+
const targetNetwork = network || config.defaultNetwork;
|
|
11
|
+
const cacheKey = targetNetwork;
|
|
12
|
+
const cached = registryCache.get(cacheKey);
|
|
13
|
+
if (cached) {
|
|
14
|
+
return cached;
|
|
15
|
+
}
|
|
16
|
+
const provider = getProvider(targetNetwork);
|
|
17
|
+
const registryAddress = getRegistryContractAddress(targetNetwork);
|
|
18
|
+
const bitcoinNetwork = getNetwork(targetNetwork);
|
|
19
|
+
const contract = getContract(registryAddress, PACKAGE_REGISTRY_ABI, provider, bitcoinNetwork);
|
|
20
|
+
registryCache.set(cacheKey, contract);
|
|
21
|
+
return contract;
|
|
22
|
+
}
|
|
23
|
+
export function clearRegistryCache() {
|
|
24
|
+
registryCache.clear();
|
|
25
|
+
}
|
|
26
|
+
export async function getScope(scopeName, network) {
|
|
27
|
+
const contract = getRegistryContract(network);
|
|
28
|
+
const result = await contract.getScope(scopeName);
|
|
29
|
+
if (!result.properties.exists) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
exists: result.properties.exists,
|
|
34
|
+
owner: result.properties.owner,
|
|
35
|
+
createdAt: result.properties.createdAt,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export async function getScopeOwner(scopeName, network) {
|
|
39
|
+
const contract = getRegistryContract(network);
|
|
40
|
+
try {
|
|
41
|
+
const result = await contract.getScopeOwner(scopeName);
|
|
42
|
+
return result.properties.owner;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function getPackage(packageName, network) {
|
|
49
|
+
const contract = getRegistryContract(network);
|
|
50
|
+
const result = await contract.getPackage(packageName);
|
|
51
|
+
if (!result.properties.exists) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
exists: result.properties.exists,
|
|
56
|
+
owner: result.properties.owner,
|
|
57
|
+
createdAt: result.properties.createdAt,
|
|
58
|
+
versionCount: result.properties.versionCount,
|
|
59
|
+
latestVersion: result.properties.latestVersion,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export async function getPackageOwner(packageName, network) {
|
|
63
|
+
const contract = getRegistryContract(network);
|
|
64
|
+
try {
|
|
65
|
+
const result = await contract.getOwner(packageName);
|
|
66
|
+
return result.properties.owner;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export async function getVersion(packageName, version, network) {
|
|
73
|
+
const contract = getRegistryContract(network);
|
|
74
|
+
const result = await contract.getVersion(packageName, version);
|
|
75
|
+
if (!result.properties.exists) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
exists: result.properties.exists,
|
|
80
|
+
ipfsCid: result.properties.ipfsCid,
|
|
81
|
+
checksum: result.properties.checksum,
|
|
82
|
+
sigHash: result.properties.sigHash,
|
|
83
|
+
mldsaLevel: result.properties.mldsaLevel,
|
|
84
|
+
opnetVersionRange: result.properties.opnetVersionRange,
|
|
85
|
+
pluginType: result.properties.pluginType,
|
|
86
|
+
permissionsHash: result.properties.permissionsHash,
|
|
87
|
+
depsHash: result.properties.depsHash,
|
|
88
|
+
publisher: result.properties.publisher,
|
|
89
|
+
publishedAt: result.properties.publishedAt,
|
|
90
|
+
deprecated: result.properties.deprecated,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export async function isVersionDeprecated(packageName, version, network) {
|
|
94
|
+
const contract = getRegistryContract(network);
|
|
95
|
+
const result = await contract.isDeprecated(packageName, version);
|
|
96
|
+
return result.properties.deprecated;
|
|
97
|
+
}
|
|
98
|
+
export async function isVersionImmutable(packageName, version, network) {
|
|
99
|
+
const contract = getRegistryContract(network);
|
|
100
|
+
const result = await contract.isImmutable(packageName, version);
|
|
101
|
+
return result.properties.immutable;
|
|
102
|
+
}
|
|
103
|
+
export async function getPendingTransfer(packageName, network) {
|
|
104
|
+
const contract = getRegistryContract(network);
|
|
105
|
+
try {
|
|
106
|
+
const result = await contract.getPendingTransfer(packageName);
|
|
107
|
+
const pendingOwner = result.properties.pendingOwner;
|
|
108
|
+
if (!pendingOwner || pendingOwner.toString() === '0'.repeat(64)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
pendingOwner,
|
|
113
|
+
initiatedAt: result.properties.initiatedAt,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export async function getPendingScopeTransfer(scopeName, network) {
|
|
121
|
+
const contract = getRegistryContract(network);
|
|
122
|
+
try {
|
|
123
|
+
const result = await contract.getPendingScopeTransfer(scopeName);
|
|
124
|
+
const pendingOwner = result.properties.pendingOwner;
|
|
125
|
+
if (!pendingOwner || pendingOwner.toString() === '0'.repeat(64)) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
pendingOwner,
|
|
130
|
+
initiatedAt: result.properties.initiatedAt,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export async function getTreasuryAddress(network) {
|
|
138
|
+
const contract = getRegistryContract(network);
|
|
139
|
+
const result = await contract.getTreasuryAddress();
|
|
140
|
+
return result.properties.treasuryAddress;
|
|
141
|
+
}
|
|
142
|
+
export async function getScopePrice(network) {
|
|
143
|
+
const contract = getRegistryContract(network);
|
|
144
|
+
const result = await contract.getScopePrice();
|
|
145
|
+
return result.properties.priceSats;
|
|
146
|
+
}
|
|
147
|
+
export async function getPackagePrice(network) {
|
|
148
|
+
const contract = getRegistryContract(network);
|
|
149
|
+
const result = await contract.getPackagePrice();
|
|
150
|
+
return result.properties.priceSats;
|
|
151
|
+
}
|
|
152
|
+
export function parsePackageName(fullName) {
|
|
153
|
+
if (fullName.startsWith('@')) {
|
|
154
|
+
const slashIndex = fullName.indexOf('/');
|
|
155
|
+
if (slashIndex > 0) {
|
|
156
|
+
return {
|
|
157
|
+
scope: fullName.substring(1, slashIndex),
|
|
158
|
+
name: fullName.substring(slashIndex + 1),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { scope: null, name: fullName };
|
|
163
|
+
}
|
|
164
|
+
export function computePermissionsHash(permissions) {
|
|
165
|
+
const json = JSON.stringify(permissions);
|
|
166
|
+
const hash = crypto.createHash('sha256').update(json).digest();
|
|
167
|
+
return new Uint8Array(hash);
|
|
168
|
+
}
|
|
169
|
+
export function encodeDependencies(dependencies) {
|
|
170
|
+
if (Object.keys(dependencies).length === 0) {
|
|
171
|
+
return new Uint8Array(0);
|
|
172
|
+
}
|
|
173
|
+
const json = JSON.stringify(dependencies);
|
|
174
|
+
return new Uint8Array(Buffer.from(json, 'utf-8'));
|
|
175
|
+
}
|
|
176
|
+
export function registryToMldsaLevel(registryLevel) {
|
|
177
|
+
const levels = {
|
|
178
|
+
1: 44,
|
|
179
|
+
2: 65,
|
|
180
|
+
3: 87,
|
|
181
|
+
};
|
|
182
|
+
return levels[registryLevel] || 44;
|
|
183
|
+
}
|
|
184
|
+
export function mldsaLevelToRegistry(mldsaLevel) {
|
|
185
|
+
const levels = {
|
|
186
|
+
44: 1,
|
|
187
|
+
65: 2,
|
|
188
|
+
87: 3,
|
|
189
|
+
};
|
|
190
|
+
return levels[mldsaLevel] || 1;
|
|
191
|
+
}
|
|
192
|
+
export function registryToPluginType(registryType) {
|
|
193
|
+
return registryType === 2 ? 'library' : 'standalone';
|
|
194
|
+
}
|
|
195
|
+
export function pluginTypeToRegistry(pluginType) {
|
|
196
|
+
return pluginType === 'library' ? 2 : 1;
|
|
197
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
+
import { MLDSASecurityLevel } from '@btc-vision/bip32';
|
|
3
|
+
import { Wallet, EcKeyPair } from '@btc-vision/transaction';
|
|
4
|
+
import { CLICredentials, NetworkName, CLIMldsaLevel } from '../types/index.js';
|
|
5
|
+
export declare function getNetwork(networkName: NetworkName): Network;
|
|
6
|
+
export declare function getMLDSASecurityLevel(level: CLIMldsaLevel): MLDSASecurityLevel;
|
|
7
|
+
export declare class CLIWallet {
|
|
8
|
+
private readonly wallet;
|
|
9
|
+
private readonly network;
|
|
10
|
+
private readonly mldsaLevel;
|
|
11
|
+
private constructor();
|
|
12
|
+
static fromCredentials(credentials: CLICredentials): CLIWallet;
|
|
13
|
+
static load(): CLIWallet;
|
|
14
|
+
get p2trAddress(): string;
|
|
15
|
+
get underlyingWallet(): Wallet;
|
|
16
|
+
get keypair(): EcKeyPair;
|
|
17
|
+
get mldsaKeypair(): EcKeyPair;
|
|
18
|
+
get mldsaPublicKey(): Buffer;
|
|
19
|
+
get mldsaPublicKeyHash(): string;
|
|
20
|
+
get securityLevel(): CLIMldsaLevel;
|
|
21
|
+
get walletNetwork(): Network;
|
|
22
|
+
signMLDSA(data: Buffer): Buffer;
|
|
23
|
+
verifyMLDSA(data: Buffer, signature: Buffer): boolean;
|
|
24
|
+
static verifyMLDSA(data: Buffer, signature: Buffer, publicKey: Buffer, level: CLIMldsaLevel): boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function generateMLDSAKeypair(level: CLIMldsaLevel): {
|
|
27
|
+
privateKey: Buffer;
|
|
28
|
+
publicKey: Buffer;
|
|
29
|
+
};
|
|
30
|
+
export declare function computePublicKeyHash(publicKey: Buffer): string;
|
|
31
|
+
export declare function validateMnemonic(phrase: string): boolean;
|
|
32
|
+
export declare function generateMnemonic(): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { networks } from '@btc-vision/bitcoin';
|
|
2
|
+
import { MLDSASecurityLevel, QuantumBIP32Factory } from '@btc-vision/bip32';
|
|
3
|
+
import { Mnemonic, Wallet, EcKeyPair, MessageSigner } from '@btc-vision/transaction';
|
|
4
|
+
import * as crypto from 'crypto';
|
|
5
|
+
import { loadCredentials, canSign } from './credentials.js';
|
|
6
|
+
export function getNetwork(networkName) {
|
|
7
|
+
switch (networkName) {
|
|
8
|
+
case 'mainnet':
|
|
9
|
+
return networks.bitcoin;
|
|
10
|
+
case 'testnet':
|
|
11
|
+
return networks.testnet;
|
|
12
|
+
case 'regtest':
|
|
13
|
+
return networks.regtest;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function getMLDSASecurityLevel(level) {
|
|
17
|
+
switch (level) {
|
|
18
|
+
case 44:
|
|
19
|
+
return MLDSASecurityLevel.LEVEL2;
|
|
20
|
+
case 65:
|
|
21
|
+
return MLDSASecurityLevel.LEVEL3;
|
|
22
|
+
case 87:
|
|
23
|
+
return MLDSASecurityLevel.LEVEL5;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class CLIWallet {
|
|
27
|
+
wallet;
|
|
28
|
+
network;
|
|
29
|
+
mldsaLevel;
|
|
30
|
+
constructor(wallet, network, mldsaLevel) {
|
|
31
|
+
this.wallet = wallet;
|
|
32
|
+
this.network = network;
|
|
33
|
+
this.mldsaLevel = mldsaLevel;
|
|
34
|
+
}
|
|
35
|
+
static fromCredentials(credentials) {
|
|
36
|
+
const network = getNetwork(credentials.network);
|
|
37
|
+
const securityLevel = getMLDSASecurityLevel(credentials.mldsaLevel);
|
|
38
|
+
if (credentials.mnemonic) {
|
|
39
|
+
const mnemonic = new Mnemonic(credentials.mnemonic, '', network, securityLevel);
|
|
40
|
+
const wallet = mnemonic.deriveUnisat();
|
|
41
|
+
return new CLIWallet(wallet, network, credentials.mldsaLevel);
|
|
42
|
+
}
|
|
43
|
+
if (credentials.wif && credentials.mldsaPrivateKey) {
|
|
44
|
+
const wallet = Wallet.fromWif(credentials.wif, credentials.mldsaPrivateKey, network, securityLevel);
|
|
45
|
+
return new CLIWallet(wallet, network, credentials.mldsaLevel);
|
|
46
|
+
}
|
|
47
|
+
throw new Error('Invalid credentials: requires either mnemonic or both WIF and MLDSA key');
|
|
48
|
+
}
|
|
49
|
+
static load() {
|
|
50
|
+
const credentials = loadCredentials();
|
|
51
|
+
if (!credentials) {
|
|
52
|
+
throw new Error('No credentials found. Run `opnet login` to configure your wallet.');
|
|
53
|
+
}
|
|
54
|
+
if (!canSign(credentials)) {
|
|
55
|
+
throw new Error('Credentials incomplete for signing. Run `opnet login` to reconfigure.');
|
|
56
|
+
}
|
|
57
|
+
return CLIWallet.fromCredentials(credentials);
|
|
58
|
+
}
|
|
59
|
+
get p2trAddress() {
|
|
60
|
+
return this.wallet.p2tr;
|
|
61
|
+
}
|
|
62
|
+
get underlyingWallet() {
|
|
63
|
+
return this.wallet;
|
|
64
|
+
}
|
|
65
|
+
get keypair() {
|
|
66
|
+
return this.wallet.keypair;
|
|
67
|
+
}
|
|
68
|
+
get mldsaKeypair() {
|
|
69
|
+
return this.wallet.mldsaKeypair;
|
|
70
|
+
}
|
|
71
|
+
get mldsaPublicKey() {
|
|
72
|
+
return Buffer.from(this.wallet.mldsaKeypair.publicKey);
|
|
73
|
+
}
|
|
74
|
+
get mldsaPublicKeyHash() {
|
|
75
|
+
const hash = crypto.createHash('sha256').update(this.mldsaPublicKey).digest();
|
|
76
|
+
return hash.toString('hex');
|
|
77
|
+
}
|
|
78
|
+
get securityLevel() {
|
|
79
|
+
return this.mldsaLevel;
|
|
80
|
+
}
|
|
81
|
+
get walletNetwork() {
|
|
82
|
+
return this.network;
|
|
83
|
+
}
|
|
84
|
+
signMLDSA(data) {
|
|
85
|
+
const result = MessageSigner.signMLDSAMessage(this.wallet.mldsaKeypair, data);
|
|
86
|
+
return Buffer.from(result.signature);
|
|
87
|
+
}
|
|
88
|
+
verifyMLDSA(data, signature) {
|
|
89
|
+
return MessageSigner.verifyMLDSASignature(this.wallet.mldsaKeypair, data, signature);
|
|
90
|
+
}
|
|
91
|
+
static verifyMLDSA(data, signature, publicKey, level) {
|
|
92
|
+
const securityLevel = getMLDSASecurityLevel(level);
|
|
93
|
+
const dummyChainCode = new Uint8Array(32);
|
|
94
|
+
const keypair = QuantumBIP32Factory.fromPublicKey(publicKey, dummyChainCode, networks.bitcoin, securityLevel);
|
|
95
|
+
return keypair.verify(data, signature);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function generateMLDSAKeypair(level) {
|
|
99
|
+
const securityLevel = getMLDSASecurityLevel(level);
|
|
100
|
+
const keypair = EcKeyPair.generateQuantumKeyPair(securityLevel);
|
|
101
|
+
return {
|
|
102
|
+
privateKey: Buffer.from(keypair.privateKey),
|
|
103
|
+
publicKey: Buffer.from(keypair.publicKey),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export function computePublicKeyHash(publicKey) {
|
|
107
|
+
return crypto.createHash('sha256').update(publicKey).digest('hex');
|
|
108
|
+
}
|
|
109
|
+
export function validateMnemonic(phrase) {
|
|
110
|
+
return Mnemonic.validate(phrase);
|
|
111
|
+
}
|
|
112
|
+
export function generateMnemonic() {
|
|
113
|
+
return Mnemonic.generatePhrase();
|
|
114
|
+
}
|