UNPKG

@chainreactionom/nano-mcp

Version:

NANO cryptocurrency wallet implementation for MCP with comprehensive testing

237 lines (200 loc) 8.57 kB
import * as nanocurrency from 'nanocurrency'; import * as nanoWeb from 'nanocurrency-web'; import crypto from 'crypto'; export class KeyManager { constructor(logger) { this.logger = logger; } validateKeyFormat(key) { if (!key) { this.logger.log('KEY_VALIDATION', 'Key is null or undefined'); return false; } if (typeof key !== 'string') { this.logger.log('KEY_VALIDATION', `Key is not a string, got ${typeof key}`); return false; } if (!/^[0-9a-fA-F]{64}$/.test(key)) { this.logger.log('KEY_VALIDATION', `Key does not match hex format: ${key}`); return false; } return true; } async generateKeyPair() { try { // Generate entropy const entropy = crypto.randomBytes(32).toString('hex').toLowerCase(); // Log entropy generation this.logger.log('KEY_GENERATION', { entropy_length: entropy.length, entropy: entropy }); // Generate wallet using nanocurrency-web const wallet = await nanoWeb.wallet.generate(entropy); const account = wallet.accounts[0]; // Log raw account data this.logger.log('WALLET_GENERATED', { address: account.address, public_key: account.publicKey, private_key_length: account.privateKey.length }); // Extract and format the private key let privateKey = account.privateKey.toLowerCase(); if (privateKey.startsWith('0x')) { privateKey = privateKey.slice(2); } // Ensure the private key is in the correct format if (!/^[0-9a-f]{64}$/.test(privateKey)) { throw new Error('Generated private key is in invalid format'); } // Log key processing this.logger.log('KEY_PROCESSING', { original_private_key_length: account.privateKey.length, processed_private_key_length: privateKey.length, is_hex: /^[0-9a-f]{64}$/.test(privateKey) }); // Verify the key pair const verifiedPublicKey = nanocurrency.derivePublicKey(privateKey); if (!verifiedPublicKey) { throw new Error('Failed to derive public key from private key'); } const publicKeyMatches = verifiedPublicKey.toLowerCase() === account.publicKey.toLowerCase(); // Log key verification this.logger.log('KEY_VERIFICATION', { original_public: account.publicKey, derived_public: verifiedPublicKey, match: publicKeyMatches }); if (!publicKeyMatches) { throw new Error('Key pair verification failed'); } // Store the keys in the correct format return { address: account.address, privateKey: privateKey, publicKey: account.publicKey.toLowerCase() }; } catch (error) { this.logger.logError('KEY_GENERATION_ERROR', error); throw new Error(`Failed to generate key pair: ${error.message}`); } } verifyKeyPair(privateKey, publicKey) { try { // Log input validation this.logger.log('KEY_PAIR_VERIFICATION', { private_key_length: privateKey?.length, public_key_length: publicKey?.length }); // Validate key formats if (!this.validateKeyFormat(privateKey)) { throw new Error(`Invalid private key format: ${privateKey}`); } if (!this.validateKeyFormat(publicKey)) { throw new Error(`Invalid public key format: ${publicKey}`); } // Derive public key and compare const derivedPublicKey = nanocurrency.derivePublicKey(privateKey.toLowerCase()); const matches = derivedPublicKey.toLowerCase() === publicKey.toLowerCase(); // Log verification result this.logger.log('KEY_PAIR_VERIFICATION_RESULT', { original_public: publicKey, derived_public: derivedPublicKey, match: matches }); return matches; } catch (error) { this.logger.logError('KEY_VERIFICATION_ERROR', error); return false; } } signBlock(block, privateKey) { try { // Log input validation this.logger.log('BLOCK_SIGNING_INPUT', { block_type: block?.type, private_key_length: privateKey?.length }); // Validate and format private key if (!privateKey || typeof privateKey !== 'string') { throw new Error('Invalid private key: must be a string'); } let formattedPrivateKey = privateKey.toLowerCase(); if (formattedPrivateKey.startsWith('0x')) { formattedPrivateKey = formattedPrivateKey.slice(2); } if (!/^[0-9a-f]{64}$/.test(formattedPrivateKey)) { throw new Error('Invalid private key format'); } // Validate block structure if (!block || typeof block !== 'object') { throw new Error('Invalid block: must be an object'); } const requiredFields = ['type', 'account', 'previous', 'representative', 'balance', 'link']; for (const field of requiredFields) { if (!block[field]) { throw new Error(`Missing required block field: ${field}`); } } // Ensure all strings are lowercase for consistency const formattedBlock = { ...block, type: block.type.toLowerCase(), account: block.account.toLowerCase(), previous: block.previous.toLowerCase(), representative: block.representative.toLowerCase(), link: block.link.toLowerCase(), }; // Create block hash const blockHash = nanocurrency.hashBlock(formattedBlock); if (!blockHash) { throw new Error('Failed to generate block hash'); } // Sign the block const signature = nanocurrency.signBlock({ hash: blockHash, privateKey: formattedPrivateKey }); if (!signature) { throw new Error('Failed to generate signature'); } // Log signing result this.logger.log('BLOCK_SIGNING_RESULT', { hash: blockHash, signature_length: signature.length }); return { signature, block: { ...formattedBlock, signature, work: block.work || null // Preserve work if present } }; } catch (error) { this.logger.logError('BLOCK_SIGNING_ERROR', error); throw new Error(`Failed to sign block: ${error.message}`); } } deriveAddress(publicKey) { try { // Log input validation this.logger.log('ADDRESS_DERIVATION_INPUT', { public_key_length: publicKey?.length }); if (!this.validateKeyFormat(publicKey)) { throw new Error(`Invalid public key format: ${publicKey}`); } const address = nanocurrency.deriveAddress(publicKey.toLowerCase()); // Log derivation result this.logger.log('ADDRESS_DERIVATION_RESULT', { public_key: publicKey, derived_address: address }); return address; } catch (error) { this.logger.logError('ADDRESS_DERIVATION_ERROR', error); throw new Error(`Failed to derive address: ${error.message}`); } } }